A Git commit is an immutable snapshot of all tracked files in a repository, stored together with metadata such as the author, committer, timestamp and message, and with links to one or more parent commits. Among version control and development tools, the commit is the basic unit of history in Git: every branch, tag, merge and pull request ultimately points to commits.
Each commit is identified by a cryptographic hash of its content, traditionally a 40-character SHA-1 value and, in repositories using the newer object format, a 64-character SHA-256 value. Because the hash covers the snapshot and the parent links, changing any past commit changes every hash after it, which makes Git history tamper-evident.
What a Git commit contains
Despite how tools display it, a commit does not store a diff. It is a small object in Git’s content-addressed database that points to a tree object, which records the full directory structure; trees in turn point to blob objects holding file contents. Unchanged files are shared between commits through identical hashes, so snapshots are cheap, and Git later compresses similar objects into packfiles using delta encoding.
| Field | Content | Purpose |
|---|---|---|
| tree | Hash of the root tree | Defines the complete project snapshot |
| parent | Zero, one or several commit hashes | Links the commit into history |
| author | Name, email, timestamp, time zone | Who wrote the change and when |
| committer | Name, email, timestamp, time zone | Who applied it to this history |
| gpgsig (optional) | Signature block | Proves who created the commit |
| message | Free text, subject line and body | Explains why the change was made |
The author and committer can differ: when a maintainer applies a contributor’s patch or a developer rebases someone else’s work, the author stays the original writer while the committer becomes the person who created the new commit object.
How a commit is created
Creating a commit is a two-stage process. The developer first selects changes with git add, which copies file contents into the staging area, also called the index. Running git commit then turns the index into a tree, writes a commit object that points to it and to the current HEAD commit as parent, and moves the current branch reference to the new commit.
The staging area allows a developer to commit only part of the work in progress, down to individual lines with git add -p. This is how a messy working session becomes a series of focused commits. Hooks such as pre-commit and commit-msg run at this moment and are commonly used to call a linter, formatter or message checker before the commit is accepted.
| Command | Effect | Rewrites existing history |
|---|---|---|
| git commit | Records a new commit on the current branch | No |
| git commit –amend | Replaces the latest commit with a new one | Yes, the last commit |
| git merge | May create a commit with two or more parents | No |
| git rebase | Replays commits onto a new base | Yes, all replayed commits |
| git cherry-pick | Copies a commit onto another branch | No, creates a new commit |
| git revert | Adds a commit that undoes an earlier one | No |
Commits, branches and merges
Commits form a directed acyclic graph. A normal commit has one parent, the first commit in a repository has none, and a merge commit has two or more. A Git branch is nothing more than a movable pointer to one commit, and a tag is a pointer that normally does not move. Deleting a branch therefore does not delete commits immediately; unreachable commits remain recoverable through the reflog until garbage collection removes them, by default after about 30 to 90 days.
When two branches change the same lines differently, Git cannot build the merge snapshot automatically and stops with a merge conflict. The developer resolves the files and then records the result as the merge commit. On hosting platforms such as GitHub and GitLab, a pull request is a proposal to bring a set of commits into another branch after code review.
Writing good commits
A good commit is atomic: it contains one logical change that builds and passes tests on its own. Atomic commits make git bisect, which binary-searches history for the commit that introduced a bug, far more effective, and they allow a single change to be reverted cleanly without collateral damage.
The commit message is documentation that travels with the code. The widely followed convention is a subject line of about 50 characters in the imperative mood, such as “Fix rounding in invoice totals”, a blank line, and a body wrapped at about 72 characters that explains why the change was needed, since the diff already shows what changed. Some teams adopt Conventional Commits, with prefixes such as feat: and fix:, so that changelogs and version numbers can be generated automatically.
| Practice | Benefit | Risk if ignored |
|---|---|---|
| One logical change per commit | Easy review, bisect and revert | Unrelated changes are tangled |
| Message explains the reason | Future readers understand intent | Context is lost when authors leave |
| Each commit builds and passes tests | Reliable bisect and rollback | Broken intermediate states |
| Signed commits | Verifiable authorship | Commits can be forged by name |
| No secrets or large binaries | Small, safe repository | Leaks persist in every clone |
Common mistakes and how to recover
- Rewriting shared history: amending or rebasing commits that others have already pulled forces them to reconcile diverged histories; use
git reverton shared branches instead. - Committing a secret: removing it in a later commit does not help, because the earlier snapshot still contains it; the credential must be rotated immediately, and history rewriting only reduces further exposure.
- Committing to the wrong branch: create a branch at the current commit, then reset the original branch; nothing is lost while a reference points to the commits.
- Huge mixed commits: split them with an interactive rebase before sharing, or with partial staging before committing.
A detail that often surprises developers is that the author email is not verified by Git itself. Anyone can set any name and email in their configuration, which is why projects with strict provenance requirements use GPG or SSH commit signing and reject unsigned commits on protected branches.
The simple explanation
A Git commit is like a dated photograph of an entire project folder. It comes with a short note that says what changed and why. Each photograph also remembers the one taken before it. Together they form a complete album of the project’s history.
Suppose something breaks later. The team can flip back through the album and find the exact photograph where the problem appeared. They can then return to any earlier state without losing anything.
How it is used in practice
A developer on a web shop team is asked to add a discount code field to checkout. While working, she also notices a typo in an unrelated error message and a missing test for tax rounding. Instead of one large commit, she stages and commits the work as three separate commits: “Fix typo in payment error message”, “Add test for tax rounding on discounted items” and “Add discount code field to checkout”, each with a short body explaining the reason.
Two weeks after release, totals are wrong for some orders. A colleague runs git bisect between the last good release tag and the current branch; after about seven test runs across roughly a hundred commits, Git points to the discount commit. Because that commit contains only the discount feature, the team reverts it with a single git revert, ships the fix the same day, and later reintroduces the corrected feature in a new commit.