Use git rev-parse to Resolve Revisions and Repository Paths
You will finish with a small, reliable toolkit for Git scripts: locate the repository from any subdirectory, turn a revision into an object ID, and express the commits changed between two refs. The examples were checked with Git 2.43.0, matching the installed git-man page on this machine.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 15 minutes. You need Git and a working repository. These commands read repository state only; none needs elevated privileges and none changes commits, refs or configuration.
1. Confirm the installed command
Check the executable and version before relying on an option. This matters when a script is copied to an older host:
$ command -v git
/usr/bin/git
$ git --version
git version 2.43.0
Checkpoint: the version output should be the version whose manual you are using. The command is normally called as git rev-parse, not as a separate executable.
2. Find the working tree from a subdirectory
Run these from anywhere below the working tree:
$ git rev-parse --show-toplevel
/home/you/project
$ git rev-parse --show-prefix
src/lib/
$ git rev-parse --show-cdup
../../
--show-toplevel prints the top-level directory, absolute by default. The prefix is the current directory relative to that top level. The cdup value is the path back up to it. At the top level, --show-prefix and --show-cdup produce an empty line.
These values solve a common script error: a relative path received in src/lib is not automatically relative to the repository root. A script can preserve the caller's location, then switch to the root deliberately:
$ root=$(git rev-parse --show-toplevel) || exit 1
$ cd "$root" || exit 1
$ printf 'repository root: %s\n' "$PWD"
repository root: /home/you/project
Checkpoint: if this reports that you are not in a repository, stop there. Do not paper over the error with sudo; elevated privileges do not make an unrelated directory a Git work tree.
3. Inspect the repository and its paths
Use the following read-only checks when a script needs to distinguish a work tree from Git's internal directory:
$ git rev-parse --is-inside-work-tree
true
$ git rev-parse --is-inside-git-dir
false
$ git rev-parse --git-dir
/home/you/project/.git
$ git rev-parse --show-object-format
sha1
The last value is the repository's object format, not a promise that every Git installation uses SHA-1. A repository using another supported hash format reports that format instead. --git-dir can be relative when Git chooses a relative path. Request a canonical absolute path when that is what a script needs:
$ git rev-parse --path-format=absolute --git-dir
/home/you/project/.git
--git-path objects resolves a path below the Git directory while respecting relocation variables such as GIT_OBJECT_DIRECTORY. That is safer for scripts than concatenating $(git rev-parse --git-dir) with a guessed directory name.
4. Validate a revision before using it
--verify requires exactly one argument and prints its object name only when Git can resolve it. Add a type suffix when a commit is required:
$ git rev-parse --verify HEAD^{commit}
8fc4c13fa03a16665b594e35dc991f3ae967062a
$ git rev-parse --verify --short HEAD
8fc4c13
The full length depends on the repository's object format. --short produces a unique abbreviation with at least four characters, using the effective core.abbrev setting when no length is supplied. Do not use a short ID as a permanent identifier or security decision.
For a revision supplied by another person, a file or a web request, put --end-of-options before the value. This prevents a value beginning with a dash being interpreted as another rev-parse option:
$ REV='HEAD'
$ git rev-parse --verify --end-of-options "$REV^{commit}"
8fc4c13fa03a16665b594e35dc991f3ae967062a
Quote the shell variable. If an invalid value should simply fail a test, add --quiet and inspect the status:
$ git rev-parse --verify --quiet --end-of-options 'not-a-revision' >/dev/null
$ printf 'status: %s\n' "$?"
status: 1
--default FALLBACK supplies a fallback only when no parameter is given. It does not turn an invalid supplied revision into the fallback. Treat a non-zero status as an error before using the output.
5. Read ranges without confusing their direction
Commands such as git log consume sets of reachable commits. Two-dot notation means the right side, excluding commits reachable from the left:
$ git log --oneline origin/main..HEAD
7b31c2a Add report
2e9a8f1 Fix parser
This asks what your current branch has that origin/main does not. The shorthand origin/main.. means origin/main..HEAD. The reverse, ..origin/main, asks what the remote side has that your current HEAD does not.
Three dots select the symmetric difference: commits reachable from either side but not both. It is useful for seeing both sides of a branch split:
$ git log --oneline --left-right main...feature/report
< 1a2b3c4 Main branch change
> 7b31c2a Add report
Do not put two dotted ranges beside one another and assume most Git commands will process two independent ranges. For ordinary history commands, the arguments form one combined revision set.
6. Use revision suffixes deliberately
Several compact forms are useful once the basic checks are clear:
HEAD~3means the third first-parent ancestor.HEAD^2selects the second parent of a merge commit.HEAD^{tree}peels a tag or commit to a tree object.HEAD:README.mdnames the file as stored in the tree atHEAD.@{1}means the previous value of the current branch in its reflog, if that reflog exists.
These are names, not file paths on disk. A missing reflog entry, an absent path, or an object of the wrong type makes verification fail. Test the exact suffix you intend to pass to the next command.
7. Quote arguments for shell scripts
--sq-quote emits one shell-quoted line and only quotes its input. It does not interpret normal rev-parse options:
$ git rev-parse --sq-quote 'a b' "x'y"
'a b' 'x'\''y'
--sq is different: it still interprets rev-parse input, then emits one shell-quoted line. When using either result with eval, quote the command substitution exactly as shown in the manual and never mix untrusted text into a hand-built command string casually. Prefer arrays in Bash when you control the script; they avoid eval altogether.
Done means
- You can locate the repository root and distinguish it from
.git. - You verify a user-supplied revision with a type suffix and
--end-of-options. - You understand that
A..Bselects B minus A, whileA...Bis symmetric. - You treat short object IDs as display values, not durable identifiers.
- Your script quotes paths and revision values, checks exit statuses, and makes no unnecessary state changes.