My game team has a problem that I suspect is not unique to my game team.

Everyone’s got a pile of markdown files living inside the project repo. Scratch notes. Personal task lists. AI context files tuned to their own machine and workflow. Nobody wants to commit them because nobody wants their teammates inheriting their mess. Nobody wants to gitignore them because then there’s no history. So they just sit there, untracked, slowly accumulating until someone does a git status and has a small crisis.

I got tired of it and built gitnook.

What It Does

Gitnook gives files inside a repo their own independent commit history, completely separate from the outer repo. Files you add to a gitnook get excluded from outer git automatically. Your .gitignore stays untouched. Nothing ever gets pushed.

cargo install gitnook

The Basics

# Create a gitnook inside any existing git repo
gitnook init notes

# Track a file -- it disappears from outer git immediately
gitnook add TASKS.md --to notes

# Commit a snapshot
gitnook commit -m "updated sprint tasks" --to notes

# Check status, see history
gitnook status notes
gitnook log notes

Once you set an active gitnook with gitnook switch, the --to flag becomes optional. Less typing, which is the whole point.

The Use Case I Actually Care About

The thing I keep coming back to is AI context files. If you use Claude Code, Cursor, or Copilot, you probably have a CLAUDE.md, DESIGN.md, or something similar sitting in your project root. These files need to be different per developer. Your local paths. Your preferred style. Your specific quirks and shortcuts. Committing them means everyone gets your version, or you’re constantly resolving conflicts that should never have existed. Gitignoring them means no history on files you’re actively iterating on.

Gitnook fits right in that gap. The shared repo stays clean and you get a full commit trail of how your AI context evolved over time. Useful when you want to look back and see why you told the model to stop doing that one thing.

How It Works

On gitnook init, it creates .gitnook/<name>/ as a bare git repository backed by libgit2. It also appends .gitnook/ to .git/info/exclude so the outer repo never sees it.

When you gitnook add a file:

  1. The file gets staged in the gitnook’s bare repo index
  2. Its path gets appended to .git/info/exclude

gitnook remove reverses both steps. The whole thing is local. It never touches .gitignore, it never touches the network.

my-project/
├── .git/
│   └── info/
│       └── exclude        <- gitnook writes here, never .gitignore
├── .gitnook/
│   ├── config.toml        <- active gitnook + registry
│   └── notes/             <- bare git repo
├── TASKS.md               <- excluded from outer git, versioned by "notes"
└── src/

Stuff It Can’t Do Yet

v1 is local only. No remote, no push, no sync across machines. Single linear history per gitnook, no branching. A file can only belong to one gitnook at a time.

Branching and a gitnook push command for backup are both on the roadmap. If either of those would actually get you to use this, let me know and I’ll prioritize accordingly.


Source and install instructions on GitHub: deadsoftie/gitnook