<!doctype html>
Rebase Demo
A demonstration of using rebase to base commits on a different branch.
If you see HTML above, click the approriate branch below:
Note:main
may be calledmaster
in older repositories.
Try it
This repository has this scenario set up for you to try. It is best viewed in Visual Studio code with the Markdown Preview Enhanced plugin enabled.
The line below is what changes between branches:
You are on branch: main
Use git switch
branch
to switch to each of these branches, then
switch to my-patch
to try out the fix.
Scenario
Let's say you are creating a patch, so you start a branch. Let's call it my-patch1
.
This is what you intend:
gitGraph%%{init { 'theme': 'default', 'themeVariables': {
'git0': '#0000ff',
'git1': '#00ff00'
} } }%%
commit
commit tag: "main" type: HIGHLIGHT
branch my-patch1
commit id: "fix-1"
commit id: "fix-2"
But let's say you forgot you were working on another branch, feature1
,
and do this instead:
gitGraph
%%{init { 'theme': 'default', 'themeVariables': {
'git0': '#0000ff',
'git1': '#ff0000',
'git2': '#00ff00'
} } }%%
commit
commit tag: "main"
branch feature1
commit type: HIGHLIGHT tag: "feature1"
branch my-patch1
commit id: "fix-1"
commit id: "fix-2"
You want to fix it like this:
gitGraph
%%{init { 'theme': 'default', 'themeVariables': {
'git0': '#0000ff',
'git1': '#ff0000',
'git2': '#00ff00'
} } }%%
commit
commit tag: "main"
branch feature1
commit type: HIGHLIGHT tag: "feature1"
checkout main
branch my-patch1
commit id: "fix-1"
commit id: "fix-2"
That is, you want to leave feature1
alone
and not include it in your pull request.
The fix: git rebase --onto
To fix this, we need to tell git three things:
- Where we want to put our commits. Here, that's
main
- The patch before the range we want to move. Here,
that's the
feature1
branch. - The branch we want to move. Here, that's our
my-patch1
branch
To do this, we use git rebase --onto
:
git rebase --onto main feature1 my-patch1
The result
gitGraph
%%{init { 'theme': 'default', 'themeVariables': {
'git0': '#0000ff',
'git1': '#ff0000',
'git2': '#808080',
'git3': '#00ff00'
} } }%%
commit
commit tag: "main"
branch feature1
commit type: HIGHLIGHT tag: "feature1"
branch "-old-my-patch1-"
commit id: "fix-1"
commit id: "fix-2"
checkout main
branch my-patch1
commit id: "fix-1-copy"
commit id: "fix-2-copy"
The result is that your fixes are copied into a new chain of commits
starting with the current main
commit.
The Pull Request
One mistake that git is not very forgiving of is changing history
that you've already shared with others via git push
.
The easy case
If you haven't pushed your branch yet, you're all set.
You can safely push my-patch
and make your pull request.
git push --set-upstream origin my-patch
If not, you have two choices.
The safe option
The problem is that it can cause difficulties and confusion for others who have already cloned your branch.
To avoid this you can delete your pull request, if any, and then rename
your branch, e.g. my-patch-1
:
git branch -M my-patch my-patch-1
git push --set-upstream origin my-patch-1
Then make a new pull request.
The quick option
If nobody is using what you've pushed, you can do a
force push to update your branch. Normally, you can
only add commits to branches when you push. But with
the --force-with-lease
option you can tell the server
to simply set my-patch
to point to the new commit:
git push --set-upstream --force-with-lease origin my-patch
--force-with-lease
is a newer, safer version of
--force
that avoids certain race conditions. It's good
to get in the habit of using it instead of --force
.
If you make a mistake
The old commits remain in the repository. They are no longer
referenced by your branch. They might be referenced by
another-branch
like this:
gitGraph
%%{init { 'theme': 'default', 'themeVariables': {
'git0': '#0000ff',
'git1': '#ff0000',
'git2': '#808080',
'git3': '#ff8000',
'git4': '#00ff00'
} } }%%
commit
commit tag: "main"
branch feature1
commit type: HIGHLIGHT tag: "feature1"
branch "-old-my-patch1-"
commit id: "fix-1"
commit id: "fix-2"
branch another-branch
commit
checkout main
branch my-patch1
commit id: "fix-1-copy"
commit id: "fix-2-copy"
But that's unlikely in this scenario.
You can also find the commit ID in the reflog and construct such a branch later if you need to recover. Git is very forgiving of most mistakes.
If nothing references your old branch, git will eventually garbage collect those commits when pruning the reflog: (default: >90 days).