The Four Areas Git Reset - aakash14goplani/FullStack GitHub Wiki

Topics Covered

Understanding Reset

  • Before understanding a reset, you have to understand a few other things about Git. You have to be familiar with the way branches work and the way they work in there, the index and the repository work. And if you are not familiar with that, then you will have a hard time understanding reset.

  • The other reason that reset feels confusing is that it has many use cases. That is, you can use it in different ways for very different reasons. So if you look out for ways to do things with git, you see your reset coming up again and again with slight variations, and the result look very different depending on how exactly you apply it, so you end up thinking it must be a very complex command.

  • Let's recollect those operations that can move a branch? The commands probably move a branch because they create new commits and they move the current branch at the same time as they create the new commit.

    • So commit is the obvious one. It creates a new commit and moves the current branch to point at the new commit.
    • Merge also creates a commit in most cases and it also moves the current branch to point at the new commit.
    • The rebase also does something similar. It creates new commits by copying existing commits, in this case, and it moves the current branch to point at one and do commits.
    • Also git pull gets new commits from a remote and updates below current or remote branches.

    And maybe you know a few more. All of these operations move branches, however, none of them is a specialized operation to move a branch. They all move branches implicitly as a side effect of creating new commits or pulling them from a remote.

  • You might wonder whether there is an operation that is more specialized, an operation that explicitly is all about moving a branch. Well, that's a reset.

  • The most important thing that a reset does, the first step in a reset so to say is just that, it moves a branch, generally, the current branch, the branch that head is pointed to.

  • You get a commit, say this commit here, and the reset moves the current branch to that commit. So that commit is now the current commit. Notice that reset doesn't move head. Head is still pointing to the same branch it was pointing at before, but the branch itself is moving, so head is following along for the ride. If you only look at the repository, then that's all that reset does. It moves a branch to point at the specific commit.

image 1

  • The part that you might find confusing, however, is not what reset does to the repository, it's the second step, what a reset does to the other two main areas, the working area and the index, and the reset does different things there depending on its options.

  • If you give it the git reset --hard option, then reset copies data from the new current commit to both the working area and the index.

image 2

  • With the git reset --mixed option, reset copies data from the new current commit to the index, but leaves the working area alone. This is the default option, so if you don't give any option to reset, that will be a mixed reset.

image 3

  • And finally, the git reset --soft option means don't touch any of the areas. Just move the branch and skip step two entirely.

image 4

  • So this is what reset does. First, it moves the current branch, so it also changes the current commit, and second, optionally, it copies the files and directories from the new current commit to the working area and the index.

A Reset Example

  • The use case is that I want to revert the whole project to the state it was in a previous commit. Here is the snapshot of the repository. Here is the current commit, the current branch master, which is pointing on the current commit and finally, here is the head reference pointing at the current branch.

image 5

  • I will modify menu.txt by adding a new dish to the menu. There, strawberry squids, nice. So look at the diagram. I just changed the menu file in the working area. I made it orange to show it changed, now let's stage it, and I just copied the updated file to the index. And now let's commit it. Look what happens in the repository when I commit. Git creates a new commit, it moves the master branch to point at this new commit, and the new commit contains the new version of the menu file and the same version of the recipe's directory.

image 6

  • Let's go through this entire process once more. In my cookbook project, whenever I add the new recipe, I'm also supposed to have a file in the Recipes folder that contains the ingredients and other details about the recipe, so let me add the file with a couple of ingredients. Now I'll just file this here in the working area. And I add it to the index and I commit it. And again, Git creates a new commit, updates the master branch, and the new commit to reference is the latest version of both menu.txt and the recipes directory.

image 7

  • Now I changed my mind. I don't want to have strawberry squids in my cookbook anymore. Unfortunately, by now I have not one, but two commits that reference strawberry squids. I regret those commits. I would like them to go away now. How can I do that? Well, one way I can do that is by using a reset.

  • I have a commit here, two commits ago, this one fbe535, it still contains the yellow versions of my files before I started doing the squids thing. I would like these to be the latest commit in master, as if the last two commits never happened.

  • That's where our reset comes useful. I can take note of these commits hash and that's git to reset the current branch to it. This will move the branch back in time so to speak. And what about the options to reset. Should this be a hard reset, a soft reset, or a mixed reset. Well, after the reset, I want to have the yellow versions of the files not just in the repository, but also in the index and the working area. I want to be aligned, I want to be clean again, so this must be a hard reset. Remember, --hard copies the files from the repository to the other two areas. Let's do this and see what happens.

$ git reset --hard fbe535
  • First, Git moves the current branch to the previous commit, which now becomes the new current commit and head follows along. Second, because this is a hard reset, Git copies the content of the new current commit, the yellow versions of the files, to both the working directory and the index. In here because the two squid-related commits are unreachable now, they have no branch pointing at them, they will eventually be garbage collected. So we're back to where we were before this whole strawberry squid fiasco all with a quick reset.

image 8

More Reset Examples

  • Suppose that I've been experimenting with menu.txt file, for example, I edit it and I add the new line for a BBQ recipe. I will stage this edit, git add menu.txt, there we are, and change file in the working area and this also staged in the index.

  • Now what if I change my mind right now and I want to clean the stage file. Maybe I want to commit something else first then get back to these changes later. In other words, I want to keep the changes in my working area, but I want to remove all changes from the index. In the index, I want the same version of the files that is in the repository. How do I do that?

  • Earlier on, we've seen one way to do that using the git rm --cached command. That works, but it's not the only way to do it. In fact, if you read Git's messages carefully, when I asked for the git status (after doing a git add), Git itself suggested a different command to our stage file. It suggested to using a reset. How exactly?

  • Well the idea is to have a head reset. This means that we're moving the current branch to the commit pointed at by head, but the current branch is already pointing at that commit by definition, so in this case, the reset does move the branch. If you wish, it moves it to the same place where it already is, so it doesn't move it. This is like skipping the first step in a reset altogether, the step where it moves the branch.

  • What happens after that. Remember the second step of reset. Git moves data from the repository to the working area in the index. In this case, we didn't specify the kind of reset we want, so git will go with the default, and the default is a mixed reset. Mixed reset moves data from the current commit to the index, but not the working area. And the result is, it unstages all the changes. You can also unstage a single file by naming that file in the head reset.

$ git reset menu.txt
  • Now what if I decide to throw it all away? I don't want to commit this change at all anymore. So for now, I just want to delete my changes and go back to the clean status. I can do that with the
$ git reset --hard HEAD
  • The hard head reset doesn't move the branch because it's still a head reset, but it copies data from the repository to both the index and the working area. It overrides everything there.

  • This is a popular command, but also a command that should be used with some care because it's a destructive command. In fact, it's one of the easiest way to lose data in Git. You are saying explicitly I don't care about all the stuff in my working area. Please just reset it to the current commit, and if I set it, everything in the working area and the index gets overwritten.

  • What you need to remember is the two steps.

    • First move a branch, possibly moving it in place, but in general, moving it to a specific commit.
    • Second, copy data from the current commit to the index, the working area, both or neither, depending on which kind of reset you are doing.
⚠️ **GitHub.com Fallback** ⚠️