Git, CI/CD & GitOpsVII · Repository InspectionInspection
git show and the commit object — inspecting one commit in full
What you'll learn
- Explain what git show <rev> displays: the commit object header and the diff against the parent
- Use git show <rev>:<path> to extract a single file at a single commit without checking it out
- Use git show <rev> -- <path> to view the diff of a single path at a single commit
- Distinguish git show from git log -p and git diff for the same commit
- Recognise show as the right command for post-mortem inspection of a single commit
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
git log answers the question “what happened?”. git show answers
the question “what does this one commit look like?”. The two
commands overlap with git diff, but show is the right tool for
the single-commit case: it pairs the commit object metadata with
the diff against the parent in one command, and the <rev>:<path>
syntax it inherits from git rev-parse is the production primitive
for extracting a single file from history without checking it out.
The post-mortem operation “show me what the Terraform plan looked
like at the commit that introduced the incident”, the CI operation
“extract the Dockerfile from the commit that was tagged as the
release”, and the audit operation “show me the contents of the IAM
policy at the commit hash recorded in the change ticket” are all
the same command: git show <rev>:<path>.
What show displays
git show <rev> produces three sections in this order:
- The commit object header — the same output as
git log -1 <rev>: full hash, author, committer, date, subject, and body. - A blank line, then the commit message body if it was not already printed.
- The diff against the parent — the same output as
git diff <rev>~1..<rev>orgit log -p -1 <rev>.
git show 8a3f9d2
# commit 8a3f9d2c1b4e7f0a9d6c5b2e8f1a4d7c0b3e6f9a (HEAD -> main, origin/main)
# Author: Ada Lovelace <ada@example.com>
# Date: Mon Aug 18 14:23:09 2026 +0000
#
# fix(terraform): pin router module to v3.2.7
#
# The 3.2.6 release introduced a regression in the
# us-east-1 default route that triggered a fan-out
# of duplicate routing entries.
#
# diff --git a/modules/router/versions.tf b/modules/router/versions.tf
# index 1b2c3d4..5e6f7a8 100644
# --- a/modules/router/versions.tf
# +++ b/modules/router/versions.tf
# @@ -1,5 +1,5 @@
# module "router" {
# source = "git::ssh://git@github.com/example/router.git"
# - version = "3.2.6"
# + version = "3.2.7"
# }
The output is exactly what a post-mortem reader needs: the what
(commit metadata) and the change (diff) in one scroll. The same
information from git log would require two commands and a
pipe; git show is the single command that does both.
The rev:path syntax
The most powerful form of git show is not the default. It is the
<rev>:<path> syntax, which extracts a single file at a single
commit and writes its contents to standard output:
git show 8a3f9d2:modules/router/versions.tf
# module "router" {
# source = "git::ssh://git@github.com/example/router.git"
# version = "3.2.7"
# }
The output is the raw file contents, not the diff. This is the form that production scripts consume: a CI pipeline that needs to read the Dockerfile at the tagged release uses the command to extract the bytes, and an incident post-mortem that needs the IAM policy at the offending commit uses the command to retrieve the policy without touching the working tree.
The <rev> can be any revision expression: a full hash, an
abbreviated hash, a branch name, a tag name, a relative reference
like HEAD~3, or a reflog entry like HEAD@{2}. The <path> is
the path as it exists in the commit object, not the path in the
working tree. The two are usually identical, but a rename can
make them differ.
# Extract a file from a tagged release
git show v3.2.7:deploy/prod/main.tf > /tmp/main.tf
# Extract a file from the most recent commit that touched it
git show HEAD~3:terraform/backend.tf
# Extract a file from a specific commit in a different branch
git show feature/edge-mtu:terraform/main.tf
The <rev>:<path> syntax is the production primitive for “give me
the version of this file at this commit”. It is the difference
between checking out a commit (which moves the working tree and
resets the index) and reading a file from history (which leaves
the working tree untouched).
The — separator for path-restricted diffs
The <rev> -- <path> form is different: it shows the diff of
the path at the named commit, not the file contents. The double
dash separates the revision from the path arguments; any path
after the dash is a path filter, not a revision.
# Diff of one file at one commit
git show 8a3f9d2 -- modules/router/versions.tf
# commit 8a3f9d2...
# Author: Ada Lovelace <ada@example.com>
# Date: Mon Aug 18 14:23:09 2026 +0000
#
# fix(terraform): pin router module to v3.2.7
#
# diff --git a/modules/router/versions.tf b/modules/router/versions.tf
# ...
The two forms are the answer to two different questions:
git show <rev>:<path>— “what are the contents of this file at this commit?”git show <rev> -- <path>— “what was the change to this file at this commit?”
The first is the right primitive for extracting a file; the second is the right primitive for inspecting a single change in a commit that touched multiple paths.
flowchart LR
Q1["What changed in this commit?"] --> A1["git show <rev>"]
Q2["What were the contents of this file at this commit?"] --> A2["git show <rev>:<path>"]
Q3["What was the change to this file at this commit?"] --> A3["git show <rev> -- <path>"]
Q4["What is the diff between two commits?"] --> A4["git diff <rev1>..<rev2>"]
Q1 --> B1["commit metadata + diff vs parent"]
A2 --> B2["raw file bytes to stdout"]
A3 --> B3["commit metadata + diff for one path"]
A4 --> B4["diff only, no commit metadata"]
show versus log -p versus diff
Three commands all produce a diff. The differences are small but matter for the right production use:
git show <rev>— one commit’s metadata plus the diff against the parent. The right command for “what is in this single commit?”.git log -p -n 1 <rev>— same output asgit show <rev>, but with thelogmachinery that supports--prettyand the traversal flags. The right command when the format string needs to be customised.git diff <rev>~1..<rev>— the diff only, no metadata. The right command when the output is being piped to a patch applier or a code-review tool.
The three commands are interchangeable for the basic case. The choice is “do I want the metadata or just the diff?”.
Production discipline
- Use
git show <rev>:<path>to read a file from history. The command is read-only, leaves the working tree untouched, and produces raw file bytes to stdout. It is the right primitive for CI, post-mortem, and audit queries. - Use
git show <rev> -- <path>to inspect a single path’s change. The form is the diff-restricted-to-one-path version ofgit show <rev>, and is the right command when the commit touched many paths but the question is about one of them. - Prefer
git showovergit log -p -n 1for inspecting a single commit. The intent is clearer; the output is the same. - Never use
git checkout <rev> -- <path>to read a file. The command updates the index, which can silently change the next commit. Usegit showinstead.
Cross-course references
- Linux for Production Sysadmins - Part IX (Filesystem) distinguishes the same way between “read the file” and “check out the file”, and covers the discipline of leaving the working tree untouched when only a read is needed.
- Ansible for Production Sysadmins - Part XXXVII (RepoArch)
uses
git show <tag>:playbooks/site.ymlas the canonical “what was in this release?” query. - Terraform for Production Sysadmins - Part IX (State)
uses
git show <rev>:deploy/prod/main.tfas the canonical “what was the production Terraform at this commit?” query.
Quiz
Knowledge check · 4 questions
Q1. A CI pipeline needs to extract the `Dockerfile` at the release tag `v3.2.7` to inspect it without modifying the working tree. Which command is the safest?
Q2. `git show <rev> -- <path>` and `git show <rev>:<path>` produce the same output: both extract the raw bytes of `<path>` at `<rev>` to standard output.
Q3. Name the two Git commands that read a file from history without modifying the working tree or the index, and explain why `git checkout <rev> -- <path>` is not the same.
Q4. Diagnose a contaminated index by identifying the wrong command among the four that an engineer ran, and recommend the safe primitive for the same query.
An engineer is preparing a release commit. They want to inspect the Ansible playbook that was tagged as `v3.2.6` to verify the contents before cutting `v3.2.7`. They run `git checkout v3.2.6 -- playbooks/site.yml` and read the file. They then run `git status` and see `M playbooks/site.yml`. They realise the index now holds the contents of `v3.2.6`, not the current working tree. They run `git restore --staged playbooks/site.yml` to undo the contamination, then re-run the read with `git show v3.2.6:playbooks/site.yml`. The contamination is reversed.
Passing score: 75%. Answers are checked in this browser.