How to rebase after git-subtree add?

Epeli picture Epeli · Oct 12, 2012 · Viewed 13.4k times · Source

I'm trying to learn the new git-subtree command which was added in Git 1.7.11. I seem to lose ability to rebase after I add a subtree. I have the primary repository with README file and a library repository which also has a README file. I add it to lib directory with subtree add:

$ git subtree add -P lib/mylib myliborigin master

This works fine, but now the history looks like this:

*   22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - 
|\                                                                                                                
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
* b99d55b Add readme
* 020e372 Initial

Now when I want to rebase my repo against origin/master and it fails because the squash commit is applied directly against its parent commit which does not apply, because it is applied to the root of the repo and not the prefix I gave it to it when adding the subtree.

The reason for this is pretty clear if I look at the squash commit. There is no information about the prefix. It is just the original mylib commits squashed together. Only the next merge commit knows anything about it, but rebase does not take it to account here.

Are there any workarounds (besides never rebasing over the subtree commits)?

Answer

Kevin Cooper picture Kevin Cooper · Jan 17, 2018

This works in simple cases:

git rebase --preserve-merges master

Thanks to @Techlive Zheng in the comments.


You may see

fatal: refusing to merge unrelated histories
Error redoing merge a95986e...

Which means that git failed to automatically apply your subtree. This puts you in the situation @ericpeters described in his answer. Solution:

Re-add your subtree (use the same command you originally used):

git subtree add -P lib lib-origin master

Continue the rebase:

git rebase --continue

And you're all set!


If you're wondering if it worked successfully, you can compare with your original version after rebasing to make sure you didn't change anything:

git diff <ref-before-rebase> <ref-after-rebase> -- .

(the -- . at the end instructs git to only diff the files in your current directory).


If all else fails and you don't care about preserving the commits themselves, you can simply git cherry-pick the original subtree commit.

The commit message will look something like Add 'lib/' from commit '9767e6...' -- that's the one you want.