Git rebase - global-121/121-platform GitHub Wiki
git rebase
It is recommended to watch the related video before following the steps in the guide. If you don't know where to find it, reach out.
Local machine setup
To make your life easier with rebasing on your machine, do these things (one time only).
Set VSCode as your default code editor for git
Update your file at ~/.gitconfig adding (or editing) the following values:
[core]
editor = code --wait
[merge]
tool = vscode
[mergetool "vscode"]
cmd = code --wait $MERGED
[diff]
tool = vscode
[difftool "vscode"]
cmd = code --wait --diff $LOCAL $REMOTE
[sequence]
editor = code --wait
Before starting a rebase
Before doing anything, verify that your branch is up to date with origin, and that you have a clean working tree:
git fetch origin
git status
Should output something like
On branch aberonni/remove-wait-for-in-tests
Your branch is up to date with 'origin/aberonni/remove-wait-for-in-tests'.
nothing to commit, working tree clean
If your branch is not up to date and you don't know what to do, go to troubleshooting.
Starting a rebase
To start a rebase:
git rebase -i TARGET_BRANCH
For example, to rebase on main:
git rebase -i origin/main
Running a rebase with the -i flag means "interactive". This will prompt git to open your (previously configured) editor, so you can decide what to do with the commits that you are "reapplying" on the target branch. Once you have decided what you want to do, close the editor to initiate the rebase.
[!IMPORTANT] You should always check to see if all of these commits belong to you. The ones that don't should be dropped - not doing so will lead to nasty and confusing conflicts.
More info in the Dropping commits section.
[!TIP] If you don't know what to do in the editor, and all of the commits in the list are yours, then it is safe to just close the list without touching anything. If you're still not sure, ask!
Executing the rebase
During the rebase process, each of the commits that you have chosen will be "reapplied" on top of the target branch, in the chosen order.
If there are no conflicts, then you will see a message like the following, and then you can proceed to push:
Successfully rebased and updated refs/heads/aberonni/remove-wait-for-in-tests.
However, there could be conflicts during the rebase process. In that case, the rebase will be interrupted, and you will see an error like the following:
CONFLICT (content): Merge conflict in services/121-service/package.json
error: could not apply fd633410f... Add better reporter for tests
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply fd633410f... Add better reporter for tests
And you will have to do the following
- Solve all conflicts in the files manually (VSCode will help identify files with conflicts)
- Add or delete each file when you have solved the conflict (VSCode will tell you if you are trying to do this to a file that still has conflicts)
- Run the following command once you have solved all of the conflicts for that commit:
git rebase --continue - Git will prompt you to update the commit message. You can close the file to leave the commit message as-is.
- Repeat from step 1 until you have solved all conflicts
In the VSCode editor, during conflict resolution, "Current" always represents the changes on the "target branch", while "Incoming" represent the changes on your branch.
package-lock.json
You should never solve a conflict on this file manually. Instead:
- Solve any conflicts in
package.jsonmanually - Run
npm installin the folder that contains thepackage.jsonand thepackage-lock.jsonfiles in question - The conflicts in
package-lock.jsonshould be automatically solved.
Pushing the rebase
To push a rebased branch, you need to "push force" because you have "rewritten git history" and so the git origin (GitHub) will not accept your version of history.
[!CAUTION] WARNING: This is a destructive operation. Do not do this if you do not understand what you are doing. If in doubt, stop and ask for help. Never do this on a branch that is not your own.
git push --force-with-lease --force-if-includes
If you see an error, it might be because the HEAD of your target branch has changed since you started rebasing. In this case, you need to update your local branch, and then run the rebase again:
git pull --rebase
git rebase -i TARGET_BRANCH
Dropping commits
It is important to always run an interactive rebase, because not doing so can lead to unexpected surprises. This is because, at times, you might be "carrying" commits that aren't yours.
A typical scenario in which this can happen is when changing target branch.
Scenario: Changing target branch
At times we will find ourselves working on a branch that does not branch off of main.
For example:
- Gianni built a new endpoint on the branch
feat/fancy-new-backend-endpoint - Andrew wants to build the frontend for that endpoint so he creates a branch based on Gianni's branch:
feat/pretty-new-frontend-feature
From time to time, Gianni will make changes to their branch, which Andrew will need. So Andrew will run:
git checkout feat/pretty-new-frontend-feature
git fetch origin
git rebase -i origin/feat/fancy-new-backend-endpoint
So far, so good, and nothing out of the extraordinary.
But, at a certain point, Gianni's endpoint will be merged into main. From here onwards, Andrew will need to rebase on main:
git checkout feat/pretty-new-frontend-feature
git fetch origin
git rebase -i origin/main # the target branch has changed
[!IMPORTANT] The first time the target branch changes (eg. in this example from
feat/fancy-new-backend-endpointtomain), there will be extra commits shown in the rebase editor.
Now Andrew will see a mix of their own commits, and Gianni's commits from feat/fancy-new-backend-endpoint. That is because git cannot tell that those commits have now been merged into main. So it is important that, in the rebase editor, Andrew manually drops all of the commits that do not belong to his branch. Not dropping those commits will lead to
- a nasty, confusing, and unnecessary series of rebase conflicts
- a confusing git history
[!NOTE] The necessity to drop commits can also happen in other scenarios. For example, if the target branch has had any operation on it that has modified it's history (eg. commits were squashed).
For the above reasons, it is important to always check the list of commits in the interactive rebase editor, and ensure that they all belong to you.
Troubleshooting
My branch is not up to date before rebasing
Do you just have pending changes? Then just push:
git push
Did you do a rebase already and forget to push? Then "push force":
git push --force-with-lease --force-if-includes
Did you rebase via GitHub UI? Then you need to "pull with rebase":
git pull --rebase
My rebase is in a weird state and I need help
Don't push anything to remote. Abort the rebase with the following command and ask for help:
git rebase --abort
Cheatsheet
# Before rebasing:
git checkout <BRANCH_YOU_WANT_TO_REBASE>
git fetch origin
git status
# Make sure everything is up to date with origin.
# Then start rebase, for example, if <TARGET_BRANCH> is main:
git rebase -i origin/main
# At this point, you edit the rebase file in your editor of choosing, and then close it.
# git will now initiate the rebase.
# If there are conflicts, you solve them (either in VSCode or manually).
# Then, for each file with conflicts solved:
git add/rm FILE # Or you can do this in VSCode
# Once all conflicts are solved, resume the rebase with:
git rebase --continue
# Once the rebase is complete, you push with:
git push --force # DESTRUCTIVE OPERATION
# If at any point your rebase ends up in a weird state, you can go back to step 0 with
git rebase --abort