git deep dive part 4: Use reset to control branch and three trees
Tags: git
Published: 2020-05-07 15:43:21

---
title: git deep dive part 4: Use reset to control branch and three trees
published: true
description: git deep dive
tags: git
---

In the [previous article](https://dev.to/kenakamu/git-deep-dive-part-3-head-index-and-working-tree-3oim), 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](https://git-scm.com/docs/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.

```shell
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
```
Let's commit the change now.

```shell
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
```
Then, I modify the file again.

```shell
echo "The fifth line" >> hello.txt
git add hello.txt
echo "The sixth line" >> hello.txt
```
Now I have different version of hello.txt in HEAD, index and working tree.

```shell
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
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/c2c6knb5cfq5o4uuwo8b.png)

# 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^^.

```shell
git reset --soft HEAD^
```
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.

```shell
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
```
The gray color boxes are the ones changing from the previous image.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sbbm0syr0uiheniflm3r.png)

# Mixed reset

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

```shell
gitDeepDive> git reset --mixed HEAD
Unstaged changes after reset:
M       hello.txt
```

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

```shell
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>
```
In the diagram, only the difference is that pointer to hello.txt in index is "reset".
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ce24guck5agmycp2j5lk.png)

# Hard reset

Finally, try hard reset.

```shell
gitDeepDive> git reset --hard HEAD
HEAD is now at 367c2d0 Update news
```
git now reset working tree files as well.

```shell
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
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/84paqjisbrnmg2sbk6of.png)

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.

```shell
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
```

# Delete branch

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

```shell
gitDeepDive> git switch master
Switched to branch 'master'
gitDeepDive> git branch -D dev
Deleted branch dev (was 867d90c).
gitDeepDive> git branch -a
* master
  test
```

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.

```shell
gitDeepDive> cat .git\packed-refs
# pack-refs with: peeled fully-peeled sorted
2adbcacc0047a991956dedb4b16691ba244674b3 refs/heads/master
2adbcacc0047a991956dedb4b16691ba244674b3 refs/heads/test
```
Now dev branch is gone, but the commit still exists.

```shell
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
```
I can checkout to the commit. I see "detached HEAD" again but I know what it means so not scary at all.

```shell
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
```
I can create new dev branch from here.

```shell
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
```
# 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](https://dev.to/kenakamu/git-deep-dive-part-5-merge-branches-3h6d)