-
Notifications
You must be signed in to change notification settings - Fork 0
Home
This is the source for the gitnook GitHub Wiki. Each top-level section corresponds to a wiki page.
- Home
- Installation
- Getting Started
- Command Reference
- Working With Multiple Gitnooks
- How It Works
- Edge Cases and Gotchas
- Limitations
- Roadmap
- Contributing
gitnook gives you lightweight, local-only version control contexts inside any existing git repo. Files tracked by a gitnook have their own independent commit history, are automatically excluded from the outer repo via .git/info/exclude, and are never pushed to your team's remote.
When you work inside a project repo, you often have files you want to version locally but should never be committed to the shared history:
-
.env.localor credential files that contain machine-specific secrets - Personal scratch notes or a
TODO.mdthat lives alongside the code - Local config overrides that differ from the team defaults
Your options without gitnook are all awkward: add to .gitignore (excluded but unversioned), commit to the repo (now everyone sees it), or maintain a separate repo elsewhere (context-switching just to track files that live here).
gitnook is the fourth option: version those files right where they live, privately, without touching the outer repo or .gitignore.
cargo install gitnookgit clone https://github.com/your-username/gitnook
cd gitnook
cargo build --releaseThe compiled binary is at target/release/gitnook. Add it to your PATH.
- Rust 1.82 or later
- libgit2 (bundled via the
git2crate - no separate install needed)
All gitnook commands must be run from inside an existing git repository.
cd my-project # must be inside a git repo
gitnook init # creates a gitnook named "default"
gitnook init secrets # or give it a descriptive nameOn first init, gitnook:
- Creates
.gitnook/<name>/as a bare git repository - Creates
.gitnook/config.tomlrecording the new gitnook and setting it as active - Adds
.gitnook/to.git/info/excludeso the outer git never sees any of this
gitnook add .env.localThis does two things atomically:
- Stages
.env.localin the active gitnook's index - Appends
.env.localto.git/info/exclude
From this point on, git status and git add . in the outer repo will never touch .env.local.
To target a specific gitnook instead of the active one:
gitnook add .env.local --to secretsgitnook commit -m "initial tracked state"gitnook reads your user.name and user.email from the outer repo's git config and uses them as the commit author. If those are not set, it falls back to "gitnook user" <gitnook@local>.
gitnook status # all gitnooks
gitnook status secrets # one gitnookExample output:
[secrets] modified: .env.local
[notes] clean
[scratch] 1 new file: todo.md
Status categories:
- new file - staged in the index but not yet committed
- modified - committed previously, but the on-disk content has changed since
- clean - no differences between the index and the working tree
gitnook diff # active gitnook
gitnook diff secrets # named gitnookExample output:
diff --gitnook a/.env.local b/.env.local
--- a/.env.local
+++ b/.env.local
@@ -1,3 +1,3 @@
DB_HOST=localhost
-DB_PASS=old_password
+DB_PASS=new_password
PORT=5432
gitnook log # active gitnook
gitnook log secrets # named gitnookExample output:
commit a1b2c3d
Author: Rahul <rahul@example.com>
Date: Thu Mar 20 14:22:00 2025
add local db credentials
Creates a new gitnook. Defaults to "default" if no name is given. The first gitnook created in a repo automatically becomes the active gitnook.
gitnook init
gitnook init secrets
gitnook init personal-notesErrors:
-
gitnook '<name>' already exists- rungitnook listto see all gitnooks
Stages one or more files in the target gitnook and excludes them from the outer git. If --to is omitted, files go to the active gitnook.
gitnook add .env.local
gitnook add notes.md todo.md
gitnook add config/local.yml --to scratchWarnings and errors:
- If a file is already tracked by the outer git, gitnook prints a warning and tells you to run
git rm --cached <file>to fully remove it from outer git's history. - If a file is already tracked by a different gitnook, the command errors. A file can only belong to one gitnook.
- Re-adding a file to the same gitnook re-stages its current content (useful to stage modifications before a commit).
Removes a file from a gitnook's index and deletes its entry from .git/info/exclude. The file becomes visible to the outer git again as an untracked file.
gitnook remove .env.local
gitnook remove notes.md --to personalNotes:
- The file is not deleted from disk.
- The commit history in the gitnook is not affected - past commits that included the file remain intact.
Creates a commit in the target gitnook from its current index.
gitnook commit -m "update token"
gitnook commit -m "checkpoint before refactor" --to scratchOutput: [<name>] <short-sha> <message>
Shows working-directory status for all gitnooks (no argument) or a single named gitnook.
gitnook status
gitnook status secretsShows the working-tree diff between the current on-disk state of tracked files and their last committed content in the gitnook. Uses unified diff format with 3 lines of context.
gitnook diff
gitnook diff secrets- Files staged but never committed appear with
--- /dev/null(entire content shown as additions). - Files unchanged since the last commit are omitted.
- Prints
No changes.if nothing differs.
Shows commit history for the active gitnook or a named one, in reverse chronological order.
gitnook log
gitnook log personalIf the gitnook has no commits yet: No commits yet in gitnook '<name>'
Lists all gitnooks in the current repo, with file counts and an active marker.
gitnook listExample output:
* secrets (active) 3 files tracked
personal 1 file tracked
scratch 2 files tracked
Changes the active gitnook. The active gitnook is the default target for add, commit, status, diff, and log when --to is not specified.
gitnook switch personalThe change is persisted to .gitnook/config.toml.
Permanently deletes a gitnook and cleans up all of its state:
- Removes every tracked file's path from
.git/info/exclude(those files become visible to the outer git again as untracked) - Deletes the
.gitnook/<name>/bare repo directory - Removes the gitnook from
.gitnook/config.toml; if it was the active gitnook, the active is reassigned to the next remaining one
If it is the last gitnook in the repo, destroy also removes the .gitnook/config.toml, the .gitnook/ directory itself, and the .gitnook/ entry from .git/info/exclude - leaving no trace.
gitnook destroy scratch
gitnook destroy secretsThis operation is irreversible. The commit history stored in the bare repo is deleted. The tracked files themselves are not deleted from disk.
Errors:
-
gitnook '<name>' does not exist- rungitnook listto see all gitnooks
A repo can contain any number of named gitnooks. Each has its own independent index, commit history, and tracked file list.
# Set up two gitnooks
gitnook init secrets
gitnook init scratch
# Add files to each
gitnook add .env.local --to secrets
gitnook add .env.test --to secrets
gitnook add scratch.md --to scratch
# Commit independently
gitnook commit -m "local credentials" --to secrets
gitnook commit -m "initial scratch" --to scratch
# Check the full picture
gitnook list
# * secrets (active) 2 files tracked
# scratch 1 file tracked
gitnook status
# [scratch] clean
# [secrets] cleangitnook switch scratch
gitnook add new-idea.md # goes to scratch, no --to needed
gitnook commit -m "new idea"--to overrides the active gitnook for a single command without permanently switching. Useful when you want to stay active on one gitnook but occasionally commit to another.
gitnook add token.txt --to secrets # does not change the active gitnookmy-project/
├── .git/
│ └── info/
│ └── exclude ← gitnook-managed exclusions
├── .gitnook/
│ ├── config.toml ← global config: active gitnook + registry
│ ├── secrets/ ← bare git repo
│ │ ├── HEAD
│ │ ├── config
│ │ ├── objects/
│ │ └── refs/
│ └── scratch/ ← another bare git repo
│ └── ...
└── src/
Each gitnook directory is a valid bare git repository created with git2::Repository::init_bare(). gitnook never shells out to git - all operations (staging, committing, diffing, log walking) go through the libgit2 C library via the git2 Rust crate.
.git/info/exclude is git's local-only ignore file - it is never committed and never pushed. gitnook uses it exclusively (never .gitignore) so that exclusions are completely invisible to the team. The file is created if it does not exist. All entries written by gitnook are plain file paths, one per line.
.gitnook/config.toml is a TOML file that records which gitnooks exist and which one is active:
active = "secrets"
[gitnooks.secrets]
created = "2025-01-15T10:00:00Z"
[gitnooks.scratch]
created = "2025-01-16T08:30:00Z"This file is excluded from the outer git (via .gitnook/ in exclude) and lives entirely on your machine.
If you run gitnook add on a file that is already in the outer git's index (i.e., already committed to the team repo), gitnook will print a warning:
Warning: .env.local is currently tracked by git. Run: git rm --cached .env.local
gitnook still adds the file to the gitnook index and exclude file, but the outer git will continue to track the old committed version until you run git rm --cached. After that, the file disappears from the outer git history going forward (existing commits are unaffected).
gitnook add on a file that is already in the same gitnook re-stages the current on-disk content. This is intentional - it is how you stage modifications before a commit.
echo "new secret" >> .env.local
gitnook add .env.local # re-stage the modification
gitnook commit -m "rotate token"gitnook resolves all file paths relative to the git root. If you pass a path that is outside the root, the command errors: '<path>' is outside the git repo.
All gitnook commands require an outer git repo. Running gitnook outside one gives:
Error: Not inside a git repository
If you run any command (other than init) without having initialised at least one gitnook:
Error: No gitnooks found. Run 'gitnook init' first.
| Limitation | Detail |
|---|---|
| Local only | Gitnooks are never pushed. No remote, clone, or fetch support in v1. |
| No branching | Each gitnook has a single linear history on main. |
| One file per gitnook | A file can only be tracked by one gitnook at a time. |
| Feature | Description |
|---|---|
gitnook push |
Push a gitnook as a git bundle or to a bare remote for backup or selective sharing |
gitnook branch / gitnook checkout
|
Create and switch branches within a gitnook |
| Shell completions | Tab completion for commands and gitnook names (bash, zsh, fish) |
gitnook stash |
Stash uncommitted index state within a gitnook |
Contributions are welcome. The project is structured as a standard Rust binary crate:
src/
├── main.rs ← CLI entry point and command dispatch
├── cli.rs ← clap Cli struct and Commands enum
├── repo.rs ← outer git root detection
├── config.rs ← .gitnook/config.toml read/write
├── exclude.rs ← .git/info/exclude read/write
├── gitnook.rs ← core operations
└── error.rs ← thiserror error types
tests/
└── integration.rs ← end-to-end integration tests
cargo test # all tests
cargo test --test integration # integration tests only
cargo test --lib # unit tests only- Never write to
.gitignore- all exclusions go to.git/info/exclude -
.gitnook/must always be in.git/info/excludeafterinit - Never shell out to
git- use thegit2crate for all git operations - A file can only belong to one gitnook at a time
- All errors go to stderr; all normal output goes to stdout