A demonstration of using rebase to base commits on a different branch.
<style> .mine code { background-color: #80ff80; color: #000000; } .theirs code { background-color: #ff0000; color: #000000; } .main code { background-color: #0000ff; color: #ffffff } .old code { background-color: #808080; color: #ffffff } .another code { background-color: #ffc000; color: #000000; } </style>
Ignore the HTML above if you see it.
This repository is best viewed in Visual Studio code with the following plugins enabled:
To get started:
- Open the README.md file
- Use the Markdown: Open Preview to the Side command, or click this icon at the top of the window:
- You may then close the README.md buffer.
main
move/feature1
move/patch1
Note:main
may be calledmaster
in older repositories.
This repository has this scenario set up for you to try.
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 move/patch1
to try out the fix.
You can use the GitLens commit graph to view the results, or you can use this command:
git log --graph --all
Let's say you are creating a patch, so you start a branch. Let's call it move/patch1
.
This is what you intend:
gitGraph%%{init { 'theme': 'default', 'themeVariables': {
'git0': '#0000ff',
'git1': '#00ff00'
} } }%%
commit
commit tag: "main" type: HIGHLIGHT
branch move/patch1
commit id: "fix-1"
commit id: "fix-2"
But let's say you forgot you were working on another branch, move/feature1
,
and do this instead:
gitGraph
%%{init { 'theme': 'default', 'themeVariables': {
'git0': '#0000ff',
'git1': '#ff0000',
'git2': '#00ff00'
} } }%%
commit
commit tag: "main"
branch move/feature1
commit type: HIGHLIGHT tag: "move/feature1"
branch move/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 move/feature1
commit type: HIGHLIGHT tag: "move/feature1"
checkout main
branch move/patch1
commit id: "fix-1"
commit id: "fix-2"
That is, you want to leave move/feature1
alone
and not include it in your pull request.
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
move/feature1
branch. - The branch we want to move. Here, that's our
move/patch1
branch
To do this, we use git rebase --onto
:
git rebase --onto main move/feature1 move/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 "--patch1-old--"
commit id: "fix-1"
commit id: "fix-2"
checkout main
branch 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 "-patch1-"
commit id: "fix-1"
commit id: "fix-2"
branch another-branch
commit
checkout main
branch 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).
To find past commits, the first place to look is on the remote branches.
git branch -v --all
Doing git log
on these may identify which commit you need (often it is the
tip of the corresponding remote branch itself).
To find past commits that are no longer on any branch git reflog
will list
every commit that a branch has pointed to, for at least the past 90 days.
Once you have identified the particular commit a branch should point to,
e.g. a03925f
, you can do:p
git switch my-branch1
git reset --hard a03925f
This will move the branch to the indicated commit ID (a03925f
) and update
the working tree to match.
Another convenient trick is to create a branch to remember a particular commit you want to investigate.
git switch -C temp a03925f
git rebase
can do much more, including reordering history, but this is
the most common usage.