Git Workflow – Is Using Git Stash an Antipattern?

gitteamworkworkflows

I've recently been looking at how me and my team uses Git and how our workflows work. We currently use a feature-branch workflow which seems to work well.

I've also seen some individuals on our team use workflow based on git stash. The workflow goes something like this:

  • Work on a main branch (like master)
  • Make commits as you go
  • If you need to get changes or switch branches, push your uncommitted changes onto the stash
  • Once your updating is done, pop the changes off the stash.

I should mention that this workflow is used instead of a feature branch workflow. Instead of taking a branch and working on it, developers here only ever work on a single branch and push/pop off the stack as they see fit.

I actually don't think this is a great workflow, and branching would be more appropriate than using git stash in this way. I can see the value of git stash as an emergency operation, but not for using it in a daily, regular workflow.

Would using git stash regularly be considered an anti-pattern? If so, what are some specific problems that could arise? If not, what are the benefits?

Best Answer

From the Git SCM Book:

Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the git stash command.

Stashing takes the dirty state of your working directory — that is, your modified tracked files and staged changes — and saves it on a stack of unfinished changes that you can reapply at any time.

Given this description, I would say this is an Anti Pattern. An overly simplified explanation of Git Stash would be that it is the "Cut and Paste" of source control. You take a bunch of changed files, "stash" them away in a holding pen outside of Git's normal branching workflow, and then reapply those changes to a different branch at a later date.

Going back a little further, committing to master is the anti pattern here. Use branches. That's what they were designed for.

It really boils down to this:

You can hammer a screw into the wall and it will hold up a picture, but using a screwdriver is what you should do. Don't use a hammer when the screwdriver is sitting right beside you.

About Committing "Broken" Code

While the following is opinion, I have come to this opinion from experience.

Commit early, and commit often. Commit as much broken code as you want. View your local commit history as "save points" while you hack away at something. Once you've done a logical piece of work, make a commit. Sure it might break everything, but that doesn't matter as long as you don't push those commits. Before pushing, rebase and squash your commits.

  1. Create new branch
  2. Hack hack hack
  3. Commit broken code
  4. Polish the code and make it work
  5. Commit working code
  6. Rebase and Squash
  7. Test
  8. Push when tests are passing

For the OP, this Linux kernal message thread might be of interest, because it kind of sounds like some members of the OP's team is using Git in a similar manner.

@RibaldEddie said in a comment below:

First of all, a stash is not outside of a "branching workflow" since under the hood a stash is just another branch.

(at the risk of incurring the wrath of many people)

Linus said:

With "git stash", you can have multiple different stashed things too, but they don't queue up on each other - they are just random independent patches that you've stashed away because they were inconvenient at some point.

What I think @RibaldEddie is trying to say is that you can use git stash in a feature branch workflow -- and this is true. It's not the use of git stash that is the problem. It is the combination of committing to master and using git stash. This is an anti pattern.

Clarifying git rebase

From @RibaldEddie's comment:

Rebasing is much more like copy-pasting and even worse modifies committed history.

(Emphasis mine)

Modifying commit history is not a bad thing, as long as it is local commit history. If you rebase commits that you've already pushed, you'll essentially orphan anyone else using your branch. This is bad.

Now, say you've made several commits during the course of a day. Some commits were good. Some... not so good. The git rebase command in conjunction with squashing your commits is a good way to clean up your local commit history. It's nice to merge in one commit to public branches because it keeps the commit history of your team's shared branches clean. After rebasing, you'll want to test again, but if tests pass then you can push one clean commit instead of several dirty ones.

There is another interesting Linux Kernel thread on clean commit history.

Again, from Linus:

I want clean history, but that really means (a) clean and (b) history.

People can (and probably should) rebase their private trees (their own work). That's a cleanup. But never other peoples code. That's a "destroy history"

So the history part is fairly easy. There's only one major rule, and one minor clarification:

  • You must never EVER destroy other peoples history. You must not rebase commits other people did. Basically, if it doesn't have your sign-off on it, it's off limits: you can't rebase it, because it's not yours.

    Notice that this really is about other peoples history, not about other peoples code. If they sent stuff to you as an emailed patch, and you applied it with "git am -s", then it's their code, but it's your history.

    So you can go wild on the "git rebase" thing on it, even though you didn't write the code, as long as the commit itself is your private one.

  • Minor clarification to the rule: once you've published your history in some public site, other people may be using it, and so now it's clearly not your private history any more.

    So the minor clarification really is that it's not just about "your commit", it's also about it being private to your tree, and you haven't pushed it out and announced it yet.

...

Now the "clean" part is a bit more subtle, although the first rules are pretty obvious and easy:

  • Keep your own history readable

    Some people do this by just working things out in their head first, and not making mistakes. but that's very rare, and for the rest of us, we use "git rebase" etc while we work on our problems.

    So "git rebase" is not wrong. But it's right only if it's YOUR VERY OWN PRIVATE git tree.

  • Don't expose your crap.

    This means: if you're still in the "git rebase" phase, you don't push it out. If it's not ready, you send patches around, or use private git trees (just as a "patch series replacement") that you don't tell the public at large about.

(emphasis mine)

Conclusion

In the end, the OP has some developers doing this:

git checkout master
(edit files)
git commit -am "..."
(edit files)
git stash
git pull
git stash (pop|apply)

There are two problems here:

  1. Developers are committing to master. Lock this down immediately. Really, this is the biggest problem.
  2. Developers are constantly using git stash and git pull on master when they should be using feature branches.

There is nothing wrong with using git stash -- especially before a pull -- but using git stash in this manner is an anti pattern when there are better workflows in Git.

Their use of git stash a red herring. It is not the problem. Committing to master is the problem.

Related Topic