Cheat Sheet

Git Rebase Cheat Sheet

git rebase replays your commits on top of another branch, which keeps a feature branch current and its history clean. This sheet covers the commands with short worked examples: interactive rebase, squash and fixup, conflicts, --onto, and how to undo a rebase.

Last updated September 11, 2026

Rebase onto main or master

Run rebase from the branch you want to move. It replays that branch's commits on top of the target.

CommandWhat it does
Replay the current branch's commits on top of main
Same, on repos whose default branch is master
Update the remote-tracking branches first
Rebase onto the remote's latest main, no local checkout of main needed
Check out feature, then rebase it onto main
Pull, replaying your local commits on top instead of merging
Make every pull a rebase by default
Stash uncommitted changes first, restore them after
Move stacked branches along with this one (Git 2.38+)
Push the rewritten branch; refuses if the remote grew new commits
Stop and put the branch back exactly as it was

The full update loop, step by step:

git switch feature
git fetch origin
git rebase origin/main        # if it stops on a conflict: fix, git add, git rebase --continue
git push --force-with-lease   # the commits were rewritten, so a plain push is rejected

Rebase After a Pull

When your branch and its remote have both moved, Git stops with "You have divergent branches and need to specify how to reconcile them". Rebasing puts your local commits back on top instead of making a merge commit.

CommandWhat it does
Fetch, then replay your local commits on top of the remote's
Make every pull a rebase, which clears the divergent branches error
Keep your own merge commits instead of flattening them
Stash and restore uncommitted work around every rebase
Re-base a branch after upstream squash-merged part of it
Rebase without pulling, when you already ran
# hint: You have divergent branches and need to specify how to reconcile them.
git config --global pull.rebase true   # set it once
git pull                               # now rebases instead of merging

After someone squash-merges your pull request, the branch's old commits no longer match anything upstream, so a plain rebase replays them as duplicates. Move only the commits you added since then:

git rebase --onto origin/main <last-commit-that-got-squashed> feature

Interactive Rebase

opens a todo list of commits, oldest first. Change the word in front of a commit to change what happens to it; reorder the lines to reorder the commits.

CommandWhat it does
Edit the last 5 commits
Edit every commit on this branch that main does not have
Edit from commit abc1234 (included) up to HEAD
Edit every commit on the branch, including the very first one
Replay the branch's merge commits instead of flattening them
Reopen the todo list mid-rebase
Preview which commits the todo will contain
Todo actionEffect
()Keep the commit as is
()Keep it, but stop to change the message
()Stop after this commit to amend or split it
()Meld into the commit above, combining both messages
()Meld into the commit above, discarding this message
Meld into the commit above, keeping this message instead
()Delete the commit (deleting the line does the same)
()Run a shell command at this point in the replay
()Pause here; resume with

A typical cleanup todo before opening a pull request:

pick a1b2c3d Add login endpoint
fixup e4f5a6b fix typo
reword 7c8d9e0 Add rate limiting
drop f0a1b2c WIP

Rebase Editor

The todo list opens in your sequence editor, which defaults to and then to vim. Saving an empty todo, or quitting with an error, cancels the rebase.

CommandWhat it does
Use VS Code for commit messages and the todo list
Use a different editor for the todo list only
Swap vim for nano everywhere
Accept the todo as generated, with no editor at all
In vim: save the todo and start the rebase
In vim: quit with an error, which cancels the rebase

is not optional for a GUI editor. Without it the command returns immediately, Git sees an unchanged todo, and the rebase runs before you have edited anything.

Fixup, Squash & Reword

Mark a commit as a fix the moment you make it, and files it under its target in the todo for you.

CommandWhat it does
Commit staged changes as a fix for commit abc1234
Same, but stop to combine the messages during the rebase
Fix abc1234 and reword its message in one go
Sort / commits under their targets automatically
Autosquash on every interactive rebase
Fold staged changes into the newest commit, no rebase needed
Same, keeping the existing message
# Fix a typo that lives three commits back
git add src/auth.js
git commit --fixup=abc1234
git rebase -i --autosquash main   # the fixup line is already in place; save and quit

Squash Commits

To squash the last N commits into one, rebase over them interactively and keep only the first .

CommandWhat it does
Squash the last 3: keep the first , mark the rest or
Un-commit the last 3, keeping all their changes staged
Squash everything the branch has added since main
Collapse the whole branch into one staged change, no todo list
From main: land the whole branch as a single commit
git rebase -i HEAD~3
pick a1b2c3d Add password reset
fixup e4f5a6b fix failing test
fixup 7c8d9e0 review feedback

The reset route gets the same result without touching the rebase machinery:

git reset --soft HEAD~3
git commit -m "Add password reset"

With Conflicts

A rebase replays commits one at a time, so it can stop on any of them. Fix the files, stage them, continue. Never run yourself mid-rebase; commits for you.

CommandWhat it does
List the conflicted files ("both modified")
Just the paths still unresolved
Show the remaining conflict markers
Mark a fixed file as resolved
Commit the resolution and replay the next commit
Walk the conflicts in your configured merge tool
Show the commit the rebase is stuck on
Redo the markers with the common ancestor shown between them

The whole loop:

git rebase main
# CONFLICT (content): Merge conflict in src/app.js
vim src/app.js                # remove the <<<<<<< ======= >>>>>>> markers
git add src/app.js
git rebase --continue         # repeat until "Successfully rebased"

Abort, Continue, Skip

after staging fixes, when the commit is already upstream, when the whole thing went sideways.

CommandWhat it does
Resume after resolving conflicts or an stop
Cancel the rebase and restore the branch as it was
Drop the commit being replayed and move on
Forget the rebase state but leave the files as they are now

If says there is nothing to commit, the resolution made the commit empty, usually because the change already exists upstream. That is what is for.

Ours vs Theirs

During a rebase, ours and theirs are the reverse of what most people expect. Rebase checks out the branch you are rebasing onto and replays your commits against it, so is that base branch (main) and is your own commit.

CommandWhat it does
Take the version from the branch you are rebasing onto
Take your own branch's version
Accept your version of every file conflicted at this stop
Accept the base branch's version of every conflicted file
Auto-resolve every conflict in favor of main
Auto-resolve every conflict in favor of your commits

Editors rename the same two sides. In VS Code's conflict view during a rebase, "Current Change" is the base branch and "Incoming Change" is the commit of yours being replayed.

After a / checkout, the file still needs and .

# Keep your version of the lock file for every conflict in this rebase
git rebase -X theirs main

# Or decide per file, mid-conflict
git checkout --theirs -- package-lock.json
git add package-lock.json
git rebase --continue

Rebase --onto

moves a range of commits to a new base: . Read it as "take the commits after old-base up to branch, and replay them on new-base".

CommandWhat it does
Move feature's own commits from old-base onto main
Re-parent featureB from featureA onto main
Move a branch started from the wrong base
Drop the two commits between HEAD4 and HEAD2
# You branched featureB off featureA, and featureA just merged.
# Replay only featureB's own commits onto main:
git rebase --onto main featureA featureB

Edit a Commit

Mark a commit in the todo and the rebase stops right after applying it, with the commit as HEAD.

CommandWhat it does
Open a todo that includes commit abc1234; mark it
Change the stopped commit's files or message
Un-commit the stopped commit so you can split it into several
Replay the rest of the branch on top of the change
# Change a file inside an older commit
git rebase -i abc1234^        # change "pick abc1234" to "edit abc1234"
vim src/config.js
git add src/config.js
git commit --amend --no-edit
git rebase --continue

To split it instead: at the stop, then stage and commit the pieces separately before .

Exec

runs a command after a commit is applied and stops the rebase if it fails, which pinpoints the commit that broke things.

CommandWhat it does
Run the tests after replaying each commit
The same as a hand-placed todo line
Combine a test run with normal todo editing
# Find the commit that broke the tests while rebasing a branch
git rebase -x "npm test" main
# stops at the first failing commit: fix it, git commit --amend, git rebase --continue

Rebase vs Merge

Both integrate one branch into another; they leave different history behind.

HistoryLinear, commits rewrittenMerge commit added, shape preserved
Commit hashesChangeUnchanged
Safe on a shared branchNoYes
ConflictsPer replayed commitOnce, all at the same time
Push afterwardsPlain

Rule of thumb: rebase your own feature branch to keep it current, merge to land finished work into a branch other people use. The git cheat sheet covers the merge side in full.

Rebase on GitHub

GitHub's "Rebase and merge" button replays a pull request's commits onto the base branch with no merge commit, rewriting them with new SHAs. Both it and the "Update branch" dropdown need a clean replay; on a conflict you do it locally and push.

CommandWhat it does
Pick up the base branch before updating the PR
The local equivalent of GitHub's "Update with rebase"
Update the open pull request with the rebased commits
Check out someone else's PR branch to rebase it
Tidy the PR's commits before asking for review
# "This branch is out-of-date with the base branch" on a PR with conflicts
git fetch origin
git rebase origin/main        # fix, git add, git rebase --continue
git push --force-with-lease   # the PR updates in place, review comments stay

Force-pushing a rebased branch keeps the pull request and its conversation; only the diffs of individual commits become unreachable, so old review comments may show as outdated.

Cherry-pick vs Rebase

copies commits and leaves the originals alone, so the change exists in two places. Rebase moves a range onto a new base and abandons the originals. See the git cheat sheet for the rest of cherry-pick.

CommandWhat it does
Copy one commit onto the current branch
Copy a range of commits, both ends included
Copy the changes but stop before committing
Same conflict loop as a rebase
Move a whole range instead of copying it commit by commit
# One fix belongs on the release branch too: copy it
git switch release-2.1
git cherry-pick abc1234

# The whole branch started from the wrong base: move it
git rebase --onto main old-base feature

Undo a Rebase

The old commits are not gone after a rebase; the reflog still points at them for about 90 days.

CommandWhat it does
Mid-rebase: stop and restore everything
Every position HEAD has held, rebased-away commits included
Move the branch back to reflog entry 7
Jump straight back, if nothing has overwritten ORIG_HEAD since
The same history, scoped to one branch
git reflog
# a1b2c3d HEAD@{0}: rebase (finish): returning to refs/heads/feature
# ...
# 9f8e7d6 HEAD@{7}: commit: the tip before the rebase started
git reset --hard HEAD@{7}

Pick the entry just below the first line: that was the branch tip before anything was rewritten.

Gotchas

  • Never rebase commits other people have already pulled. Rewriting a shared branch forces everyone else into a painful recovery; merge on shared branches, rebase on your own.
  • After any rebase, a plain is rejected. Use , never bare : with-lease fails safely if a teammate pushed in the meantime.
  • Ours and theirs swap meaning during a rebase. is the branch you are rebasing onto, is your own commit. and swap the same way.
  • Do not after fixing a conflict; then is the whole move. Committing by hand mid-rebase leads to duplicated or empty commits.
  • Rebase refuses to start over uncommitted changes. Commit them, stash them, or pass (or set once).

git rebase FAQ

Is git rebase dangerous?

It is safe on commits only you have, and risky on commits other people have already pulled. Rebase does not edit commits, it makes new ones with new hashes, so anyone who had the old ones ends up with both copies and a messy recovery. On your own branch the worst case is recoverable: git rebase --abort restores everything mid-rebase, and after it finishes git reflog still lists the old tip for about 90 days, so git reset --hard HEAD@{n} puts it back. The one habit that matters is pushing with git push --force-with-lease instead of --force, since with-lease refuses if a teammate pushed while you were rebasing.

How do you properly use git rebase?

The normal loop is git fetch origin, then git rebase origin/main from your feature branch, then git push --force-with-lease. Rebase before you open a pull request and before you merge, not in the middle of a review that people are commenting on. Keep the work in small commits while you build, then clean them up in one git rebase -i pass at the end rather than amending constantly. Two settings make it smoother: rebase.autosquash true so git commit --fixup lines sort themselves, and rebase.autostash true so an uncommitted file does not block the start.

Does git rebase rewrite history?

Yes. Rebase does not move your commits, it builds new ones with the same changes and a new parent, so every replayed commit gets a new hash even when its content is identical. The author name and author date carry over, but the committer date is set to now, which is why a rebased branch can look freshly written in some tools; git rebase --committer-date-is-author-date keeps the original timestamps. Because the hashes changed, the remote no longer recognises your branch as a fast-forward, so the push needs --force-with-lease. Anything that referenced the old hashes, such as a link in an issue, now points at commits only the reflog can still reach.

Will git rebase overwrite my local changes?

Committed work is not overwritten. Rebase refuses to start with a dirty tree, so uncommitted edits are the real risk: you get "cannot rebase: You have unstaged changes" and nothing happens until you commit, stash, or pass --autostash. Set rebase.autostash true once and Git stashes and restores around every rebase for you. Conflicts do not lose anything either, since the rebase pauses and waits for you. The one way to actually drop work is resolving a conflict by taking one side wholesale, with git checkout --ours or -X ours, which silently discards the other side.

In a git rebase conflict, which side is theirs?

Theirs is your own commit, which catches almost everyone out. Rebase checks out the branch you are rebasing onto and replays your commits against it, so from Git's point of view that base branch is ours and each commit of yours arriving on top is theirs. Editors make it worse by using different words: in VS Code, "Current Change" is the base branch and "Incoming Change" is your commit. To keep your own version of every file conflicted at this stop, run git checkout --theirs -- . then git add -A and git rebase --continue; to take that side for the whole rebase, restart it as git rebase -X theirs main. Save that for lock files and generated output, not source.

Related cheat sheets