Skip to content
academia.sh

Lesson 13 / 15

Reverting

A new commit that reverses another's effect; undoing without disturbing history, verifying with the tree ID, reverting without committing, and reverting a revert.

Contents

Reset and amending both work by rewriting history; this is why they cannot be used on shared commits. If a shared commit is wrong, only one path remains: leave the past as it is, and write a new commit that reverses the mistake’s effect. This operation is called reverting.

The Mistake

The search script’s output is meant to be limited to just the Turkish column, and grep’s output is passed through a filter:

if ! grep -i -- "$1" terimler.txt | cut -d '|' -f 1; then
    echo "eşleşme yok: $1" >&2
    exit 2
fi
git commit -m "Arama çıktısını yalnızca Türkçe sütuna indir"
[main ad60f31] Arama çıktısını yalnızca Türkçe sütuna indir
 1 file changed, 1 insertion(+), 1 deletion(-)

The change produces the intended output but carries a hidden flaw: the shell takes the pipeline’s exit code from the last command. Because cut always finishes successfully, the condition comes out false even when grep finds no match:

./ara.sh yokboyle
echo "exit code: $?"
exit code: 0

The script printed no no-match notice and exited with code zero; yet there is no match, and the exit code should be 2. The flaw is not in the output itself but in the error reporting; this is why it is prone to going unnoticed.

The commit has been shared, and it cannot be dropped from history.

Reverting

git revert --no-edit ad60f31
[main a58791f] Revert "Arama çıktısını yalnızca Türkçe sütuna indir"
 Date: Mon Mar 11 10:12:00 2024 +0300
 1 file changed, 1 insertion(+), 1 deletion(-)

--no-edit accepts the prepared message without opening the editor. The script works correctly again:

./ara.sh yokboyle
eşleşme yok: yokboyle
git log --oneline -n 4
a58791f Revert "Arama çıktısını yalnızca Türkçe sütuna indir"
ad60f31 Arama çıktısını yalnızca Türkçe sütuna indir
18600c5 README'ye kullanım bölümü ekle
bfe57d3 Öbek ve dizi terimlerini ekle

The faulty commit stands in history. Alongside it, a commit that reverses its effect has been added.

The Content of a Revert Commit

git show HEAD
commit a58791f8f8b5325563397d592015cb2009b5e567
Author: Deniz Kaya <[email protected]>
Date:   Mon Mar 11 10:12:00 2024 +0300

    Revert "Arama çıktısını yalnızca Türkçe sütuna indir"

    This reverts commit ad60f311bd296f370a18d98a40173d878160d809.

diff --git a/ara.sh b/ara.sh
index 64b5b70..582ddde 100755
--- a/ara.sh
+++ b/ara.sh
@@ -6,7 +6,7 @@ if [ $# -ne 1 ]; then
     exit 1
 fi
 
-if ! grep -i -- "$1" terimler.txt | cut -d '|' -f 1; then
+if ! grep -i -- "$1" terimler.txt; then
     echo "eşleşme yok: $1" >&2
     exit 2
 fi

The diff is the reverse of the reverted commit’s diff: the deleted line has been added, the added line has been deleted. The automatically generated message writes the reverted commit’s full ID into the body; thanks to this line, what a revert undoes can be read straight from history.

Verifying the Result

Whether the revert really returned things to the previous state can be tested with certainty by comparing tree IDs:

git log --format='%h %T %s' -n 4
a58791f ac0eb40463f387ce1cb40623aa9c16a9a7ed229f Revert "Arama çıktısını yalnızca Türkçe sütuna indir"
ad60f31 9ab09543909d738602a2a947e858b1457972ef28 Arama çıktısını yalnızca Türkçe sütuna indir
18600c5 ac0eb40463f387ce1cb40623aa9c16a9a7ed229f README'ye kullanım bölümü ekle
bfe57d3 c6ec3cea3b8943f818cd8bb423c65f0cb5c553ea Öbek ve dizi terimlerini ekle

The revert commit’s tree, ac0eb40, is the same as the tree of 18600c5, the commit before the faulty one. Two different commits point at the same tree: the project content has returned to its old state, byte for byte. This equality check, which content-addressed storage provides, removes the need to compare diffs by eye.

Why Reverting Does Not Disturb History

Reverting only adds: it touches no existing object, it changes no ID. Copies that already have this history keep the chain they hold and take on just one new commit on top. The incompatibility that arises with reset does not occur here.

The cost is that the mistake stays visible in history. This is not a flaw; it is, most of the time, the desired behavior: when a faulty change went in, and when and why it was undone, ends up on record.

Reverting Without Committing

The result of a revert can be inspected without committing it directly:

git revert -n bfe57d3
git status --short
M  terimler.txt
git diff --staged
diff --git a/terimler.txt b/terimler.txt
index a5350ef..17af3b8 100644
--- a/terimler.txt
+++ b/terimler.txt
@@ -5,5 +5,3 @@ ağaç | tree
 karma tablosu | hash table
 bağlı liste | linked list
 küme | set
-öbek | heap
-dizi | array

The -n option puts the change in the staging area but does not commit it. This is used to combine multiple commits into a single revert commit, or to inspect the result before committing it. The operation can be canceled:

git revert --abort

The command returns the working directory and the staging area to their state before the revert began.

In the example, the reverted commit was not history’s most recent one. A revert can be applied to any commit; what is computed is that commit’s diff, reversed, applied to the current content. If the commits in between touched the same lines, the application fails and a conflict is reported. Resolving conflicts is the subject of the Branching and Collaboration course.

Reverting a Revert

A revert is a commit too; it can be reverted. This is the way to reapply a change that was undone:

git revert --no-edit a58791f
[main 464855e] Reapply "Arama çıktısını yalnızca Türkçe sütuna indir"
 Date: Mon Mar 11 15:00:00 2024 +0300
 1 file changed, 1 insertion(+), 1 deletion(-)

This time the message starts with Reapply, not Revert: reverting a revert is, in essence, reapplying.

git log --format='%h %T %s' -n 4
464855e 9ab09543909d738602a2a947e858b1457972ef28 Reapply "Arama çıktısını yalnızca Türkçe sütuna indir"
a58791f ac0eb40463f387ce1cb40623aa9c16a9a7ed229f Revert "Arama çıktısını yalnızca Türkçe sütuna indir"
ad60f31 9ab09543909d738602a2a947e858b1457972ef28 Arama çıktısını yalnızca Türkçe sütuna indir
18600c5 ac0eb40463f387ce1cb40623aa9c16a9a7ed229f README'ye kullanım bölümü ekle

Four commits alternate back and forth between two trees. This last step was not kept in the example repository’s history — the flawed change was not reapplied; the revert was left standing.

Reverting or Resetting

Criterion Reverting Resetting
History Preserved, a new commit is added Rewritten
On a shared commit Suitable Not suitable
Visibility of the mistake Stays on record Erased
Which commit it applies to Any commit Backward from the branch tip

There is a single criterion: if the commit has been shared, revert; if not, either can be used.

Summary

  • Reverting creates a new commit that applies the reverse of another commit’s diff.
  • History is preserved; no object changes, only a new commit is added.
  • A revert commit’s tree is the same as the tree before the reverted commit; this equality is a verification tool.
  • The -n option puts the result in the staging area without committing it; --abort cancels the operation.
  • A revert can be reverted too; the result is the change being reapplied.

Next Step

The tools covered so far were about completed changes. So what happens when there is unfinished work and it becomes necessary to switch to something else? Committing is premature, and undoing is a loss. The next lesson takes up the stash, which sets unfinished work aside temporarily, and what that storage corresponds to in the object model.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close