Git · Lesson 4 · 30 min

Git: stash, cherry-pick & rebase

Real work is messy. You're halfway through something when an urgent fix comes in. A teammate has the one commit you need, buried in a branch full of unfinished work. And main has moved on while you worked on your branch. Three commands handle all of that: stash, cherry-pick and rebase.

You will learn

  • Park unfinished work with git stash and get it back with stash pop
  • Copy one commit to another branch with git cherry-pick
  • Bring your branch up to date with git rebase, and fix a conflict halfway through
  • Rebase or merge? And the rule for when not to rebase

Stash: a pocket for unfinished work

Git won't let you switch branches if that would overwrite changes you haven't committed. You could make a messy “WIP” commit, but stash is tidier. It saves your changes away and gives you a clean folder:

Same on both
git stash                      # save changes to tracked files, clean the folder
git stash -u                   # …and new (untracked) files too
git stash push -m "gallery photos"   # with a name, so you remember what it was
git stash list                 # stash@{0} is the newest
git stash pop                  # put the newest back, and delete it from the stash
git stash apply                # put it back, but keep a copy in the stash

Stashes are local (they're never pushed) and easy to forget. Pop them soon, and check git stash list now and then.

Cherry-pick: copy one commit

Sometimes you need one commit from another branch, not the whole branch. Maybe it's a bug fix that main needs today, while the rest of the branch isn't ready. cherry-pick copies that commit onto the branch you're on:

Same on both
git log --oneline priya-wip    # find the commit you want
git switch main
git cherry-pick 3f9a1c2        # a new commit on main, with the same change and message

The copy gets a new hash, because it has a different parent. When the original branch is merged later, git notices the change is already there.

Rebase: replay your work on top

You started a branch from main. Since then, other people's work has landed on main. You can bring your branch up to date two ways:

Questiongit merge maingit rebase main
What it doesAdds a merge commit that joins the two linesLifts your commits off and replays them, one by one, on top of the newest main
History looks likeA fork and a join, exactly as it happenedOne straight line, as if you started today
Your commitsUnchangedRewritten: same changes, new hashes
Safe on a shared branch?YesNo: only for commits nobody else has
Before:                                  After git rebase main (on gallery):

main     A───B───C───J                   main     A───B───C───J
                  \                                            \
gallery            G1───G2               gallery                G1'───G2'

After a rebase, merging the branch into main is a simple fast-forward, and git log reads like a story.

Conflicts during a rebase

A rebase replays your commits one at a time, so a conflict stops it partway. The fix is the same as for a merge, with a different last step:

  1. git status shows the conflicted file. Open it, make it look right, and delete the <<<<<<<, ======= and >>>>>>> lines.
  2. git add FILE marks it as fixed.
  3. git rebase --continue carries on with the next commit. (Not git commit.)

Lost? git rebase --abort puts your branch back exactly as it was before you started. Note that during a rebase, HEAD (“ours”) is main's side, and your commit is the other side, the opposite of a merge.

The rebase rule

Only rebase commits that nobody else has built on: your own branch, before you share it or while it's still just yours in a pull request. Never rebase main, or any branch your teammates pull from. If you must update a branch you've already pushed, push it with git push --force-with-lease, as lesson 5 shows.

Pull with rebase

git pull is a fetch plus a merge. If you'd rather replay your unpushed commits on top of what you pulled (no little merge commits), make it a fetch plus a rebase:

git pull --rebase                            # this once
git config --global pull.rebase true         # always

Real git can also rewrite a branch's commits with git rebase -i, an interactive rebase. It opens your editor with a list of commits so you can squash several into one, reword messages or drop some. It's handy for tidying a branch before a pull request. The playground doesn't have it, because it's all editor work.

You're on the gallery branch of the club website, halfway through adding photos. Priya's priya-wip branch has a fix for the club email address that main needs right now. And main has moved on since you started your branch.

Quick check

1. You're halfway through a change, and need to switch branches to fix something urgent. What's the tidy way?

2. A rebase stopped with a conflict. You fixed the file and ran git add. What's next?

3. Which branch is it not OK to rebase?

Finished the missions and the quiz? Mark it done to track your progress.