Git: Simply Stashing

stashMost Rubyists find themselves using Git on a day-to-day basis. We use it to organize our projects, protect ourselves from errors, and to make changes with the confidence that our code is safe. Its simple command line interface belies its flexibility and depth. Because of this power, it definitely merits deep study and practice.

Today, I want to discuss a few of the techniques that I use to make swapping branches easier, specifically the git-stash command.

git-stash is a wonderful way to temporarily hide changes in a dirty working directory. This allows us to work on a feature branch and quickly swap to other branches without the bother of staging / commiting our changes first.

typical-stash-workflow
[Typical Stash Workflow]

If you are like most developers, then the extent of your experience with git-stash extends no further than the following two commands:

git stash

and

git stash pop

Often, this is enough for what we need, but git-stash has so much more to offer. Let’s explore some of its myriad abilities.

Saving With a Description

The simplistic approach to stashing is git stash and is analogous (mostly) to git stash save. The latter takes several arguments, most important of which is a description. If you are going to use stash as part of your normal workflow you must use these descriptions to keep better control of each stash. Let’s see how that’d work:

touch gum_drop_mountains.rb   # New feature
git add .                     # git-stash only works on tracked files
                              # unless you pass the --include-untracked flag
                              # to stash save

git stash save "Initial pass on Gum Drop Mountains."
git stash list
stash@{0}: On master: Initial pass on Gum Drop Mountains.

Now when we view the current stash list we see meaningful messages instead of the default description WIP on <branch> <last commit message>.

Stashing In Patches

Sometimes you want to stash a specific subset of changes, for this we can use the --patch (-p) flag. This allows us to interactively specify which changes will be included in this stash.

# edit gum_drop_mountain.rb
git stash save -p "Added GDM tests"

Which will drop you into a prompt like below:

git-stash-p

Once you’ve begun the interactive process, you can press ? to see a list of available commands. Upon completion you’ll be able to see your new stash with git stash list. The changes that are left uncommitted (to the stash) will still be in your current directory.

Listing Stashes

The most common way to view your current stashed changes is to type git stash list as we touched on earlier. This will print:

# Unless you are cool and passed a description

stash@{0}: WIP on submit: 6ebd0e2... Initial Commit.
stash@{1}: On master: 9cc0589... Add git-stash

Here, the stashes are specified as stash@{<revision>}. Many of the git-stash commands allow this stash identifier to be explicitly set, making it an important piece of information. git stash list takes any of the options accessible to git log which allows for some customization.

git stash list --pretty=format:'%Cblue%h %Cred* %C(yellow)%s'

Which will output something like:

pretty-format

You can alias git-log with the above formatting, which is common. Doing so with the git stash list is a prime candidate for this style of optimization, as being able to quickly decipher these descriptions can save you time.

Showing Individual Stashes

Showing stashes can be done with the git stash show command. It accepts an argument for the stash identifier and defaults to stash@{0} (most recent). It will display the diffstat by default.

Applying Stashes

Let’s assume we’ve been working for few hours. During this time we’ve been pulled off several times to work on other branches in the project. As we were pulled away, we’ve stashed our dirty working directories and given them all nice messages so we can cobble it all together later. Now that the bugs are fixed, we get to go back to our features. Our stash list looks like this:

git stash list
stash@{0}: On gum_drop_mountain: Touching up gum drop mountain peaks.
stash@{1}: On candy_cane_lane: Adding some tinsel to candy cane lane
stash@{2}: On master: First pass on gum drop mountains.

We want to go back to the candy_cane_lane feature branch and apply our stashed changes there. To do so, we are going to use the git stash apply command.

git checkout candy_cane_lane
git stash apply stash@{1}    # Specified stash or most recent (stash@{0})

Once we’ve done that, our changes are back in their proper place and we can resume work.

Git is smart here, in that you can apply these changes to branches other than the branch from which they were stashed. Furthermore, the branch you apply the changes to doesn’t have to be a clean directory! Git will throw a merge conflict to be manually resolved if the stash no longer applies cleanly[1].

Removing Stashes

Just for funzies, let’s check the stash list before moving on.

git stash list
stash@{0}: On gum_drop_mountain: Touching up gum drop mountain peaks.
stash@{1}: On candy_cane_lane: Adding some tinsel to candy cane lane
stash@{2}: On master: First pass on gum drop mountains.

What? Our stash is still present in our list even after applying it. That’s because apply doesn’t automatically clean up after itself, we have to use the following command to remove the stash

git stash drop stash@{1}

Which will drop just the stash we applied. Had we not given it a stash identfier it would have assumed we meant to drop stash@{0} (most recent). If we want to be rid of all stashed changes we can run git stash clear. Beware of clearing stashes (with either drop or clear) as they are impossible to recover.

The Oh-So Convenient Pop

After applying a stash of changes to a branch, it is quite frequently a good idea to remove that stash from the list (like above). Luckily, Git provides us with a convenient method that wraps this workflow. Assume we want to apply stash@{1} and remove it from the stash list. We can run the following command:

git stash pop stash@{1}

If we provide no stash identifier, it would assume most recent (like most git-stash commands). The git-stash man page provides a good way of thinking about this command. That it does the inverse of git stash save.

Branching a Stash

Sometimes you’ll find yourself in a situation where you’ve begun changes on the wrong branch (master for example) and want to take your current dirty directory to its own branch. Well you are in luck! Normally you’d have to do something like this:

git stash
git checkout -b new_feature
git stash pop

But git-stash has a convenience method to do just this. Simply:

git stash
git stash branch new_feature

This will create and checkout a new branch new_feature and apply stash@{0} to it.

To Stash or Not To Stash

Determining when to stash versus when to commit and squash later is a matter of taste. As long as you write descriptive messages about the stash and be sure to keep your stash list clean, then stashing is a great way to quickly get to a clean directory. If, on the other hand, you don’t take care, you’ll find yourself painfully wasting time figuring out which stash is which.

Warnings aside, stashing is a powerful ability that Git provides and with a little practice it can help you navigate your code more quickly and safely. Happy hacking!

Resources

  1. Pro Git (stashing)
  2. git ready
  3. git-stash man page

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://yawar.blogspot.com/ Yawar

    Is it really _impossible_ to recover a dropped or cleared stash? I thought we could use the reflog?