DEV Community

Kenichiro Nakamura
Kenichiro Nakamura

Posted on

git deep dive part 4: Use reset to control branch and three trees

In the previous article, I explain the concept of three trees and some commands to handle files in working trees and index. In this article, I use git reset to control branch.

Reset to specified commit

I already explained how I can use git checkout to move the HEAD to different commit. But it didn't update branch file in .git. This means the branch still points to the latest commit, while I am in the different commit.

git reset updates branch instead of HEAD so that I actually undo the commit entirely.

Add another commit for test

I was in a middle of modifying hello.txt in the previous article.

gitDeepDive> git log --oneline --graph * 367c2d0 (HEAD -> dev) Update news * 2adbcac (test, master) Add doc folder and files * 16f1fa8 commit hello.txt gitDeepDive> git status On branch dev Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: hello.txt 
Enter fullscreen mode Exit fullscreen mode

Let's commit the change now.

gitDeepDive> git commit -m "update hello.txt" [dev 867d90c] update hello.txt 1 file changed, 2 insertions(+) gitDeepDive> git log --oneline --graph * 867d90c (HEAD -> dev) update hello.txt * 367c2d0 Update news * 2adbcac (test, master) Add doc folder and files * 16f1fa8 commit hello.txt 
Enter fullscreen mode Exit fullscreen mode

Then, I modify the file again.

echo "The fifth line" >> hello.txt git add hello.txt echo "The sixth line" >> hello.txt 
Enter fullscreen mode Exit fullscreen mode

Now I have different version of hello.txt in HEAD, index and working tree.

gitDeepDive> git status On branch dev Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: hello.txt 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: hello.txt gitDeepDive> git ls-files --stage 100644 44f41854d770f2a38d368936b14975d280cbd950 0 docs/article.txt 100644 2b366bf2f2784dbf26fcd56e1cedb3afc1345753 0 docs/news.txt 100644 95edf347f35cce2273a54c0084f54923d6e7ad1d 0 hello.txt 
Enter fullscreen mode Exit fullscreen mode

Alt Text

Soft reset

git reset has three pattern. Soft, Mix and Hard. Let's start from soft. HEAD^ means previous commit. This is easy shortcut as I don't have to check commit id. If I want to go back two commits, then simply add one more ^ like HEAD^^.

git reset --soft HEAD^ 
Enter fullscreen mode Exit fullscreen mode

Let's see how git handles the command. The HEAD points dev branch, and dev branch points to 367c2d0 which is the previous commit, but git didn't modify index and working tree.

gitDeepDive> git log --oneline 367c2d0 (HEAD -> dev) Update news 2adbcac (test, master) Add doc folder and files 16f1fa8 commit hello.txt gitDeepDive> git status On branch dev Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: hello.txt 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: hello.txt gitDeepDive> git ls-files --stage 100644 44f41854d770f2a38d368936b14975d280cbd950 0 docs/article.txt 100644 2b366bf2f2784dbf26fcd56e1cedb3afc1345753 0 docs/news.txt 100644 95edf347f35cce2273a54c0084f54923d6e7ad1d 0 hello.txt 
Enter fullscreen mode Exit fullscreen mode

The gray color boxes are the ones changing from the previous image.
Alt Text

Mixed reset

Let's try mixed option now. As I already in commit 367c2d0, I simply reset to current HEAD but use --mixed option.

gitDeepDive> git reset --mixed HEAD Unstaged changes after reset: M hello.txt 
Enter fullscreen mode Exit fullscreen mode

See the current status. I can see that index has been reset but not working tree.

gitDeepDive> git log --oneline 367c2d0 (HEAD -> dev) Update news 2adbcac (test, master) Add doc folder and files 16f1fa8 commit hello.txt gitDeepDive> git ls-files --stage 100644 44f41854d770f2a38d368936b14975d280cbd950 0 docs/article.txt 100644 2b366bf2f2784dbf26fcd56e1cedb3afc1345753 0 docs/news.txt 100644 20fe8be9820a49252e2a4dd37a60e678cd5cda14 0 hello.txt gitDeepDive> git status On branch dev 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: hello.txt no changes added to commit (use "git add" and/or "git commit -a") gitDeepDive> 
Enter fullscreen mode Exit fullscreen mode

In the diagram, only the difference is that pointer to hello.txt in index is "reset".
Alt Text

Hard reset

Finally, try hard reset.

gitDeepDive> git reset --hard HEAD HEAD is now at 367c2d0 Update news 
Enter fullscreen mode Exit fullscreen mode

git now reset working tree files as well.

gitDeepDive> git log --oneline 367c2d0 (HEAD -> dev) Update news 2adbcac (test, master) Add doc folder and files 16f1fa8 commit hello.txt gitDeepDive> git ls-files --stage 100644 44f41854d770f2a38d368936b14975d280cbd950 0 docs/article.txt 100644 2b366bf2f2784dbf26fcd56e1cedb3afc1345753 0 docs/news.txt 100644 20fe8be9820a49252e2a4dd37a60e678cd5cda14 0 hello.txt gitDeepDive> git status On branch dev nothing to commit, working tree clean 
Enter fullscreen mode Exit fullscreen mode

Alt Text

As we all know already, we can restore to any point of time as long as git snapshot contents. I can go back to commit 867d90c easily. However, I lose the change in working tree, as git doesn't snapshot the contents.

gitDeepDive> git reset --hard 867d90c HEAD is now at 867d90c update hello.txt gitDeepDive> git log --oneline 867d90c (HEAD -> dev) update hello.txt 367c2d0 Update news 2adbcac (test, master) Add doc folder and files 16f1fa8 commit hello.txt gitDeepDive> git ls-files --stage 100644 44f41854d770f2a38d368936b14975d280cbd950 0 docs/article.txt 100644 2b366bf2f2784dbf26fcd56e1cedb3afc1345753 0 docs/news.txt 100644 7a218e826670e77d05c1c244b514a7f449056752 0 hello.txt gitDeepDive> git status On branch dev nothing to commit, working tree clean 
Enter fullscreen mode Exit fullscreen mode

Delete branch

What happens if I delete branch? Let's try. I need to move out of the dev branch to delete it.

gitDeepDive> git switch master Switched to branch 'master' gitDeepDive> git branch -D dev Deleted branch dev (was 867d90c). gitDeepDive> git branch -a * master test 
Enter fullscreen mode Exit fullscreen mode

When I looked into .git folder, I don't see any branch info in refs/heads as I did git gc. Instead I see them in packed-refs both point to the same commit.

gitDeepDive> cat .git\packed-refs # pack-refs with: peeled fully-peeled sorted 2adbcacc0047a991956dedb4b16691ba244674b3 refs/heads/master 2adbcacc0047a991956dedb4b16691ba244674b3 refs/heads/test 
Enter fullscreen mode Exit fullscreen mode

Now dev branch is gone, but the commit still exists.

gitDeepDive> git cat-file commit 867d90c tree edbfe822158fb3be005aca22897a0395fa2c9252 parent 367c2d000be0ffbb640252384c820ce472fe32a4 author Kenichiro Nakamura <kenakamu@microsoft.com> 1588842488 +0900 committer Kenichiro Nakamura <kenakamu@microsoft.com> 1588842488 +0900 update hello.txt 
Enter fullscreen mode Exit fullscreen mode

I can checkout to the commit. I see "detached HEAD" again but I know what it means so not scary at all.

gitDeepDive> git checkout 867d90c Note: switching to '867d90c'. 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 867d90c update hello.txt 
Enter fullscreen mode Exit fullscreen mode

I can create new dev branch from here.

gitDeepDive> git branch -a * (HEAD detached at 867d90c) master test gitDeepDive> git switch -c dev Switched to a new branch 'dev' gitDeepDive> git branch -a * dev master test gitDeepDive> git log --oneline 867d90c (HEAD -> dev) update hello.txt 367c2d0 Update news 2adbcac (test, master) Add doc folder and files 16f1fa8 commit hello.txt 
Enter fullscreen mode Exit fullscreen mode

Summary

As long as git takes snapshot by either git add or git commit, there is a way to recover them, but I will lose my change if I delete working tree. I will explain how merge work in the next article.

Go to next article

Top comments (0)