Rebasing
The git rebase
command allows you to apply commits from a source branch to another target branch WITHOUT creating a merge commit, resulting in a linear commit history on the target branch. Rebase can be viewed as more powerful version of Cherry-Pick, optimized to apply multiple commits from one branch to another.
Since rebasing can be difficult to visualize, it is recommended that you use SmartGit when rebasing, where rebase operations can be performed in the UI, e.g. by simply selecting commits and dragging them to the required parent. See Rebasing in SmartGit.
Depending on the state of the source and target branches, rebase may result in:
- Fast Forward of new commits - if there are no new commits on the target branch which are not present in the source branch, the target branch can simply be updated to ‘point’ at HEAD of the source branch without creating new commits.
- Rewritten Commits - if there are new commits in both source and target branches, git will need to rewrite commits in the target branch (assigning new SHA hashes).
In addition, rebase
allows for advanced history rewriting and cleanup operations, such as squashing, deleting and modification of existing commits.
When rebase is used with the onto
parameter, additional precision can be specified as to controlling which commits are to be rewritten, as well as to change the parent commit from which a branch is created.
After a successful rebase, the HEAD of the current branch will point to the re-written fork containing the changes.
Example
In the below common scenario, after forking at commit C
, branches main
and feature
are now both 2 commits ahead of C
, and the commits in feature
cannot be fast-forwarded onto main
because of commits A
and B
.
As we prefer a linear commit history, we wish to rebase the commits E
and F
(on the feature
branch) onto the main
branch, as follows:
git checkout feature
git rebase main
o D' [>feature]
o A [main] |
| o E'
| o D [>feature] |
o B | o A [main]
| o E ===> |
| / o B
| / |
o C o C
... ...
Notes
- Commits
D
andE
are rewritten (toE'
andF'
) such that the parent ofE'
is nowA
(i.e. to reference the HEAD ofmain
). In this rebase scenario, Git will automatically determine the common parent commit between the branches (i.e.C
) and calculate that commitsE
andD
need to be rewritten (sinceC
is already present inmain
).- Branch
main
(including commitsA
,B
andC
) is not modified by the rebase- After the above rebase, it is now possible to Fast Forward
main
up to commitD'
- As can be seen, the effect is to create a linear commit history.
Rebase Onto
Rebase with the --onto
switch allows for more advanced scenarios.
In the below example, since forking at commit F
, main
has 3 commits (A
, B
and C
) ahead, and feature
has 2 commits (D
and E
) ahead.
We now wish to include commit E
(currently on branch feature
) into the main
branch (but not to include commit D
).
Additionally, we would like to specify that commit E
is to be included before commits A
, B
, C
.
git checkout main
git rebase --onto E F
o A [> main] o A' [> main]
| |
o B o B'
| |
o C o C'
| |
| o [feature] D | o D [feature]
| | | /
| | | /
| o E (selected) ===> o E
| / |
| / |
o F o F
Notes
- The commit history of
main
will be rewritten - although the commitsA'
,B'
andC'
have the same changes as the original commitsA
,B
andC
respectively, a new SHA hash will be assigned for each commit rewritten after the rebase is completed.- The commit history of
feature
is unchanged in this scenario - commitsD
,E
andF
aren’t modified by the above rebase.
Interactive Rebase
Rebase with the --interactive
switch allows a specified range of commits to be rewritten, and then, interactively, you will then need to specify how each commit in the range will be handled, with options such as:
- pick - i.e. to include the commit
- reword - i.e. to include the commit but to change the commit message
- edit - i.e. to amend the contents of the commit
- squash - i.e. to combine the contents of the commit into the previous commit, i.e. reducing the number of commits. Squash can be applied successively, so that any number of commits are combined into one commit.
- drop - i.e. remove the commit
Additionally, the order of the commits can be changed during an interactive rebase
See Also: Interactive Rebasing in SmartGit.