In Git, what does git rev-parse HEAD print, and why do scripts rely on it?
answer
- expression in, identifier out
- branch names move, hashes do not
- full hexadecimal on standard output
- other modes report branch and repo paths
- fails on a repository with no commits
basics
~20 sIt prints the full object ID of the commit HEAD currently resolves to, one line on stdout. Scripts use it because it turns any revision expression into an unambiguous, stable identifier they can record or compare.
solid answer
~50 s`git rev-parse` is the plumbing command that turns what you typed into what Git means. `git rev-parse HEAD` resolves the symbolic ref `HEAD` to the branch it points at, then to that branch's commit, and prints the full object ID — 40 hex characters in a SHA-1 repository. That is why build scripts run it to stamp an artifact with the exact commit being built: unlike a branch name, an object ID never moves. It has several other script-facing modes: `--short` prints an abbreviated ID, `--abbrev-ref HEAD` prints the current branch name (or literally `HEAD` when detached), `--verify` insists the argument names exactly one object and exits non-zero otherwise, `--git-dir` and `--show-toplevel` report where the repository and its working tree root are, and `--is-inside-work-tree` answers whether the current directory is in one. In a brand-new repository with no commits, `git rev-parse HEAD` fails, because `HEAD` points at a branch that does not exist yet.
code
console · 6 lines$ git rev-parse HEAD
9f2c0d1a3b47e5c8d90a1f6b2e4c7d8a5b3e1f00
$ git rev-parse --short HEAD
9f2c0d1
$ git rev-parse --abbrev-ref HEAD
maingo deeper
Recall that this command turns HEAD, a branch name or a tag into the full commit hash, and that build scripts use it to record exactly which commit was built.
Explain the two hops through the symbolic ref, and know the script-facing modes: --verify, --abbrev-ref, --git-dir, --show-toplevel.
Demonstrate robust scripting: check exit status, handle the unborn-branch and detached-HEAD cases, and use --git-dir rather than assuming .git is a directory.
Frame it as the stable contract that shared tooling should depend on, so automation does not break when Git reformats human-facing output or when worktrees change the layout.
## What the command is for Git lets you name a commit in many ways: a branch name, a tag, `HEAD`, an abbreviated hash, `HEAD~3`, `main@{yesterday}`. Every command that takes a revision must first reduce that expression to a concrete object ID. `git rev-parse` is that reduction, exposed as a standalone plumbing command. Give it an expression, get back the object ID it denotes. `git rev-parse HEAD` therefore does two hops. `HEAD` is normally a *symbolic* ref — a file containing `ref: refs/heads/main` — so Git follows it to the branch ref, reads the object ID stored there, and prints it. The output is one line: the full hexadecimal object ID, 40 characters in a SHA-1 repository and 64 in a SHA-256 one. No abbreviation, no colour, no decoration, because plumbing output is meant to be consumed by another program. If `HEAD` is detached, it contains an object ID directly rather than a `ref:` line, and rev-parse simply prints that. In a freshly initialised repository, `HEAD` points at a branch that has no commits yet — the unborn branch — and `git rev-parse HEAD` fails with a non-zero exit status, which is a case scripts must handle rather than assume away. ## Why scripts want it A branch name is a moving target: `main` means something different tomorrow. An object ID is permanent and globally identifies exactly one commit. So the pattern in any build or deployment script is to resolve once, early, and use the resolved ID everywhere afterwards — stamped into a version string, embedded in an image tag, written into a manifest, echoed into logs. That way every artifact from that run refers to the same commit even if someone pushes mid-build. The alternatives are all worse. Scraping `git log -1` parses human output that may change. Reading `.git/HEAD` directly ignores symbolic refs, packed refs, worktrees and alternate `GIT_DIR` layouts. rev-parse handles all of that and is a documented, stable interface. ## The modes worth knowing - `git rev-parse --short HEAD` — an abbreviated ID, long enough to be unambiguous in this repository. Good for display, poor for storage, because the required length grows as the repository does. - `git rev-parse --abbrev-ref HEAD` — the current branch's short name, or the literal string `HEAD` when detached. The standard way to ask "what branch am I on" from a script. - `git rev-parse --verify <rev>` — resolve strictly: exactly one object, or fail. Combined with `--quiet` it becomes a clean existence test driven by exit status rather than output. - `git rev-parse --git-dir` — the path to the repository directory, honouring `GIT_DIR` and the `.git` *file* used by linked worktrees and submodules, where `.git` is not a directory at all. - `git rev-parse --show-toplevel` — the absolute path of the working tree root, the reliable way for a script to anchor relative paths. - `git rev-parse --is-inside-work-tree` — prints `true` or `false`, so a script can bail out politely when run outside a repository. ## Exit codes and error handling Because it is plumbing, rev-parse communicates failure through its exit status, and that is the intended way to use it. Two idioms are worth memorising: `git rev-parse --verify --quiet <rev>` to test that a revision resolves, and `git rev-parse --is-inside-work-tree` to test that you are inside a repository at all. Any script that runs `git rev-parse HEAD` without checking the exit status will silently produce an empty or error-laden value in an empty repository or outside a working tree. ## Where the boundary is rev-parse is also the command that implements Git's full revision syntax, so it can resolve far more elaborate expressions than `HEAD`. For plumbing purposes what matters is the contract: one expression in, one unambiguous object ID or path out, on stdout, with the exit status carrying success. That contract is what makes it the first line of almost every Git-aware script.
- What does git rev-parse --abbrev-ref HEAD print when HEAD is detached?The literal string `HEAD`. With a detached HEAD there is no branch name to report, so scripts that expect a branch must treat that value as the detached case rather than as a branch called HEAD. Pairing it with `git rev-parse HEAD` gives you both the name, when one exists, and the exact commit either way.
- Why prefer git rev-parse --git-dir over hardcoding .git in a script?Because `.git` is not always a directory. In a linked worktree or a submodule it is a file pointing elsewhere, and `GIT_DIR` can relocate it entirely. `--git-dir` resolves all of those cases correctly, whereas a hardcoded path silently breaks the moment the script runs somewhere less conventional.
- How do you use rev-parse to test whether a revision exists without printing anything?`git rev-parse --verify --quiet <rev>` resolves strictly and suppresses output, so the exit status alone carries the answer: zero if the revision names exactly one object, non-zero otherwise. That is the plumbing idiom — check the status, do not parse the message.
saying these in an interview costs you the question
- Reads .git/HEAD directly instead of resolving it
- Thinks rev-parse prints the commit message or author
- Assumes --short output is stable enough to store
- Expects it to succeed in a repository with no commits
- Confuses --abbrev-ref output with a detached-HEAD hash