-
Notifications
You must be signed in to change notification settings - Fork 1
The Four Areas More Tools
-
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 themenu.txt
file. So now we have an updated menu in the working area and a new file in the working area and the index.
-
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
-
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.
- 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
- 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 toA.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, onlymerge_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.
- Let's look at an example involving the
menu
file and thereadme
file. I will edit themenu
and I will also make a similar change to thereadme
file. And now let me stage all these changes.
$ git add menu.txt readme.md
-
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 themenu
just yet. So I want to unstage themenu
file, but not thereadme
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.
-
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.
-
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"
-
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. Orgit add
. You can usegit 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
orreset
itself. We'll describe those effects as well, but our focus was in moving data across the areas.