đ Originally published (in Japanese) at forge.workstyle.tech.
TL;DR Git output alone may not show whether a path exists or a command succeeded. The command that ran and the paths being compared can also change how results should be read. This article reproduces cases involving rev-parse, grep, stash, and git status in a disposable repository.
TL;DR
When checking Git, a non-empty output or a record count alone may not tell you whether the target exists or whether the command succeeded. Check the exit code, the command that ran, and the paths being compared. If youâre using timestamps as evidence, check the state with GIT_OPTIONAL_LOCKS=0.
How to check whether a target exists with rev-parse
You canât determine whether a path exists just because git rev-parse (a command that resolves Git names and paths) produced non-empty output. In the example below, the specified path does not exist in HEAD (the name that refers to the current commit).
$ v=$(git rev-parse "HEAD:ććšăăȘăpath" 2>/dev/null); echo "ć€=[$v] exit=$?"
ć€=[HEAD:ććšăăȘăpath] exit=128
The unresolved argument is printed to standard output as-is. The exit code (a number indicating whether the command succeeded) is 128, but the variable v is not empty. So checking only [ -n "$v" ] would lead you to conclude that the path exists.
To check whether it exists, look at the commandâs exit code rather than whether it produced output.
git cat-file -e <rev>:<path>
In the reproduction I recorded, the exit code was 128 when the path did not exist and 0 when it did. To list a treeâs contents for inspection, use git ls-tree. What matters is whether Git could resolve the specified target, not whether it printed anything.
What to check when grep appears to return 0 matches or an error
If grep appears to find 0 matches or returns an error, check what is actually being invoked. In one environment, a shell function named grep was overriding the command and calling a different grep implementation.
$ grep -c alpha s.txt
error: unknown option '-G'
exit=1
$ command grep -c alpha s.txt
1
exit=0
The file contains one match for alpha. But the first invocation returns an error and an exit code of 1. If you interpret exit code 1 as âno matches,â youâll miss a match thatâs right there. The command meant to check the result can end up hiding the very state youâre trying to verify.
You can check what will be invoked with this command:
type grep
To invoke the regular command instead of a shell function, use command grep. You can also call it by its absolute path. Before checking the match count, make sure the grep you ran is the one you expected.
What Are You Counting When Comparing Stash Differences?
When checking a stash (Gitâs feature for temporarily saving work), state what youâre comparing. stash@{0} is the working tree (the set of files you were working on) at the time of the stash. stash@{0}^2 is the index (the staged content) at that time, stash@{0}^1 is HEAD, and stash@{0}^3 is untracked files.
In one comparison, the entire tree had differences across 3 files and 434 lines. From that, I concluded, âA stash isnât a reliable backup.â But those 3 files werenât among the 20 items I was checking.
After comparing file contents path by path, the 20 items broke down as follows: 15 were identical, 5 were deleted in both places, and none differed. For those 20 items, the stash was a complete backup.
The numbers differed because I wasnât counting the same items. Before revisiting the size of the differences, check that the comparison covers the same scope. Stating upfront âwhich paths, in what two things, are being countedâ makes it easier to interpret the numbers correctly.
Can the timestamp change after git status?
In the environment I recorded, git status, which I intended to use for reading only, changed the index mtime (file modification time).
When timestamps matter as evidence, use this instead of the usual git status:
GIT_OPTIONAL_LOCKS=0 git -C <path> status --porcelain
A command that displays state does not necessarily leave files unchanged. If you plan to treat timestamps as evidence after checking, consider whether the command itself could affect them.
Key takeaways
- Decide which path or file you want to check.
- Check the exit code as well as the output.
- Use
type grepto see whether a shell function replaces the command. - Compare the same paths and file-count scope.
- For timestamps, check state with
GIT_OPTIONAL_LOCKS=0 git -C <path> status --porcelain.
Top comments (0)