As others have said, it is not possible to rebase a branch without touching the working directory (even the suggested alternatives such as creating a new clone or worktree cannot change this fact; these alternatives do indeed not touch your current working directory, but only by creating a new worktree).
For the special case where the branch that you want to update is to be rebased on the current working tree (or a parent thereof), it is possible to "rebase" the other branch without unnecessarily touching files.
This special case often happens if you are having a git workflow where you are working on many branches that are all branched from the main "master" branch (which is regularly updated to the remote master branch).
To illustrate, assume a Git repository with the following structure:
repo
- commitA
- commitB
- commitC <-- master <-- OtherBranch based on master
- commitD <-- First commit in otherBranch
- commitE <-- Second commit in OtherBranch
- commitD <-- Unrelated commit in current working tree
For the sake of the example, let's assume that "OtherBranch" is branched off "master", and that your current working tree is also based on "master".
Your workflow typically starts with updating your local master branch with the remote version...
# Fetch commits from remote "origin" and update the master branch:
# If your current branch is identical to master
git pull origin master
# If your current branch has extra commits on top of master
git pull --rebase origin master
# If you don't want to touch your current branch
git fetch origin master:master
... and then you fiddle with the current branch and do some time-consuming compilations. Eventually, you decide that you want to work on OtherBranch
. This OtherBranch
should be rebased on master
(preferably with minimal filesystem operations). The following section will show how.
Rebasing other branch (reference example - do NOT do this)
The following solution is the git way to do it:
git checkout OtherBranch
git rebase master # or git rebase origin/master
The disadvantage of that is that the first command changes the dates of the current worktree, even though the files are going to be restored by the second command.
Rebasing other branch with minimal changes
To minimize the number of touched files, you need to check out to the new base branch and then apply all extra commits in OtherBranch
on top of the base branch using git cherry-pick
.
Before doing anything, you need to identify the commits in OtherBranch
.
git log OtherBranch
shows the commits on OtherBranch (mainly useful if you haven't changed OtherBranch
yet)
git reflog
shows the changes to branches in your local repository (useful if you have already updated branches and made a mistake).
In the current example, you will discover that the last commit on OtherBranch
is commitE
. You can see a list of commits before that with git log commitE
(or if you want a shorter list, git log --oneline commitE
). If you look through the list, you will see that the base commit is commitC
.
Now you know that the base commit is commitC
and the last commit is commitE
, you can rebase OtherBranch (from its previous "master" to the new "master") as follows:
# Replace the old OtherBranch with "master" and switch to it.
git checkout -B OtherBranch master
# Cherry-pick commits starting from commitC and ending at commitE.
cherry-pick commitC^..commitE
Alternatively (if you want to successfully complete the "rebase" before replacing OtherBranch
):
# Create new branch NewOtherBranch based off "master" and switch to it.
git checkout -b NewOtherBranch master
# Cherry-pick commits starting from commitC and ending at commitE.
cherry-pick commitC^..commitE
# Replace the old branch with the current branch (-M = --move --force)
git branch -M OtherBranch
Why does this work?
Rebasing branches in git requires one to switch the current branch to the branch that you want to update (OtherBranch
).
With the git rebase
workflow, the following happens:
- Switch to
OtherBranch
(potentially branched off a very old base branch).
- Rebase (internal step 1): Save commits that are not in the upstream branch.
- Rebase (internal step 2): Reset current branch to the (new) base branch.
- Rebase (internal step 3): Restore commits from step 2.
Step 1 and step 3 touch many files, but ultimately many of the touched files have not actually changed.
My method combines step 1 and 3 into step 3, and as a result the number of touched files is minimal. The only files that are touched are:
- Files that were changed between the base branch and the current commit in the current working tree.
- Files that are changed by the commits in the
OtherBranch
.