Commits, hashes and history
Beginner Core Git Git CLI
Why this matters
Everything in Git is made of commits. Branches point at them, merges combine them, reviews discuss them, releases name them, and every "undo" is a movement between them. Once you know what a commit contains and how commits connect, git log stops being a wall of text and becomes a story you can read.
What a commit contains
Git can print a commit's raw content. It is small and readable:
$ git cat-file -p HEADtree e297bc0065845ba6de1ebe44b53beba3915f167d
parent 0d9eb38f20c62bc225052eaa54f22d806c4f12e1
author Ana <ana@northwind-trails.example> 1788438600 +0200
committer Ana <ana@northwind-trails.example> 1788438600 +0200
docs: add Windows steps to installation guide (#12)Five things:
| Line | Meaning |
|---|---|
tree |
A pointer to the snapshot: the complete list of files and their contents at this moment |
parent |
The commit that came before this one. The first commit has none; a merge commit has two |
author |
Who wrote the change, and when (the number is seconds since 1970; git log shows it as a date) |
committer |
Who recorded it; usually the same person, different after some operations such as rebase |
| the message | Why, in the author's words |
Everything else Git shows you is derived from these five lines and the snapshot they point to.
Hashes
Every commit is identified by a hash: 40 characters (Git is moving to 64) computed from the commit's content, including its parent. Change anything, even a letter of the message, and the hash changes.
$ git rev-parse HEAD97773ae0017c4eb07f069e6008fba12c656e0a9aNobody types 40 characters. Git accepts any unique prefix, and shows seven by default:
$ git rev-parse --short HEAD97773aeThree consequences:
- Your hashes never match the course's, because your commits have your name and your dates in them. That is expected.
- A hash is a fingerprint. If two people have commit
97773ae, they have byte-for-byte the same snapshot and the same history behind it. This is what makes "we are looking at the same thing" a certainty rather than a hope. - History cannot be silently edited. Changing an old commit changes its hash, and therefore the hash of every commit after it. That is the whole story behind lesson 2.7.
Reading history
git log walks the chain from the newest commit backwards along parent links:
$ git log --oneline --decorate97773ae (HEAD -> main) docs: add Windows steps to installation guide (#12)
0d9eb38 (tag: v1.2.0, origin/main) docs: add map question to FAQ (#9)
8fa922e Initial import of trailguideNewest first. The names in parentheses are labels pointing at commits: HEAD -> main means "you are on branch main, which points at this commit"; origin/main is where the remote was at the last fetch; tag: v1.2.0 is a release name. Lesson 2.5 explains each of them.
The long form shows the metadata:
$ git log -1commit 97773ae0017c4eb07f069e6008fba12c656e0a9a
Author: Ana <ana@northwind-trails.example>
Date: Thu Sep 3 14:30:00 2026 +0200
docs: add Windows steps to installation guide (#12)-1 limits it to one commit; -5 shows five. And git show adds what changed:
$ git show --stat HEADcommit 97773ae0017c4eb07f069e6008fba12c656e0a9a
Author: Ana <ana@northwind-trails.example>
Date: Thu Sep 3 14:30:00 2026 +0200
docs: add Windows steps to installation guide (#12)
docs/getting-started.md | 4 ++++
1 file changed, 4 insertions(+)Without --stat, git show prints the full line-by-line diff. Both accept any hash: git show 0d9eb38.
Good commit messages, briefly
The message is the only part of a commit written for humans, and the one you will read most. The convention this course follows, and the playground's CONTRIBUTING asks for:
- A short subject line in the imperative, at most about 50 characters, with a type prefix:
docs: add Windows steps to installation guide (#12). - Optionally a blank line and a body explaining why, when the subject is not enough.
Lesson 5.4 goes deeper; for now, notice that the messages above tell the story on their own.
Common mistakes
- Comparing hashes with the course or a colleague and worrying they differ. They will. Compare messages and content instead; hashes only match for the same commit.
- Typing a full hash by hand. Copy it, or use the first seven characters.
- Expecting
git logto show uncommitted changes. It shows commits only.git statusandgit diffshow the rest. - Getting stuck in the pager. Long logs open in a viewer; press q.
Try it yourself
Goal: inspect the raw content of your latest commit and find its parent.
- In the playground, run
git log --onelineand note the two newest hashes (make a second commit first if you only have one: editdocs/faq.md,git add,git commit -m "docs: test commit"). - Run
git cat-file -p HEADand find theparentline. - Compare the parent hash with the second line of
git log --oneline.
Expected result: the parent hash begins with the same seven characters as the second commit in the log.
Show solution
HEAD is the newest commit; its parent line names the commit before it, which is exactly the second line of git log. The chain in git log is nothing more than Git following parent lines one by one.