Git stash - putting changes temporally aside

It sometimes happens that you need to switch branches for a moment, but some changes are in progress in the current branch. To put aside those changes for a while, we can use the git stash command: let's give it a try in our grocery repository.

Append a new fruit to the shopping list, then try to switch branch; Git won't allow you to do so, because with the checkout you would lose your local (not yet committed) changes to the shoppingList.txt file. So, type the git stash command; your changes will be set apart and removed from your current branch, letting you switch to another one (berries, in this case):

[1] ~/grocery (master) 
$ echo "plum" >> shoppingList.txt 
 
[2] ~/grocery (master) 
$ git status 
On branch master 
Changes not staged for commit: 
  (use "git add <file>..." to update what will be committed) 
  (use "git checkout -- <file>..." to discard changes in working directory) 
 
        modified:   shoppingList.txt 
 
no changes added to commit (use "git add" and/or "git commit -a") 
 
[3] ~/grocery (master) 
$ git checkout berries 
error: Your local changes to the following files would be overwritten by checkout: 
        shoppingList.txt 
Please commit your changes or stash them before you switch branches. 
Aborting 
 
[4] ~/grocery (master) 
$ git stash 
Saved working directory and index state WIP on master: b25ffa6 Add an apricot 
HEAD is now at b25ffa6 Add an apricot 
 
[5] ~/grocery (master) 
$ git checkout berries 
Switched to branch 'berries' 

How does git stash work? Actually, git stash is a fairly complex command. It basically saves from two up to three different commits:

  • A new WIP commit containing the actual state of the working copy; it contains all the tracked files, and their modifications.
  • An index commit, as a parent of the WIP commit. This contains stuff added to the staging area.
  • An optional third commit, let's call it an untracked files commit, which contains untracked files (using the --include-untracked option) or untracked plus previously ignored files (using the --all option).

Let's take a look at the actual situation in our repository using the git log command:

[6] ~/grocery (berries) 
$ git log --oneline --graph --decorate --all 
*   fedc4cf (refs/stash) WIP on master: b25ffa6 Add an apricot 
| 
| * 7312ff0 index on master: b25ffa6 Add an apricot 
|/ 
* b25ffa6 (master) Add an apricot 
* 280e7a8 Cherry picked the blackberry 
* 5dc3352 Add a grape 
* de8bcb9 Add a peach 
| * 362f8ec (HEAD -> berries) Add a strawberry 
| * f037469 (melons) Add a watermelon 
| * af9b640 Add a blackberry 
|/ 
* 00404b4 Add an orange 
* f583fdc Add an apple 
* 40d865b Add a banana to the shopping list

As you can see, in this case there are only two commits. The WIP commit, fedc4cf, is the one with the message starting with WIP on master, where master is of course the branch where HEAD was at the time of the git stash command run. The index commit, 7312ff0, is the one with the message starting with index on master.

The WIP commit contains the unstaged changes made to tracked files; as you can see, the WIP commit has two parents: one is the index commit, containing staged changes, the other is the last commit on the master branch, where HEAD was and where we run the git stash command.

With all this shelved information, Git can then re-apply your work on top of the master branch when you finish your job on the berries branch; a stash can be applied wherever you want, and more than once if you like.

Using the git stash command, we actually used the git stash save subcommand, the default option. The save subcommand saves changes to tracked files only, using a default set of messages for these special commits we see.

To retrieve a stash, the command is git stash apply <stash>; it applies changes within the two commits, eventually modifying your working copy and staging area. The stash will not be deleted after the apply; you can do it manually using the git stash drop <stash> subcommand. Another way implies the git stash pop <stash> subcommand: it applies the stash and then deletes it.

While using these subcommands, you can refer to the various stashes you did in the past using different notations; the most common is stash@{0}, where 0 means the last stash you did. To retrieve the penultimate, you can use stash@{1} and so on.

To make a complete example, let's drop the actual stash without applying it, and then do a new one following these steps:

  1. Drop the last stash created using git stash drop (git stash clear drops all the stashes).
  2. Append a new fruit to the shopping list (for example, a plum) and add it to the staging area.
  3. Then add another one (for example, a pear) but avoid adding it to the staging area.
  4. Now create a new untracked file (for example, notes.txt).
  5. Finally, create a new stash using -u (the --include-untracked option).

Here is the complete list of commands:

[1] ~/grocery (master) 
$ echo "plum" >> shoppingList.txt 
 
[2] ~/grocery (master) 
$ git add . 
 
[3] ~/grocery (master) 
$ echo "pear" >> shoppingList.txt 
 
[4] ~/grocery (master) 
$ echo "Reserve some tropical fruit for next weekend" > notes.txt 
 
[5] ~/grocery (master) 
$ git status 
On branch master 
Changes to be committed: 
  (use "git reset HEAD <file>..." to unstage) 
 
        modified:   shoppingList.txt 
 
Changes not staged for commit: 
  (use "git add <file>..." to update what will be committed) 
  (use "git checkout -- <file>..." to discard changes in working directory) 
 
        modified:   shoppingList.txt 
 
Untracked files: 
  (use "git add <file>..." to include in what will be committed) 
 
        notes.txt 
 
[6] ~/grocery (master) 
$ git stash -u 
Saved working directory and index state WIP on master: b25ffa6 Add an apricot 
HEAD is now at b25ffa6 Add an apricot 

OK, let's see what happened using the git log command:

 
[7] ~/grocery (master) 
$ git log --oneline --graph --decorate --all 
*-.   87b1d8b (refs/stash) WIP on master: b25ffa6 Add an apricot 
|  
| | * b07c304 untracked files on master: b25ffa6 Add an apricot 
| * ad2efef index on master: b25ffa6 Add an apricot 
|/ 
* b25ffa6 (HEAD -> master) Add an apricot 
* 280e7a8 Cherry picked the blackberry 
* 5dc3352 Add a grape 
* de8bcb9 Add a peach 
| * 362f8ec (berries) Add a strawberry 
| * f037469 (melons) Add a watermelon 
| * af9b640 Add a blackberry 
|/ 
* 00404b4 Add an orange 
* f583fdc Add an apple 
* 40d865b Add a banana to the shopping list 

As you can see, this time there is one more commit, the untracked files one: this commit contains the notes.txt file, and figures as an additional parent for the WIP commit.

Summarizing, you basically use the git stash save command (with the -u or --all option if needed) to shelve your modification and then git stash apply to retrieve them; I suggest using git stash apply and then git stash drop instead of git pop to have a chance to redo your stash application when needed, or when your stash is not as trivial as usual.

To take a look at all the options for this command, please refer to the git stash --help output.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.54.63