Skip to content

git rebasing a branch

Martin Shetty edited this page Jul 4, 2022 · 2 revisions

Even if you have used git before, the concept of rebasing your branch may be foreign to you. Rebasing basically means pulling your branch forward in time so it looks like a branch off the current master instead of the historical one you actually branched off of. Doing this makes the git history much neater and makes it easier to tell where a problem occurred later.

Say you've got a git branch that you're working on which fixes some issue. Before that branch can get merged into the master some time will pass. Design reviews have to occur, changes made based on that feedback, etc. During that time other changes will have been made to the master, so you want to rebase before the final commit.

Before you do your rebase you'll want to perform a fetch. git fetch origin. Fetch gets the latest content from origin and updates the origin/foo pointers ("branches"). Without that, the next command will rebase to whatever was the last-known state of origin, which might be old.

Once that's done you can rebase using git rebase origin/master. This command will make sure that you're rebasing against an up-to-date version of the master branch.

There may be conflicts caused by this, so you may have to go through a few cycles of fixing conflicts and then continuing the merge. Check the git documentation for all the details, but you basically edit the file that's got the conflicts and correct them, git add the updated file, then continue the rebase using git rebase --continue

Once the rebase is done you'll want to make sure you code still builds and fix any problems caused by the rebase. Then, force push your new branch: git push -f

Note that if you forget the -f modifier, git will suggest that you pull first. Don't do it!

After the rebase if you check the git status git may helpfully suggest that you pull in the latest changes from your branch. For example:

--->git status   
On branch issue_215_stm32hal   
Your branch and 'origin/issue_215_stm32hal' have diverged,   
and have 91 and 16 different commits each, respectively.  
  (use "git pull" to merge the remote branch into yours)   
nothing to commit, working tree clean

You may be tempted to git pull like the comment suggests.

Don't do it, it's a trap!

Git is helpfully suggesting how you can merge back into the branch that you're working on, but you don't want to do that. Think of your remote branch as a write only backup of your work. You don't want to pull changes from it, you want to push your new rebased version onto it. That's why you want to git push -f. You need to force the push (hence the -f) because you're overwriting the previous remote version.

More conflict resolution

There are a few additional actions you may need to take when resolving conflicts.

Removing a file

When there is a conflict such that git cannot automatically decide if a file should be kept or not, you may have to explicitly remove it. Do so with:

git rm path/to/file

Chosing one version of file

Sometimes you may not be able to manually resolve conflicts. This is particularly true when dealing with binary files where a more granular comparison of content is not possible like it is for code and text-based files. In this case you may just need to pick one version of the file over the other.

This selection is named somewhat counter-intuitively. Say you are rebasing my_branch against master.

To select the file in my_branch you will want to say git checkout --theirs -- path/to/file

To select the file in master you will want to say git checkout --ours -- path/to/file

This is the opposite of what you might expect. The reason is that when rebasing against master you take the perspective of master.

More generally, you may sometimes want to bring in a single file from any arbitrary branch or commit. The command for this is as follows:

git checkout {name_of_branch, commit_sha} path/to/file

You will still need to git add the files before you can continue with the rebase.

Interactive rebase

An interactive rebase allows you to select which commits in your history you want to apply and how. This may be necessary if an automatic rebase fails, or if you already know that you do not intend to apply all the changes for whatever reason.

Use the command

git rebase -i origin/master

This will open up a text editor that will allow you to select what you want to do with each commit in your branch history. The configuration file provides adequate documentation explaining what the options mean. You may also reorder the commits if needed. This has proved to be useful when resolving some exotic problems.

Finding relevant commits

When rebasing, it is generally good to compare your history with that on the server if order of commits is in doubt. Browse to your branch on github and select "commits", which should be at:

https://github.com/RespiraWorks/Ventilator/commits/branch_name

The first few commits at the top should be the relevant ones. Trace back to the last commit that coincides with what is currently in master (or whatever branch you intent to rebase to), and the commits above it should be the ones you ought to keep in an interactive rebase. Note that if your "parent" branch has been rebased since you last worked on this code, the common or irrelevant commits may not have the same checksums, or may not even exist as individual commits if squashed. Read the commit comments and examine the content of the changes to confirm identity.

Squashing

It is generally preferred that you squash all your branch commits into one when merging a PR. This is a type of rebasing. All commits in your branch will be flattened into one, thus making the change history cleaner.

To do this, start an interactive rebase as described above, but now change all commits except the first to say squash instead of pick, e.g. something like:

pick 5dd7564a first commit in branch
squash 5f9b32d4 second commit
squash 7abe560c third commit message
squash 42389785 fourth commit message, very informative
squash 12c13675 some more changes

After all the conflicts are resolved and the rebase is complete, you can review the aggregate commit message and possibly clean it up to remove redundancies or uninformative text.

You can read more about what squash rebasing entails here.

Pulling in rebased branches

Sometimes, the branch on the remote server will have been rebased with a "forced push" just as described above, but your local machine will still have the old branch history. A git pull will not be successful. If it fails, you can overwrite your local history with:

git reset --hard origin/branch_name

Further reading

If you want to better understand rebasing, it is worth reading some of the following guides: