Git reset

This has such a wide range of responsibilites from get rid of what is in the worktree or the index to walking back a commit


















Soft

Git reset soft can be very useful if you need to make a commit that is partially finished and you want to edit that commit and change the contents

➜  hello-git git:(trunk) git log -p --oneline -1
7488b35 (HEAD -> trunk) Revert "E"
diff --git a/README.md b/README.md
index eb42c13..38dc2c1 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
 A + 10
 D
-E
 remote-change
 downstream change

Now lets say we need to continue to edit our previous commit. We have two options.

  1. we could make changes and use commit --amend. If you are not familiar with git commit --amend it allows you to meld the current staged changes into the previous commit and edit the message.
  2. we could use git reset --soft HEAD~1 to move trunk back one commit and alter the index and worktree to match the contents of the commit.


Note

Both operations will change the sha since we are changing the graph fundamentally


















Problem

Use git reset --soft HEAD~1 to move trunk back one commit. This should make the working state of your branch contain the changes of the revert instead of the revert commit being in the graph



inspect state of your git branch via git log and git status



git commit back the reverted change with a new message


















Solution

➜  hello-git git:(trunk) git reset --soft HEAD~1
➜  hello-git git:(trunk) git log -p --oneline -1
d53a122 (HEAD -> trunk) A + 10
diff --git a/README.md b/README.md
index 68dae75..eb42c13 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-A + 7
+A + 10
 D
 E
 remote-change

Notice log -p shows us that the current commit (-1) is not the revert. We have successfully moved back trunk!

now check out the git status!

➜  hello-git git:(trunk) git status
On branch trunk
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   README.md

Its the revert we just got done doing!

➜  hello-git git:(trunk) git diff --staged
diff --git a/README.md b/README.md
index eb42c13..38dc2c1 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
 A + 10
 D
-E
 remote-change
 downstream change

commit the soft reset contents

➜  hello-git git:(trunk) git commit -m 'its not just a revert anymore'
[trunk ce41293] its not just a revert anymore
 1 file changed, 1 deletion(-)
➜  hello-git git:(trunk) git log --oneline -2
ce41293 (HEAD -> trunk) its not just a revert anymore
d53a122 A + 10

















NOTE

Obviously this is dangerous as we have just altered history of a branch

The basics of soft is to reset the history to the point you want (HEAD~1) and the index and worktree will contain the changes whence you came (Our revert in this example). We could have used soft reset and went back several commits and we would have all their changes.


















Hard

Hard will do the same thing as soft except it will drop changes to the index and worktree. That means any work that is being tracked by git will be destroyed


















Problem

Before we try walking back a commit, lets first create a local change and see what happens when we execute git reset --hard

Add a small change to README.md and create a new file, foo.md, with any change you see fit, then execute git reset --hard which will destroy index and worktree changes


















Solution

➜  hello-git git:(trunk) echo "HELLO" >> README.md
➜  hello-git git:(trunk) echo "Foo" > foo.md
➜  hello-git git:(trunk) git status
On branch trunk
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        foo.md

no changes added to commit (use "git add" and/or "git commit -a")

Note

  • foo.md is not tracked by git
  • README.md is tracked and the changes are present in the worktree
➜  hello-git git:(trunk) git reset --hard
HEAD is now at ce41293 its not just a revert anymore
➜  hello-git git:(trunk) git status
On branch trunk
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        foo.md

nothing added to commit but untracked files present (use "git add" to track)

Notice that it destroyed all work to README.md. That is because --hard will reset ALL work in the worktree and index (staging area). So even if we had git add README.md it would still have been reset back to the HEAD state.

foo.md did not get destroyed because git is not tracking the file in any way.


















Problem

Destroy foo by using git add and git reset --hard


















Solution

➜  hello-git git:(trunk) git add .
➜  hello-git git:(trunk) git reset --hard
HEAD is now at ce41293 its not just a revert anymore
➜  hello-git git:(trunk) git status
On branch trunk
nothing to commit, working tree clean

Notice that now we destroyed our work with foo.md. That is because we started to track foo.md by adding to the index (staging area). Now when we called git reset --hard it destroyed that work.

NOTE

CAUTION WITH RESET HARD

It can destroy your work and it can be virtually impossible to get back that work.


















Git reset --hard with HEAD~1

We can walk back our tree just like soft, except we discard the changes


















Problem

Try it now. Try walking back our commit with git reset --hard HEAD~1


















Solution

➜  hello-git git:(trunk) git reset --hard HEAD~1
HEAD is now at d53a122 A + 10
➜  hello-git git:(trunk) git status
On branch trunk
nothing to commit, working tree clean

Not only did we reset trunk one step back, we also discarded all changes.


















Challenge Question!

Can you restore hello-git to the position it was BEFORE we started this section?


















Solution

➜  hello-git git:(trunk) git reflog
... 14 other entries ... # i did extra work here
7488b35 (tag: git-reset-start) HEAD@{15}: commit: Revert "E"

So we can see our previous commit with Revert "E". Lets move trunk back to there.

➜  hello-git git:(trunk) git checkout 7488b35
Note: switching to '7488b35'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 7488b35 Revert "E"
➜  hello-git git:(7488b35) git branch -D trunk
Deleted branch trunk (was d53a122).
➜  hello-git git:(7488b35) git checkout -b trunk
Switched to a new branch 'trunk'
➜  hello-git git:(trunk)

Look at that. We have checked out that point in time, deleted our current version of trunk, and then checked out a new version of trunk.


















Note

There are more things you can do with git reset but i find i use restore and git reset --hard only. Git reset --hard to restore me back to HEAD cleanly and restore to remove a staged file.