Git stashing allows developers to temporarily shelf and restore uncommitted changes – a invaluable technique for collaborating with others, reviewing code, testing, and handling interruptions. This comprehensive guide will elaborate on unshelving, including:

  • Core concepts like the stash list
  • Specific step-by-step examples
  • How stashing compares to alternatives
  • Advanced application like merges
  • Best practices for avoiding pitfalls
  • When unshelving can cause conflicts
  • How to resolve conflicts when unstashing
  • Key takeaways for learning the process

Whether you are new to Git or an experienced practitioner, this guide aims to provide deeper technical insight into stashing and unshelving workflows.

What is the Stash List in Git?

The stash list maintains a stack-based record of all changesets captured during git stash save operations. New stashes get pushed onto the top at stash@{0}:

$ git stash list
stash@{0}: WIP on feature/report-generator
stash@{1}: On master: Updated readme 

Internally, stashes reside under .git/refs/stash. Each stash ref saves 3 trees recording the pre-stash state, changes to the index/staging area, and changes to the working tree:

.git/
  └─ refs/
    └─ stash
      ├─ stash@{0} 
      │  ├─ pre-state-tree
      │  ├─ index-tree
      │  └─ working-tree   
      └─ stash@{1}
         ├─ pre-state-tree
         ├─ index-tree
         └─ working-tree

Note only the pre-stash tree gets committed to the regular repository history during stash creation. The other two trees remain isolated from the commit DAG as implementation details purely for restoring state.

Fig 1. Overview of stash creation and application lifecycle

Now that you understand the stash data structure, let‘s see how to leverage stashing/unshelving in practice.

Why Stash Changes Instead of Using Feature Branches?

Developers commonly utilize feature branches for encapsulating work-in-progress code as well. Feature branches act as partitions that isolate development. What advantages does stashing offer over long-lived branches?

Isolating experiments: Stashing allows quick capture of transient changes for ethnography or prototyping – no need to pollute history with throwaway branches.

Reduced overhead: Creating and merging branches introduces overhead from resolving naming conflicts or keeping branches up-to-date. Stash operations are atomic metadata manipulations rather than full commits.

Lower storage: Stashes consume negligible disk space unlike potentially massive feature branch commits bloating .git.

In summary, stashing shines for short-term isolation. Lean on branches for long-running, substantial workstreams.

When Should You Stash Changes?

Let‘s explore several common scenarios where stashing uncommitted changes shines:

Task switching: Shelve in-flight work before jumping onto higher priorities using:

git stash push -m "WIP: Payment form styling" 

Collaborating: Stash experimental or unfinished work to share a clean branch:

git stash push -u -m "prototype: automl module"  
git checkout feature/reports

The -u flag stashes and removes untracked files in a single step.

Testing: Stow UI changes to diagnose a system regression:

git stash push 
mvn test // build application and run test suite  
git stash pop

Pausing: Temporarily stash incremental progress when forced to context switch:

// Friday afternoon  
git stash push 

// Monday morning
git stash pop

These scenarios demonstrate stashing‘s versatility. Next let‘s break down implementation step-by-step.

Step-By-Step Guide to Stash Changes

Stashing in Git checks out the current branch to a pristine state, stages modifications, removes untracked files, and records metadata – atomically in a single command.

Here is how to easily stash changes:

1. Inspect progress

Always start by reviewing changes about to be stashed:

➜  git status  
On branch feature/dashboard 

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)

    modified:   src/charts.js
    modified:   src/config.yaml

Changes 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:   src/index.html  

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    src/assets/img/

This repo has a mix of staged changes, unstaged changes, and untracked files.

2. Execute git stash push

➜ git stash push -m "Stash dashboard layout experiment"
Saved working directory and index state WIP on feature/dashboard: Dashboard layout experiment

Git stashes all 3 categories of changes and cleans up the working tree.

3. Verify working tree is clean

Double check git status to confirm the stash worked properly:

➜ git status
On branch feature/dashboard
nothing to commit, working tree clean

Success! The push subcommand bundles all progress on feature/dashboard into a shelved changeset.

When Do You Unshelve Changes?

Now those changes reside tucked away safely in the stash list rather than the active file system. When should you restore them?

Resuming work: Unstash after handling interruptions to pick up right where you left off:

➜ git stash pop  

Reviewing progress: Apply stashes to share unfinished features or review incremental work.

Discarding experiments: Popping and immediately dropping stashes constitutes a simple undo mechanism.

Debugging builds: Extract modifications necessary to reproduce an issue after stashing non-essential adjustments.

Maintaining context: Jumping around long-running branches can lose surrounding awareness. Stashes preserve tribal knowledge.

Next let‘s demonstrate actually unshelving changes…

Unshelving the Latest Stash

Restoring the most recently created stash is straightforward:

git stash pop # apply latest stash

# OR

git stash apply stash@{0} # use explicit reference

By default this tries merging changes back into the working tree. Use git stash apply for a less destructive restore.

Fig 2. Unshelving the most recent stash from index 0.

Note this does not remove anything from the stash list itself. Changes are extracted out normally.

Unshelving Specific Older Stashes

The stash list tracks push order with older stashes sinking towards the bottom. Reference any stash by inserting its ID:

➜ git stash list

stash@{2}: WIP on new-payment-service: 81250b0 Configure Stripe webhook 
stash@{1}: On production: 750b2f0 Hotfix - increase cache TTL to 10 minutes  
stash@{0}: On master: 5001020 Updated contributing guidelines

git stash apply stash@{2} # apply oldest stash

Traveling further back in history unstashes older lines of development!

Partially Unshelving Changes

The pop and apply commands work on whole stashes. But git also supports interactively choosing individual file hunks to re-apply:

git stash pop -p # choose hunks interactively

diff --git a/src/index.html b/src/index.html
+<<<- stash
+ <li>New nav item</li>
```
Apply this hunk to index and worktree [y,n,q,a,d,e,?]? 

Answer y to accept or n to reject hunks. This offers granular control for big changesets.

Challenges with Unshelving: Merge Conflicts

What happens if the code base advances while a stash sits suspended in time? The potential arises for merge conflicts.

For example, say feature/dashboard gets developed:

// On feature/dashboard  

git stash push # Shelve dashboard experiment 

git checkout main
git pull origin main # Pull latest main with dashboard changes
git checkout feature/dashboard 

git stash pop

This could produce conflicts like:

Auto-merging src/index.html
CONFLICT (content): Merge conflict in src/index.html 
Automatic merge failed; fix conflicts and then commit the result

Git was unable to cleanly merge parallel modifications to the same regions. How should you resolve?

1. Investigate conflicts

Examine the conflict markers pointing out mismatches:

<<<<<<< Updated upstream 
<div id="header">
=======
<div id="page-header"> 
>>>>>>> Stashed changes

2. Resolve each conflict

Manually edit files to remove markers and reconcile changes:

<div id="page-header">
</div>

3. Add resolved files & commit

Stage resolved files then create a merge commit:

git add .
git commit -m "Merged stashed dashboard changes" 

Pay careful attention to conflicts. They indicate accidental divergence that needs aligned!

Removing Stashes Safely

As you continually apply and delete stashes, the list gradually fills with obsolete entries. To delete:

git stash clear # remove all but latest  
git stash drop stash@{5} # delete single stash

Dropped stashes get PERMANENTLY erased and cannot be recovered!

Proceed carefully and avoid force pushes after deleting commits another dev may have relied on. The reflog serves as a safety net providing roughly 90 days to rescue dropped stashes with git log -g --stat refs/stash.

Alternatively, you can extract diffs to inspect changes first:

git stash show -p stash@{2} > /tmp/experiment.diff
git apply /tmp/experiment.diff

6 Best Practices for Stashing/Unshelving

Let‘s conclude by highlighting key learnings for stashing and unstashing smoothly:

1. Comment stashes with -m messages explaining origins.

2. Limit stash lifetime by regularly unstashing stale entries.

3. Handle conflicts diligently when unstashing parallel work.

4. Delete obsolete stashes to avoid clutter from accumulation.

5. Extract diffs rather than directly applying risky changes.

6. Leverage the reflog to rescue accidentally dropped stashes.

Respecting these best practices will ensure you become a stash master!

Conclusion

Temporary stashing serves a complementary role to long-lived feature branching. Stashes enable developers to easily shelve, share, and restore changes by manipulating Git‘s metadata.

This guide covered the inner workings of the stash list, step-by-step usage examples, challenges like conflicts, and tips for unshelving smoothly. Leveraging stashes unlocks new workflows!

Now put this knowledge into practice with git stash. What other creative applications can you discover?

Similar Posts