Skip to content

Index and working tree

The index records what the next commit would contain; the working tree is the checked-out files. girt has no add or commit, so the examples use git to make changes for girt to look at. These commands read both and, in the case of checkout and worktree, change them. Status and checkout work on macOS and Linux only. The library side is in The index and working tree.

girt index list prints the path of every index entry. --stage adds the mode, the blob ID and the stage number, which is 0 except during a conflicted merge.

$ girt index list --stage
100644 2f7896d1d1365eafb0da03d9fe456fac81408487 0 .gitignore
100644 94954abda49de8615a048f8d2e64b5de848e27a1 0 README.md
100644 f328e4d9d04c31d0d70d16d21a07d1613be9d577 0 src/main.rs

girt status compares HEAD, the index and the working tree and prints one <XY> <path> line per changed path, using the letters of git status --porcelain: the first column is the index against HEAD, the second the working tree against the index. -u adds untracked files, filtered through .gitignore, info/exclude and core.excludesFile. Without -u it corresponds to git status --porcelain -uno, and with it to --untracked-files=all. -b starts with the branch line of git status -sb, such as ## main...origin/main [ahead 1, behind 2]: the commits each side has that the other lacks, or [gone] when the upstream reference no longer exists.

$ printf 'more\n' >> README.md
$ printf 'new\n' > new.txt && git add new.txt
$ printf 'scratch\n' > notes.txt
$ girt status -u
M README.md
A new.txt
?? notes.txt

Status converts working files as Git does before comparing them: line endings, ident, working-tree-encoding and filter programs such as Git LFS, configured in local or global config and chosen per path by .gitattributes. --refuse-filter-commands fails instead of running a filter program, and --raw compares literal bytes; see Line endings and filters. It applies no core.filemode or core.symlinks policy and no rename detection, so a repository that relies on those can show changes Git wouldn’t. On macOS, non-ASCII working-tree paths are refused for now.

girt ignore check reports, for each path, the rule that decides whether it is ignored, or that none does. Tracked paths are never ignored. It exits 0 when any path is ignored and 1 otherwise.

$ mkdir -p target && touch target/app
$ girt ignore check target/app notes.txt
target/app: ignored by .gitignore:1: target/
notes.txt: not ignored

girt checkout <revision> replaces the index and working tree with that revision’s tree. It doesn’t move HEAD, so afterwards status reports the difference as staged changes. It prints what it changed. It refuses to run over uncommitted changes, so the first command puts the status example’s changes away.

$ git reset -q --hard && rm notes.txt
$ girt checkout main~1
installed 0, removed 1, created 0 directories, removed 0 directories
$ girt status
D .gitignore
$ girt checkout main --from main~1
installed 1, removed 0, created 0 directories, removed 0 directories

Checkout updates the working tree from a known starting point, --from, which defaults to HEAD. That baseline must match the index and working tree exactly: girt refuses to run over dirty, staged or conflicted paths rather than overwrite them. After a clone --no-checkout, where there is no index yet, the baseline is empty. Files are converted as in status, with the same --raw and --refuse-filter-commands options, and on macOS non-ASCII paths are refused, as in status. Split and sparse indexes, resolve-undo data and unknown index extensions are refused; the derived caches (TREE, FSMN, UNTR, EOIE, IEOT) are dropped for Git to rebuild.

Move the branch or unstage files (girt reset)

Section titled “Move the branch or unstage files (girt reset)”

girt reset <revision> moves the current branch, or a detached HEAD, to a commit and replaces the index with that commit’s tree, so the commits you stepped back over show up as unstaged changes. --soft moves only the branch and keeps those changes staged. Working files never change. Both record the old commit in ORIG_HEAD and append reset: moving to <revision> to the reflogs, as git reset does. With paths after --, only those index entries are reset, which unstages them without moving anything.

$ printf 'draft\n' >> README.md && git add README.md
$ girt reset -- README.md
$ girt status
M README.md
$ girt reset --soft main~1
$ girt status
A .gitignore
M README.md

--hard, --keep and --merge, which also rewrite working files, are not available yet, and an index from a sparse checkout is refused.

girt worktree list prints the main worktree and every linked one, with its HEAD and branch. The other subcommands look after a registration under .git/worktrees/: lock protects it from pruning, unlock removes that protection, prune deletes a registration whose checkout no longer exists, with its HEAD and index, and repair relinks a registration with a checkout that has moved.

$ git worktree add -q ../review
$ girt worktree list
/home/me/demo 7ed7a0cc68995713f52d973e6adfd3d9e667324f [main]
/home/me/review 7ed7a0cc68995713f52d973e6adfd3d9e667324f [review]
$ girt worktree lock review --reason "on a removable disk"
$ girt worktree unlock review
$ mv ../review ../review-moved
$ girt worktree repair review ../review-moved
$ girt worktree list
/home/me/demo 7ed7a0cc68995713f52d973e6adfd3d9e667324f [main]
/home/me/review-moved 7ed7a0cc68995713f52d973e6adfd3d9e667324f [review]

The CLI has no command to create a linked worktree. The library can (Repository::create_orphan_worktree) creates one with an unborn branch, and git worktree add works too.