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:
- Move the branch tip. Done in every mode.
- Make the staging area match the target. Done in every mode except
--soft. - 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 restoreoperates on a path; it does not touch history.git resetoperates 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.
--softleaves the content in the staging area,--mixedin the working directory;--hardmatches both to the target.--harddeletes 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.