Skip to content
academia.sh

Lesson 12 / 15

Reset Modes

Moving the branch tip backward; the effect of the `--soft`, `--mixed`, and `--hard` modes on the three regions, lost content, and recovery through the reflog.

Contents

Amending moved the branch tip to a new commit attached to the same parent. Reset moves the branch tip further back in history: one or more commits drop out of the branch’s reachable past. Reset has three modes, and the difference among them is which regions are affected.

This lesson applies all three modes, one by one, to the same mistake.

The Mistake

The term list is meant to be sorted alphabetically and is handed to a general-purpose sorting tool. The result:

ağaç | tree
bağlı liste | linked list
dizi | array
karma tablosu | hash table
kuyruk | queue
küme | set
yığıt | stack
çizge | graph
öbek | heap

The order is wrong. “çizge” and “öbek” have dropped to the end of the list, and “küme” comes after “kuyruk.” The reason is that the sort is done by byte value, not by the Turkish alphabet: as seen in the How Computers Work course, letters outside the ASCII range are encoded in UTF-8 with more than one byte, and their first byte is greater than every ASCII letter. The correct order requires a locale-aware comparison scheme.

Without the mistake being noticed, it is committed:

git commit -m "Terim listesini abecesel sırala"
[main d85a348] Terim listesini abecesel sırala
 1 file changed, 5 insertions(+), 5 deletions(-)

What Reset Does

Reset consists of three steps, and the mode determines how many of them get applied:

  1. Move the branch tip. Done in every mode.
  2. Make the staging area match the target. Done in every mode except --soft.
  3. Make the working directory match the target. Done only with --hard.

The first step touches history; the second and third steps touch content.

--soft

git reset --soft HEAD~1
git log --oneline -n 1
18600c5 README'ye kullanım bölümü ekle

The commit dropped out of history. What happened to the content?

git status --short
M  terimler.txt

The change is standing in the staging area. Only the first step was applied: the branch tip moved back, the staging area and the working directory were left as they were. The result is the state of “the commit was never written, but the content is ready to be committed.”

git diff --staged --stat
 terimler.txt | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

This mode is used to rearrange the scope of the last commit: the commit is undone, the content is split the way it is wanted, and it is recommitted.

--mixed

The commit is rewritten, and this time reset with the default mode:

git reset HEAD~1
Unstaged changes after reset:
M	terimler.txt
git status --short
 M terimler.txt

The mark moved to the second column: the content is no longer in the staging area, only in the working directory. The first and second steps were applied. This is the default mode even when --mixed is not written.

This mode undoes both the commit and the selection made in the staging area; it is used when the decision of which files to commit together needs to be made from scratch.

--hard

git reset --hard HEAD~1
HEAD is now at 18600c5 README'ye kullanım bölümü ekle
git status --short

The output is empty. The term list has also returned to its old state: the sort has been completely undone. All three steps were applied.

This mode is destructive and takes two separate pieces of content down with it: the content of the reset commits, and every uncommitted edit that was in the working directory at the time. The first can be brought back from the reflog; the second cannot. Looking at git status’s output before running git reset --hard is the only way to avoid this second loss.

Comparing the Three Modes

Mode Branch tip Staging area Working directory Where the change stays
--soft Moved Untouched Untouched In the staging area
--mixed Moved Matched Untouched In the working directory
--hard Moved Matched Matched Nowhere

The table reduces to a single sentence: the harsher the mode, the deeper the undo reaches. The choice is made based on whether the commit’s content should be preserved.

Recovery

A reset commit stays in the object database and can be found through the reflog:

git reflog -n 6
18600c5 HEAD@{0}: reset: moving to HEAD~1
d85a348 HEAD@{1}: commit: Terim listesini abecesel sırala
18600c5 HEAD@{2}: reset: moving to HEAD~1
d85a348 HEAD@{3}: commit: Terim listesini abecesel sırala
18600c5 HEAD@{4}: reset: moving to HEAD~1
d85a348 HEAD@{5}: commit: Terim listesini abecesel sırala

The log has recorded the order the three modes were tried in exactly as it happened. It is not a coincidence that the commit ID comes out as d85a348 every time: as long as the content, message, author, and date are the same, the ID is the same too. This is a direct consequence of content-addressed storage.

The branch tip is moved back to the desired ID:

git reset --hard d85a348
HEAD is now at d85a348 Terim listesini abecesel sırala

The commit is back in history. In this example the sort really is wrong, so the final decision is to drop it:

git reset --hard HEAD~1
HEAD is now at 18600c5 README'ye kullanım bölümü ekle

The Form Given a Path

When git reset is given a path, its behavior changes entirely: the branch tip does not move, only that path’s record in the staging area is returned to its state at the target.

git add terimler.txt
git status --short
M  terimler.txt
git reset terimler.txt
Unstaged changes after reset:
M	terimler.txt
git status --short
 M terimler.txt

This is the older form of what was done with git restore --staged in the previous lesson. The same command name doing different things depending on whether it is given a path explains why git restore was named separately.

ORIG_HEAD

Before moving the branch tip, reset writes the old value to a file named ORIG_HEAD:

cat .git/ORIG_HEAD
2aff8c57391d20c90c05be4eb3f9a385fd140cf2

This name can be used like a ref; the shortest way to undo a reset done by mistake is to go back to the ORIG_HEAD target without reading the reflog:

git reset --hard ORIG_HEAD

The file records only the last operation; it is overwritten on successive resets. Going further back requires the reflog.

The Difference Between Reset and Restoring a File

The two commands are confused because both are called “undoing.” The distinction is clear:

  • git restore operates on a path; it does not touch history.
  • git reset operates on a ref; it moves the branch tip.

If you want to return a file to an old state, use git restore; if you want to drop a commit from history, use git reset.

Reset itself is also problematic on shared history: moving the branch tip back creates incompatibility with copies that already have those commits. The previous lesson’s rule holds here too — shared history is not rewound.

Summary

  • Reset moves the branch tip; the mode determines whether the staging area and the working directory are also matched to it.
  • --soft leaves the content in the staging area, --mixed in the working directory; --hard matches both to the target.
  • --hard deletes uncommitted edits irrecoverably.
  • Reset commits stay in the object database; they can be found and brought back through the reflog.
  • The same content, message, author, and date always produce the same commit ID.

Next Step

Reset and amending both work by rewriting history, and this is why they cannot be used on shared commits. So what happens if a shared commit is wrong? The next lesson takes up the tool that reverses a commit’s effect without disturbing history — reverting.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close