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.
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: feature1
Use git switch
branch
to switch to each of these branches, then
switch to my-patch
to try out the fix.
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.
git rebase --onto
To fix this, we need to tell git three things:
main
feature1
branch.my-patch1
branchTo do this, we use git rebase --onto
:
git rebase --onto main feature1 my-patch1
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.
One mistake that git is not very forgiving of is changing history
that you've already shared with others via git push
.
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 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.
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
.
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).