Optimize your workflow with Git stash | MDN Blog (2024)

Optimize your workflow with Git stash | MDN Blog (1)
Sponsored

Optimize your workflow with Git stash | MDN Blog (2)GitLab9 minute read

If you haven't used Git stash before, are already using it, or are curious about alternative workflows, this post is for you. We'll delve into use cases for stashing, discuss some of its pitfalls, and introduce an alternative method that makes managing uncommitted code safer and more convenient. By the end of this post, you'll better understand how to stash effectively and discover different strategies to improve your workflow.

What is Git stash?

You might have heard about git stash. It's a Git built-in command that can be used to store away uncommitted local changes. For example, when you have modified files in your working tree (often referred to as "dirty"), git status might show something like:

bash

$ git statusOn branch mainChanges not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory)modified: main.gono changes added to commit (use "git add" and/or "git commit -a")

When you want to save these changes, but don't want to commit them to the current branch, you can instead stash them:

bash

$ git stashSaved working directory and index state WIP on main: 821817d some commit message

This will clean up your working tree:

bash

$ git statusOn branch mainnothing to commit, working tree clean

The git stash list command shows your existing stashes, numbering them from 0 starting with the newest first. In this case, we see one stash:

bash

$ git stash liststash@{0}: WIP on main: 821817d some commit message

Stashing when switching branches

The most common use case for Git stash is when you want to store any work-in-progress code before switching branches to work on something else. For example:

bash

$ git statusOn branch feature-aChanges not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory)modified: lib/feature-a/file.gono changes added to commit (use "git add" and/or "git commit -a")$ git stashSaved working directory and index state WIP on feature-a: fd25af5 start feature A$ git switch feature-b# ... start working of feature B

Stashing changes when you switch branches has a few downsides:

  • After creating a stash, it's possible to completely forget about its existence and duplicate work.
  • It's easy to forget which branch a stash belongs to. Whenever changes in a stash are built on top of an unmerged feature branch, you might have a hard time unstashing these changes unless you're on the correct branch.
  • It might be difficult to reapply a stash to a branch if more changes have been made to the branch in the meantime, or if the branch might has been rebased.
  • A stash isn't backed up on the server. Your changes are gone when your local copy disappears (e.g., if the repository is deleted or a hard disk fails).

Alternative workflow for switching branches

Instead of using Git stash to store away your local changes, consider committing them to the branch. These commits will be temporary, and you should state this clearly in the commit message, for example by giving them the title "WIP". You can do this by running:

bash

git add .git commit -m "WIP"# or 'git commit -mWIP'

Later, when you return to that branch and see the title of the last commit as "WIP", you can roll it back with:

bash

git reset --soft HEAD~

This removes the last commit from the current branch, but leaves the changes in your working tree in place. To make this process more convenient, you can set up two aliases for this:

bash

git config --global alias.wip '!git add -A && git commit -mWIP'git config --global alias.unwip '!git reset --soft $(git log -1 --format=format:"%H" --invert-grep --grep "^WIP$")'

These aliases add two Git subcommands:

  • git wip: This command stages all your local changes (even untracked files) and writes a commit with the title "WIP" to the current branch.
  • git unwip: This command uses git log to look from the tip of the current branch to find a commit that doesn't have "WIP" as title. Then it resets to this commit using --soft to leave the changes in the working tree.

Now, when you have local changes and need to switch branches, just type git wip and the changes are stored in the current branch. If you're on a feature branch and your team's workflow is fine with rewriting history in such branches, you can even push this branch to back up these changes. Later, when you come back to this branch, you can type git unwip to continue working on these changes. If your workflow allows, you can rebase the branch before using git unwip. This will rebase the whole branch, including the WIP changes, to make sure you're working on the latest version of the target branch.

The git unwip command is designed to work on any branch. If there is no WIP commit on the tip of the current branch, nothing happens. If there are multiple commits on the tip, they are all undone. And if there is a non-WIP commit on top of a WIP commit, it's not rolled back. You'll need to resolve that manually.

Warning: Because git wip commits all untracked files, ensure any files containing secrets are in your .gitignore. Otherwise, they'll become part of the Git history, and you might accidentally push them to a remote where everyone can access them.

When to use Git stash

As mentioned earlier, Git stash isn't ideal for when you're switching branches. A better use case for Git stash is breaking down commits.

There is a lot written already about so-called "commit hygiene", and there are many opinions about it. It can be really beneficial if each commit tells its own story. Each commit makes one functional change at a time, preferably accompanied with a well-written commit message. In a workflow where you have smaller commits, it is easier for code reviews to go through the commits one by one and understand the story step by step. Whenever it's needed, it also enables you to revert a smaller set of changes.

Imagine you have a Go project, and this is an example of what you've started with:

go

package mainimport "fmt"func Greet() {fmt.Print("Hello world!")}func main() {Greet()}

This piece of code prints "Hello world!" when you run it. For various reasons, you need to refactor this. After making a bunch of changes, you end up with:

go

package mainimport ("fmt""io""os""time")var now = time.Nowfunc Format(whom string) string {greeting := "Hello"if h := now().Hour(); 6 < h && h < 12 {greeting = "Good morning"}return fmt.Sprintf("%v %v!", greeting, whom)}func Greet(w io.Writer) {fmt.Fprint(w, Format("world"))}func main() {Greet(os.Stdout)}

The main functionality remains the same, but there are a few feature changes:

  • You can specify whom to greet.
  • You can specify where to write the greeting.
  • The greeting will differ depending on the time of day.

In this scenario, we like to commit each of these functional changes separately. In many situations, you would be able to use git add -p to stage small hunks of code at once, but in this case, the changes are too intertwined. And this is where git stash comes in real handy. In the next steps below, we'll use it as a backup, where we save the end result in a stash, apply it, and then undo the changes we don't need for the current functional change. Because the end result is stored in a stash, we can repeat this process for each commit we want to make.

Let's have a look:

bash

git stash push --include-untracked

This saves all your local changes in a stash, and you can start breaking down changes into separate commits. The option --include-untracked will also include files that were never committed, which is useful if you've added new files.

Now we can start working on the first commit. Type git stash apply to bring the changes from the stash back into your local working tree:

bash

$ git stash applyOn branch mainChanges not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory)modified: main.gono changes added to commit (use "git add" and/or "git commit -a")

Open main.go in your favorite editor, and modify it so it includes the changes to add whom. This might look something like:

go

package mainimport ("fmt")func Greet(whom string) string {return fmt.Sprintf("Hello %v!", whom)}func main() {fmt.Print(Greet("world"))}

In this process, you can throw away all unwanted changes because the end result is safely stored away in a stash. This means you can adapt the code so it properly compiles and ensures the tests pass with these changes. When you're happy, these changes can be committed as usual:

bash

git add .git commit -m "allow caller to specify whom to greet"

We can repeat these steps for the next commit. Type git stash apply to get started. Unfortunately, this might give you conflicts:

bash

$ git stash applyAuto-merging main.goCONFLICT (content): Merge conflict in main.goRecorded preimage for 'main.go'On branch mainUnmerged paths: (use "git restore --staged <file>..." to unstage) (use "git add <file>..." to mark resolution)both modified: main.gono changes added to commit (use "git add" and/or "git commit -a")

Resolving conflicts is outside the scope of this article, but there is a quick way to restore the changes from the stash that may work well in such cases:

bash

git restore --theirs .git restore --staged .

Let's look at what that's doing. The git restore --theirs command will tell Git to resolve the conflict by taking all changes from theirs. In this case, theirs is the stash, which will apply the changes from there. The git restore --staged . command will unstage these changes, meaning they are no longer added to the index and are omitted the next time you type git commit.

Now you can start hacking on the code again, and eventually you might end up with something like:

go

package mainimport ("fmt""io""os")func Greet(w io.Writer, whom string) {fmt.Fprintf(w, "Hello %v!", whom)}func main() {Greet(os.Stdout, "world")}

Here you can repeat the usual commands to write another commit:

bash

git add .git commit -m "allow caller to specify where to write the greeting to"

For the final commit, simply run:

bash

git stash applygit checkout --theirs .git reset HEADgit add .git commit -m "use different greeting in the morning"

And you're done! You end up with a history of three commits added, and each commit adds one feature change at a time.

Summary

Stashing has various use cases. I wouldn't recommend it for saving changes when switching branches. Instead I recommend making temporary commits that you can push and manage easily. I use aliases to simplify this workflow and make it less prone to mistakes. On the other hand, stashing is an excellent fit for breaking down large, related commits into smaller, individual ones. With this in mind, you can maintain a cleaner project history and ensure your work is always backed up and organized.

I hope you enjoyed reading. If you're interested in the tests used in every commit for this post, this example project can be accessed at https://gitlab.com/toon/greetings.

About the author

Toon Claes is a Senior Backend Engineer at GitLab with a background in C & C++ and web and mobile development. He's passionate about mechanical keyboards, always on the lookout for the perfect one. A dedicated GNU Emacs user, Toon actively engages with the community and loves using Org mode for his projects.

This is a sponsored article by GitLab. GitLab is a comprehensive web-based DevSecOps platform providing Git-repository management, issue-tracking, continuous integration, and deployment pipeline features. Available in both open-source and proprietary versions, it's designed to cover the entire DevOps lifecycle, making it a popular choice for teams looking for a single platform to manage both code and operational data.

Previous Post How to debug mobile apps across devicesNext Post Locale-sensitive text segmentation in JavaScript with Intl.Segmenter
Optimize your workflow with Git stash | MDN Blog (2024)
Top Articles
8 Signs That a Partner May Want a Divorce
The Right Aims to Turn Back the Clock on Divorce
Instructional Resources
Gamevault Agent
Unblocked Games Premium Worlds Hardest Game
Google Jobs Denver
라이키 유출
PGA of America leaving Palm Beach Gardens for Frisco, Texas
Craigslist Deming
Nyuonsite
No Hard Feelings Showtimes Near Cinemark At Harlingen
Lesson 8 Skills Practice Solve Two-Step Inequalities Answer Key
Canvas Nthurston
Free Online Games on CrazyGames | Play Now!
2020 Military Pay Charts – Officer & Enlisted Pay Scales (3.1% Raise)
Rondom Ajax: ME grijpt in tijdens protest Ajax-fans bij hoofdbureau politie
Sprinkler Lv2
Xsensual Portland
Slim Thug’s Wealth and Wellness: A Journey Beyond Music
Scripchat Gratis
Sofia the baddie dog
UCLA Study Abroad | International Education Office
Roseann Marie Messina · 15800 Detroit Ave, Suite D, Lakewood, OH 44107-3748 · Lay Midwife
Reserve A Room Ucla
Little Einsteins Transcript
Craigslistodessa
Lincoln Financial Field, section 110, row 4, home of Philadelphia Eagles, Temple Owls, page 1
Myra's Floral Princeton Wv
Fridley Tsa Precheck
Whas Golf Card
Craigslist Neworleans
Indiana Wesleyan Transcripts
Free Robux Without Downloading Apps
Flashscore.com Live Football Scores Livescore
The Best Restaurants in Dublin - The MICHELIN Guide
Oriellys Tooele
R/Moissanite
2700 Yen To Usd
O'reilly's Palmyra Missouri
Directions To The Closest Auto Parts Store
ESA Science & Technology - The remarkable Red Rectangle: A stairway to heaven? [heic0408]
Mcalister's Deli Warrington Reviews
Kutty Movie Net
National Weather Service Richmond Va
Copd Active Learning Template
Maplestar Kemono
Wpne Tv Schedule
Pas Bcbs Prefix
Zits Comic Arcamax
Subdomain Finer
32 Easy Recipes That Start with Frozen Berries
Qvc Com Blogs
Latest Posts
Article information

Author: Rev. Porsche Oberbrunner

Last Updated:

Views: 5396

Rating: 4.2 / 5 (53 voted)

Reviews: 92% of readers found this page helpful

Author information

Name: Rev. Porsche Oberbrunner

Birthday: 1994-06-25

Address: Suite 153 582 Lubowitz Walks, Port Alfredoborough, IN 72879-2838

Phone: +128413562823324

Job: IT Strategist

Hobby: Video gaming, Basketball, Web surfing, Book restoration, Jogging, Shooting, Fishing

Introduction: My name is Rev. Porsche Oberbrunner, I am a zany, graceful, talented, witty, determined, shiny, enchanting person who loves writing and wants to share my knowledge and understanding with you.