Git Rebase
There are two ways to sync a feature branch with another branch; merge (git merge
) or rebase (git rebase
). Merging strategy has a few downsides like addition of a merge commit (or not if it can fast-forward), non-linear commit history and others. Where these things are important rebase strategy is viable.
Basic rebase
A rebase to sync changes from another branch can be done in the like this:
git checkout <feature_branch>
git rebase main
Internally, git rebase
reapply commits from feature branch on top (ie after) of all changes/commits from main (or specified branch). It basically changes and move base/tip/parent of feature branch.
The following is the visual representation of it:
A---B---C feature
/
D---E---F---G main
The feature branch after rebase becomes
A'--B'--C' feature
/
D---E---F---G main
Interactive rebase
Rebase also has an interactive mode, guiding you through the process and giving additional fine tune controls which can be used for following:
- Cleaning history
- Rewriting commit messages
- Reorder commits
- Squashing multiple commits
- Dropping a commit (off the face of the earth ;))
To do an interactive rebase, just add -i
or --interactive
flag:
git rebase -i main
It will show a visual prompt in a text editor, like the following:
pick f58924e xkcd 1296
pick dd820cc another, xkcd 1597
pick a58124a xkcd 688
# Rebase 0bbe6c4..dd820cc onto 0bbe6c4 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
Initial lines show a list of all commits in feature branch, followed by commented commands/actions which can be performed on them.
Change pick
to whichever action (reword
, edit
, squash
, drop
, etc) required. Action which require additional input will open another set of prompts with instructions for the same. The sequence of commits can also be changed here by moving commit up or down the list.
Squashing commit
Rebase can also be used for squashing commits (in local branch) by following:
git rebase -i HEAD~3
Here 3
is the number of preceding commits to be squashed into one.
The command, will open an interactive prompt, as shown in the interactive rebase section. Reorder commits, add pick
to a single commit (in which others will be squashed) and move it to the top. Add squash
before other commits. After saving, another prompt will open asking for a unified commit message. Edit, save and you’re done.
Caveats of rebase
- Don’t use rebase on public/shared branch (rebasing from it is fine). Also applies on pushed commits. Things might get messy.
- Rebase will present conflicts one commit at a time when merge will present them all at once, so choose accordingly.
- Reverting commits is a tad more difficult after rebases.