Depending on your development workflow, rebasing may be a common component of your workflow; ie:
- You branch off master (we’ll call this branch
- You add a bunch of commits, for instance, developing a new feature
- You branch off again for a small fix (we’ll call this branch
B1---B2 branch-b / A1---A2---A3 branch-a / M master
A gets merged into master. Typically (if keeping a clean git history),
A was squashed into a single commit before merging. This leaves
B with the unsquashed history of
A, which does not exist in master.
Our history now looks like:
A1---A2---A3---B1---B2 branch-b / M---A master
If you attempt to rebase
B off master (
git rebase master), you’ll end up with an epic mess (every commit originally in
A will now conflict with the squashed commit now in
master). Attempting to correct all of the conflicts will likely ruin your week.
Fixing this is actually fairly straight forward, and there are two ways.
The first approach is more complex, but a bit safer, and involves cherry-picking all commits onto a new branch. You’ll need to check out a new branch off master (let’s call this branch
C), identify all of the commits which occurred after your initial branch off of
A, and use
git cherry-pick to pull the set of commits over. At the end, you can delete the original
B branch, rename
B, and force push your
git checkout master # Checkout master git checkout -b branch-b-rebased # Create a new branch for our rebase git cherry-pick B1..B2 # Pull over the `branch-b` commits git branch -D branch-b # Delete the original `branch-b` git branch -m branch-b # Rename the new branch to `branch-b` # You can now force-push your newly rebased branch
Note that you could also
git cherry-pickindividual commits, but be sure to do them in order.
Using an interactive rebase (
-i) is my preferred method, which is similar to the naive
git rebase master, however allows us to select which commits we want to apply back on top of master. This option is also commonly used for other instances of modifying commit history, such as reordering commits, or selectively squashing small commits into larger ones.
When used, you’ll be presented with a list of commits which will be reapplied, along with basis instructions for modifying the actions that will be taken. For instance,
git rebase -i master in our example repo from
branch-b in our example above gives:
pick 72b3e0c Feature A commit 1 pick d1c0b20 Feature A commit 2 pick c4c21df Feature A commit 3 pick a834f6a Feature B commit 1 pick ce91f24 Feature B commit 2 # Rebase 2bb4c6f..ce91f24 onto 2bb4c6f (4 commands) # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # d, drop = remove commit # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
Since we know that the first three commits (Feature A) were squashed into master when
branch-a was merged, we can drop those commits from the rebase by removing the line.
If we modify this as follows, we can rebase only the “Feature B” commits on top of
d 72b3e0c Feature A commit 1 d d1c0b20 Feature A commit 2 d c4c21df Feature A commit 3 pick a834f6a Feature B commit 1 pick ce91f24 Feature B commit 2
Using either approach we can achieve the branch structure that we want, and allow
branch-b to easily compare to, and be merged back into,
B1---B2 branch-b / M---A master
As a final note, when modifying the commit history, it is important to remember two things. First, having an understanding of how the git reflog will make it easy to recover from many mistakes. Second, remember to take care when force-pushing shared branches.
Hopefully this works out for you, or was at least helpful. If you have any questions, don’t hesitate to shoot me an email, or follow me on twitter @nrmitchi.