Skip to main content

tracked

Function tracked 

Source
pub fn tracked(path: &Path) -> Tracked
Expand description

Ask git whether it tracks path.

Asked as git -C <parent> ls-files --error-unmatch -- <file-name>, and the two halves of that are both deliberate.

The BASENAME, not the absolute path. git normalises a pathspec LEXICALLY, but resolves its working directory PHYSICALLY. When .git/hooks is a symlink into the working tree — the setup at the centre of both incidents — handing git the absolute path <repo>/.git/hooks/pre-commit gets exit 1, pathspec did not match, because no such path exists in the index under that spelling. Handing it pre-commit from -C <repo>/.git/hooks resolves through the link to <repo>/devhooks/pre-commit and gets exit 0. Same file, same question, opposite answers: the absolute-path form silently reports every hook reached through a symlinked directory as untracked. (amont-fleet’s own fix::is_tracked still asks the absolute-path way, and has this hole.)

The exit code, not status.success(). git distinguishes them and the distinction is the whole point:

  • 0 — tracked.
  • 1 — the pathspec matched nothing in the index. Untracked. This is also what a hook under a plain .git/hooks returns, since .git is outside the working tree.
  • 128 + “not a git repository” — no repository at all. Treated as untracked: the test fixtures throughout this suite build hook directories in bare temp dirs, and refusing those would make every one of them fail for a reason unrelated to what it tests.
  • anything else — spawn failure, detected dubious ownership, a broken .git, a permissions error. Tracked::Unknown, which refuses.

The dubious ownership case is the one worth naming out loud, because it is common (a repo owned by another uid, which is every repo inside a container bind mount) and because it produces a fatal: that a success() check reads as a clean “no”. Under the old predicate, that meant install --force was free to write over tracked files in exactly the environments where the user cannot see what happened. The refusal message names git config --global --add safe.directory <path> because that is the fix, and a refusal without one is just an obstacle.