Free lesson · Git Mastery
git show
git show
A note from Arun Selvam, Software Engineer at Apple iOS Frameworks.
The Quickest Way to Inspect a Commit
git show is the command I reach for when I want to look at a specific commit - what it changed, what its message says, who made it, when. It's also the command I use to see the contents of a file at a specific point in history. The interface is simple but flexible; with a few variants I'll cover below, it answers most of the "what does this commit actually contain" questions you'll ask in a day.
The first time I really used git show was during code review in my third week at Apple. A colleague's commit had landed in our shared branch and broken something. I needed to see what it changed without checking out the commit or otherwise disrupting my own work. git show <commit-hash> showed me the message, the changed files, and the actual diff - all in one screen. From there I could decide whether to revert it or fix forward.
This topic covers the practical variants and patterns I use most often.
The Basic Form
With no arguments, git show displays the most recent commit:
git show
Without arguments, git show displays HEAD - the most recent commit on your current branch. The output includes the commit hash, author, date, message, and the full diff of changes the commit introduced. This is the fastest way to see what you just committed.
The output has a recognizable structure:
commit a3f2c1d8b9e7f0a1c2d3e4f5a6b7c8d9e0f1a2b3
Author: Your Name <you@example.com>
Date: Mon Jun 1 14:23:11 2026 -0700
Add input validation to login form
Previous code allowed empty passwords through to the
backend, which then returned 500. This adds explicit
validation and returns 400 with a helpful message.
diff --git a/src/login.py b/src/login.py
index 1f2a3b4..5c6d7e8 100644
--- a/src/login.py
+++ b/src/login.py
@@ -42,6 +42,9 @@ def login(request):
username = request.form.get('username')
password = request.form.get('password')
+ if not password:
+ return jsonify({'error': 'Password required'}), 400
+
user = User.query.filter_by(username=username).first()
The structure: header (hash, author, date, message), then the diff. This is the standard format git show produces; the variants below tweak what's included.
Showing a Specific Commit
Provide a commit reference to see that commit:
git show a3f2c1d
git show a3f2c1d8b9e7
git show a3f2c1d8b9e7f0a1c2d3e4f5a6b7c8d9e0f1a2b3
Any unambiguous prefix of the commit hash works. Seven characters is conventional and almost always sufficient. The full forty-character hash works too but is rarely necessary. Git resolves the prefix to the matching commit; if multiple commits start with the same prefix, it asks for more characters.
You can also use relative references:
git show HEAD # most recent commit (same as no argument)
git show HEAD~1 # one commit before HEAD
git show HEAD~3 # three commits before HEAD
git show HEAD^ # parent of HEAD (same as HEAD~1)
git show main # most recent commit on main branch
git show v1.0 # commit pointed to by the v1.0 tag
These show different commits using different reference forms. HEAD is the current commit. HEAD tilde one is one back; HEAD tilde three is three back. HEAD caret is the same as HEAD tilde one - the parent. A branch name resolves to the most recent commit on that branch. A tag name resolves to the commit it points at. Topic 6.8 covers HEAD in depth; Topic 12.1 covers tags.
I use HEAD~1 and HEAD~2 frequently to look at the immediately preceding commits without typing hashes.
Just the Message, Not the Diff
For commits with substantial changes, the diff can be many screens long. To see just the metadata and message:
git show --no-patch <commit>
git show -s <commit>
Both forms - dash dash no dash patch and the short form dash s - suppress the diff and show only the header and message. Useful when you remember a commit existed and want to read its message without scrolling through a large diff.
The short form -s is what I type interactively; the long form is clearer in scripts.
Just the List of Files Changed
To see which files a commit touched without their contents:
git show --stat <commit>
git show --name-only <commit>
git show --name-status <commit>
Three forms with increasing detail. Dash dash stat shows files plus a small diagram of insertions and deletions per file. Dash dash name dash only shows just the file paths. Dash dash name dash status shows the paths plus a letter for what kind of change - M for modified, A for added, D for deleted, R for renamed. All three are useful for getting a quick sense of a commit's scope without reading the actual changes.
The --stat form is my default for quick inspection. The visual summary tells me whether a commit was small (10 lines changed) or substantial (500 lines across 20 files) at a glance.
Showing the Contents of a File at a Specific Commit
A pattern I use often: viewing a single file as it existed at a particular commit, without checking out anything:
git show <commit>:<path>
git show HEAD:src/login.py
git show v1.0:README.md
git show HEAD~5:src/main.py
The colon syntax separates a commit reference from a file path. The command prints the entire contents of the file as it existed at that commit. The current working directory is unaffected - this is purely a read operation. Useful for comparing the historical version of a file against what you have now, or for recovering a file you accidentally changed locally.
A common use: I've broken something in a file and want to see what it looked like a few commits ago. Rather than checking out the old version (which disrupts my working directory), I git show HEAD~3:path/to/file.py | less and read it in a pager. The current state is preserved; I just look at the past.
This is also useful for grabbing a piece of code from history:
git show HEAD~5:src/helper.py > /tmp/old_helper.py
This writes the historical version of the file to a separate location. Useful when you want to compare versions side-by-side in your editor without disrupting the current working tree. The redirect is shell standard output redirection; Git itself just prints the file contents to standard output and the shell handles the redirect.
Showing a Subset of the Diff
For large commits, you can narrow the displayed diff to specific paths:
git show <commit> -- src/login.py
git show <commit> -- src/
git show <commit> -- "*.py"
The double-dash separates the commit reference from path filters. The shown diff is restricted to the paths listed. Useful for large commits where you want to see only the parts that touched a specific area. The patterns are the same as other Git commands - quote globs to prevent shell expansion.
Word-Level Diff
For text changes within a line, the default line-level diff sometimes obscures what actually changed. The word-diff form is clearer:
git show --word-diff <commit>
git show --word-diff=color <commit>
Word-diff shows changed words highlighted within their surrounding context rather than showing entire changed lines. The dash dash word dash diff equal color form uses terminal colors for the highlights. Especially useful for documentation changes where small wording adjustments produce confusing line-level diffs.
I use this for documentation reviews. A typo fix in a paragraph shows up as one minus-line and one plus-line in the default diff, which obscures what actually changed. The word-diff form highlights just the word.
Combining Forms
The flags compose. Common combinations:
git show --stat --no-patch HEAD~2 # summary of an old commit
git show --name-only HEAD~5 # just files touched 5 commits ago
git show --stat HEAD..HEAD~3 # changes from HEAD back 3 commits combined
Flags can be combined freely. The first shows just the file-level summary without the actual diff. The second shows just the file names from a specific commit. The third combines multiple commits using the dot dot range syntax - though this is unusual for show; git log handles ranges more naturally. Topic 4.2 covers log.
A Pattern During Code Review
When I'm reviewing a pull request and want to understand a specific commit deeply, my sequence is roughly:
git show --stat <commit> # scope: what files, how much
git show --no-patch <commit> # message: what was the author thinking
git show <commit> # full diff: what actually changed
git show <commit> -- <one-file> # focus: deep dive on a specific file
This is the inspection ladder. First the scope - what files and how big. Then the message - what the author was trying to do. Then the full diff if the commit is small enough. Then file-specific diffs for larger commits. Each step takes seconds; together they build a complete understanding of the commit in a couple of minutes.
Not every commit needs the full ladder. A one-line typo fix just needs git show <commit>. A 500-line refactor needs the structured approach.
A Pattern During Debugging
When something has broken and I suspect a recent commit, I work backward from the most recent commits:
git show --stat HEAD # what just landed
git show --stat HEAD~1 # what landed before that
git show --stat HEAD~2 # and the one before
This walks backward through recent commits looking at their scopes. Once I find a commit that touched the affected area, I look at it in detail with the full git show. The walking is fast - one or two seconds per commit. Topic 4.6 covers git bisect for cases where the bad commit is many commits back and walking by hand is slow.
For ten or twenty commits back, this manual walk is fast. For hundreds, git bisect (Topic 4.6) is the right tool.
What Comes Next
Topic 4.2 covers git log - the command for browsing many commits at once rather than inspecting one. Topic 4.3 covers git diff - comparing arbitrary states rather than just one commit's changes. Together they cover most history navigation.
What Goes Wrong
Warning: Common
git showmistakes:Using a hash that's ambiguous (too short). Git refuses if multiple commits start with the same prefix. Add more characters.
Forgetting the colon in the file-at-commit syntax.
git show HEAD path/to/fileis interpreted as two arguments; the file syntax requiresHEAD:path/to/filewith no space.Trying to use
git showfor ranges. It accepts a single commit; for multiple commits, usegit log. The range syntax doesn't behave intuitively inshow.Showing a large commit without
--statfirst. The diff scrolls forever. Get the summary first; dive into specific files second.Confusing
HEAD~1withHEAD^1. They mean different things for merge commits - Topic 6.8 covers the distinction. For non-merge commits they're identical.Expecting word-diff to work on binary files. It doesn't. Binary files show "Binary file changed" regardless of flags.
Try It
Exercise: Practice inspection:
- Run
git showin a repository with history. Read the output structure - hash, author, date, message, diff.- Use
git show HEAD~1,HEAD~2,HEAD~3to see the last few commits in sequence.- Find a commit by hash from
git log --oneline. Usegit show <hash>to inspect it.- Use
git show HEAD:<file>to view a file's contents at the current commit. Compare with what's in your working directory.- Try
--stat,--name-only, and--name-statuson a substantial commit. Notice the different detail levels.- Use
--word-diffon a commit that changed text in documentation. Compare against the default line-diff view.