DEV Community

orca forge
orca forge

Posted on Originally published at forge.workstyle.tech

How to Check Git State Without Misreading Command Results

📝 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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 grep to 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)