Skip to content
academia.sh

Lesson 11 / 15

Amending a Commit

Fixing the last commit's message or content; producing a new object because a commit is immutable, the reflog, and the limit of shared history.

Contents

The previous lesson undid changes that had not yet been committed. This lesson goes one step further: it takes up what to do if a commit that has already been written is missing something or has a mistake. The two most common cases are a mistyped message and a file that was forgotten from the commit.

Amending Does Not Actually Change Anything

A commit object is immutable. Its ID is the hash of its content; if the content changes, the ID changes too, and a different object results. This is why the operation called “amending a commit” does not edit the existing object: it creates a new commit attached to the same parent, and moves the branch tip to that new commit. The old commit stays in the object database but becomes unreachable from any ref.

The distinction matters in practice: fixing the past is rewriting the past.

Fixing the Message

A usage section is added to README.md and committed; but the message has two typos:

git commit -m "READMEye kullanm bölümü ekle"
[main c190fa9] READMEye kullanm bölümü ekle
 1 file changed, 4 insertions(+)
git commit --amend -m "README'ye kullanım bölümü ekle"
[main d847955] README'ye kullanım bölümü ekle
 Date: Fri Mar 8 11:30:00 2024 +0300
 1 file changed, 4 insertions(+)

The ID went from c190fa9 to d847955. Not a single byte of the content changed; the only thing that changed was the message, and the message enters the ID computation too.

The Date: line in the output is printed to report that the commit’s author date was preserved.

That the only thing that changed is really the message can be tested by comparing tree IDs:

git log -1 --format='%h %T' c190fa9
git log -1 --format='%h %T' d847955
c190fa9 4e359f7361da5c8654dce5682f26e5a79e43f5ca
d847955 4e359f7361da5c8654dce5682f26e5a79e43f5ca

The two commits point at the same tree. The project content is byte-identical, the commit IDs are different: this is direct proof that the ID covers metadata as well as content. The only thing newly written to the object database is the second commit object; the blob and tree objects are shared.

Author and Committer

The reason the commit object’s two date fields are kept separate shows up here:

git log -1 --format='author   : %an %ad%ncommitter: %cn %cd' --date=iso
author   : Deniz Kaya 2024-03-08 11:30:00 +0300
committer: Deniz Kaya 2024-03-08 11:33:00 +0300

The author is the person who wrote the change, and the time that change was first created. The committer is the person who put the commit into the object database, and the time of that recording. Amending preserves the author field and updates the committer field. Keeping the two fields separate ensures the source is not lost in cases where a contribution is placed into history by someone else.

Adding a Forgotten Change

README.md had been committed without the line describing the exit code. The missing line is added, staged, and folded into the commit while keeping the message:

git add README.md
git commit --amend --no-edit
[main 18600c5] README'ye kullanım bölümü ekle
 Date: Fri Mar 8 11:30:00 2024 +0300
 1 file changed, 6 insertions(+)

--no-edit uses the existing message without opening the editor. The line count went from four to six: the staging area’s content formed the new commit’s tree.

History shows a single commit:

git log --oneline -n 3
18600c5 README'ye kullanım bölümü ekle
bfe57d3 Öbek ve dizi terimlerini ekle
cb8f7ee Yerel notları izlemeden çıkar

Three separate commit objects were written — c190fa9, d847955, 18600c5 — but only the last one stands in history. The first two became unreachable.

The Scope of Amending

--amend builds the new commit’s tree from the staging area’s current state. This is as much a trap as a convenience: if the staging area holds files staged for other work, they enter the amended commit too, and end up recorded in the wrong place in history.

When only the message needs fixing, the staging area needs to match the last commit. The check is a single command:

git status --short

The output’s first column should carry no mark. If it does, those entries will enter the scope of the amend.

The same mechanism applies to the author field. The --author option sets the new commit’s author; a commit written while the configured identity was wrong is fixed this way. The committer field is still taken from the configuration, so the two fields diverge — this is the expected result of keeping the fields separate.

What Happened to the Old Commits

Being unreachable is not the same as being gone:

git cat-file -t c190fa9
commit

The object is in place. The way to reach it is the reflog: a local log that records what values HEAD and branch tips took on in the past.

git reflog -n 3
18600c5 HEAD@{0}: commit (amend): README'ye kullanım bölümü ekle
d847955 HEAD@{1}: commit (amend): README'ye kullanım bölümü ekle
c190fa9 HEAD@{2}: commit: READMEye kullanm bölümü ekle

The HEAD@{n} notation names the value of the HEAD ref n steps ago. The log is local and is not shared; an amend made in one copy of the repository does not show up in another copy’s log.

Unreachable objects do not stay forever. Garbage collection, which runs during repository maintenance, deletes objects unreachable from any ref and from the reflog. The default retention period is long enough; but it is not correct to work under the assumption that “an unreachable object is permanent.” The recovery window is limited.

Bringing Back a Lost Commit

The reflog is not just a record; it is a recovery tool. The way to make a commit that was amended by mistake reachable again is to give it a name:

git branch recovery HEAD@{2}
git log --oneline -n 2 recovery
c190fa9 READMEye kullanm bölümü ekle
bfe57d3 Öbek ve dizi terimlerini ekle

The command runs silently and creates a ref named recovery. Because the object is now reachable from a name, it falls outside the scope of garbage collection; once the content has been inspected and whatever was needed has been taken, this name can be deleted.

The same thing can be done with an ID: c190fa9 from the reflog can be written directly. The HEAD@{n} notation is preferred, because which step to go back to is read from the sequence, not the ID.

The general rule here is this: a single name that can reach an object is enough to protect it. Operations like amending and the resetting seen in the next lesson do not delete objects; they only move the names pointing to them.

The Limit of Amending

Amending applies only to the last commit, and is safe only on unshared commits.

The reason for the second condition is that the ID covers the chain of parents. When a commit is amended, its ID changes; every commit after it is then forced to change its parent field too, and their IDs change. History, from the amended point on, ends up made entirely of new objects.

A copy that already has this history keeps holding on to its own old chain. The result is two separate chains, in two copies, that contain the same changes but carry different IDs; when someone tries to merge them, every commit appears twice. In a repository worked on collaboratively, this is a situation to avoid.

The rule is simple: if a commit has been shared, it is not amended. Instead, a new commit is written that reverses its effect — reverting, the subject of the next lesson.

Amending is also a narrow tool because it is limited to the last commit alone. When an older commit needs fixing, history has to be rewritten from that point on; these operations will be covered in the Advanced Git course.

Summary

  • A commit object is immutable; amending creates a new commit attached to the same parent and moves the branch tip to it.
  • Because the ID covers the message too, fixing only the message still produces a new ID.
  • The author field is preserved, the committer field is updated; keeping the two fields separate preserves the contribution’s source.
  • Old commits become unreachable but are not deleted; the reflog makes it possible to reach them. Garbage collection deletes unreachable objects after a certain period.
  • Amending applies only to the last commit, and only to unshared history.

Next Step

Amending targets the last commit and moves the branch tip to a new object. It is also possible to move the branch tip further back — that is, to drop more than one commit from history — and this operation has three different modes. The next lesson shows, one by one, what these modes do to the working directory and the staging area.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close