Skip to content

The Four Areas More Tools

Aakash Goplani edited this page Feb 10, 2018 · 6 revisions

Topics Covered

The Stash

  • We're finally going to talk about the fourth area, the stash. There is only one command that affects the stash and it's, git stash.

  • So if you want something to happen in the stash, you have to be very explicit about it and this is actually the selling point of the stash, it's all yours, even more than the working area is. The data in the stash doesn't change unless you really want it to. Let's see how you can use the stash.

  • Now let's create a new recipe in the recipe's directory, guacamole.txt. This file is supposed to contain the recipe or at least a list of ingredients. For now, I'm leaving it empty and stage it. In this project, whenever I add a recipe, I must also add it to the menu.txt file. So now we have an updated menu in the working area and a new file in the working area and the index.

image 1
image 2

  • Now imagine that while I'm working on this new recipe, I get interrupted for whatever reason. I need to do some work on another branch, for example. I want to focus on this other work, but I don't want my half-baked guacamole recipe to get in the way, so this is a good time to use the stash.

  • I can store all my changes in the stash and they will stay there safely until I decide to get back to the guacamole recipe. I store the current status with

$ git stash save

or just

$ git stash

I usually use the abbreviated form. And I also use this option, --include-untracked. It means also stash files that are still untracked. It doesn't make a difference, in this case, we don't have any untracked files, but by default, git stash just ignores untracked files.

$ git stash --include-untracked
Saved working directory and index state WIP on master: fbe5356 Well, I changed my mind about that renaming
HEAD is now at fbe5356 Well, I changed my mind about that renaming

image 3

  • Git takes all the data from the working area and the index that is not in the current commit in the repository and it copies all of that data to the stash. And then, it also checks out the current commit. So now we are aligned with the current commit. We're in the clean status again. Our files are gone from both the working area and the index. They're not really gone, of course, they are still in the stash.

  • I can read the content of the stash with stash list and there it is.

$ git stash list
stasg@{0}: WIP on master: fbe5356 Well, I changed my mind about that renaming

Now we can see the point of the stash.

  • The stash is like a clipboard for your project. It's the place where you store stuff that you need to set aside for some time and it's a multiple clipboard that you can have as elements as you want. Each element gets labeled with information about the latest commit to make it easier to identify and it also gets a serial ID. Right now, we only have one element, so it's called the stash@ 0. The next one would be stash@ 1.

  • Now that my stuff is stashed, I could switch to the other branch and do stuff in it, create new commits, whatever, anything. My half done work on guacamole stays in the stash. And after doing all this work, I can retrieve the stuff I stashed. You use

$ git stash apply

to move data from the stash to the working area and the index and you can give it the name of a stash element. I will not give it a name, so it applies to the most recent element by default, which is stash@ 0.

image 4
image 5

  • And there we are, all our data in the working area and the index is back where it was when I stashed it. I can finish the job, I can stage the menu file, add a few ingredients to the guacamole recipe here. And let's stage this update as well and commit the whole thing.
$ git add guacamole.txt
$ git commit -m "ingredients added"
$ git push
  • Now that our data is safe in the repository, I don't need that data in the stash anymore.

  • Let's clear the entire stash.

$ git stash clear

image 6

Solving Conflicts

  • Let's discuss merge conflicts. We have file A.txt with following contents:
A B C D

So let's edit the A.txt from two separate branches and then try to merge them.

  • First, I will create a new branch called dev and check it out. And in this branch, I will add some characters to A.txt and commit it.
A B C D F
  • Now let me switch back to the master branch and I will modify the file in there as well. In this branch, I will add different character and I commit.
A B C D E
  • Okay, now let's try to merge the dev branch into our current master branch.
$ git merge dev
  • And as soon as we do that, we get a conflict. And in fact, if I ask for the git status, it tells me that there is an unmerged path, a file where both sides of the merge have modified the file.

  • Git is pretty good at solving these kind of problems, but in this case, Git cannot possibly decide which of these changes come first or whether one of these changes must override the other. So it changed the file in the working area to give us enough information to solve the conflict ourselves.

A B C D
<<<<<<<HEAD
E
>>>>>>>dev
F

This is the first way in which a merge conflict impacts the four areas. It results in a modified file in the working area.

  • Before looking at this file in more detail, just one question, how does the git status command know that we are in the middle of a merge and that there is a conflict on this file? Well it knows because the merge command created a few files (MERGE_HEAD, MERGE_MODE, MERGE_MSG) in the .git directory here that signal that there is a merge operation occurring and they also contain information about what is being merged amongst other things.

  • So for example, the merge_head files are reference just like head, only merge_head is a temporary reference. It only lasts as long as it's needed to complete the merge and it's pointing at the tip of the branch that we're merging, the dev command.

  • Normally, we don't even see this stuff because git completes the merge on its own, but now we're right in the middle of a merge and we can either abort the entire merge or fix the conflicts and move forward.

  • Now we can manually edit this file and decide what the merged file must look like. Here I will keep both the characters, D and the E. Now that we have solved the conflict, we need to tell Git because it cannot know that we solved the conflict yet, and in fact, the status still sees a conflict here.

  • And that's another connection to the four areas. I must use the index to tell Git that I solved the conflict. We know that the index does get which information goes into the next commit and that's still true here, but in this case, the meaning of the git add command is a bit more specific. It means these files are ready to be committed because I edited it and I solved the conflicts it contained.

  • So now we can finally go forward and complete the merge. All we have to do is tell Git to commit. And there we are. The merge is done. It's in the log and that's how you solve merged conflicts manually.

Working with Paths

  • Let's look at an example involving the menu file and the readme file. I will edit the menu and I will also make a similar change to the readme file. And now let me stage all these changes.
$ git add menu.txt readme.md

image 7
image 8
image 9

  • Now let's say that I'm not sure about these changes anymore. Let's say that I like the change to the readme, but I'm not sure about the change to the menu just yet. So I want to unstage the menu file, but not the readme file.

  • One way to unstage a file. You can do a head reset $ git reset HEAD, but the last time we did it, we did it on the entire content of the index, but we can also do that on a single file. So let's do it.

$ git reset HEAD menu.txt

We didn't specify that this should be a hard head reset, so by default it's a mixed head reset and remember what a mixed head reset does. It doesn't move the current branch because it's head reset, but it copies data from the current commit to the index. It doesn't change the working area.
image 10

  • Now let's go one step further. Let's remove the menu changes from the working area as well. You might think that to do that we could reuse the same extraction, only this time make it a hard head reset. But no, that doesn't work. Git refuses to do a hard head reset with a path. That feels a bit inconsistent, but it's the way it is.

  • Instead, the most common way to revert a single file or directory in the working area without touching other files is to use checkout. Normally checkout moves the head reference in the repository usually to a branch and then it copies all the files from the repository to the working area and the index. In this case, a word checkout is not going to move head. Checkout is just going to copy stuff from the current commit in the repository to the working area and the index and it's only going to do that for the one file we specified.

$ git checkout menu.txt
  • So we just lost all the changes to that file. The menu is back to where it was in the latest commit, exactly what we wanted.
    image 11

  • Note that checkout with a file or path in general is not nearly as harmless as a regular checkout. It's actually one of the most destructive operations you can do in Git. We didn't even get a warning. We just swiftly and very covertly destroyed all of our changes to the menu file. So handle this commit with some care.

  • So now the only file that I have left modified and staged is the readme and I can finally commit it.

$ git commit -m "updated readme file and reverted menu file"

image 12

Git Is a Toolbox

  • Git is a toolbox. It's not a single program that does stuff. It's a toolbox because you have generic tools like reset or checkout, and like in a toolbox, you can use the same tool for different jobs.

  • Think about git reset, for example, you can use it to unstage a file, you can use it to clean up your working directory, you can use it to remove the top commits from your history, and some. Or git add. You can use git add to tell Git about a new file to put a modified file in the next commit or to signal that you solved the conflicts in a file during a merge. And you can also use different tools for similar jobs.

  • If you want to unstage a file, Git doesn't give you a command named, I don't know, unstage. There is no such command. To unstage a file, you need to understand what the index is first, and once you do, you can unstage the file with the reset or you can use a git remove --cached, for example. We use both commands to unstage a file in history. Git doesn't tell you here is this command, it's the command that you've got to use if you want to unstage a file. Git gives you the basic toolset and then it's up to you to pick the right tool.

  • Some of those tools also have other effects on the repository particular, think of commands such as commit or reset itself. We'll describe those effects as well, but our focus was in moving data across the areas.

Clone this wiki locally