This is my understanding of what the answer is, based on comments:
When you do a rebase, the commits on your current branch are 'undone', then 'reapplied', but actually, they are not undone, they are 'remembered'*, and reapplied with new IDs, so for example, if I look in git reflog show FeatureB
, I get something like this:
7832f89 FeatureB@{0} rebase finished: refs/heads/FeatureB onto f4df3
efd3fed FeatureB@{1} commit: B
f3f3d43 FeatureB@{2} commit: A
2f32fed FeatureB@{3} branch: Created from HEAD
So as @Jefromi said, the originals are still there (the SHAs of the A and B commits in the reflog are not the same as the ones in git log, which correspond to A' and B').
Similarly, git reflog show FeatureC
looks like this
32873ef FeatureC@{0} commit: W
17dafb3 FeatureC@{1} rebase finished: refs/heads/FeatureC onto 89289fe
893eb78 FeatureC@{2} commit: Z
a78b873 FeatureC@{3} commit: Y
e78b873 FeatureC@{4} commit: X
378cbe3 FeatureC@{5} branch: Created from HEAD
Again, the original Z, Y and X commits are still there
So, the solution to my problem is to create a new branch FeaturesBC
off the HEAD of master (for example), then cherry-pick the commits FeatureB{2 & 1}, and then FeatureC{4, 3, 2}, and (possibly) W:
git checkout master
git checkout -b FeaturesBC
git cherry-pick f3f3d43
git cherry-pick efd3fed
//etc...
(It seems to have worked, I had to re-do some of the same merges, but it wasn't too bad)
Edit, from Jefromi:
Cherry-picking may not have been necessary. You can also simply recreate branches where the branches were before the rebase:
git branch FeatureB-old efd3fed
git branch FeatureC-old 893eb78
Or, if you want to throw away the rebased position of FeatureB and FeatureC, going back to where they were before:
git branch -f FeatureB efd3fed
git branch -f FeatureC 893eb78
Finally, note that if you like you can use the other notation provided in the reflogs - for example, FeatureC@{2}
instead of 893eb78
. This means "the second previous position of FeatureC". Be careful to only use this immediately after viewing the reflog, though, because as soon as you update the branch again (move it, commit to it...), FeatureC@{2}
will refer to 17dafb3 instead.
As @Jefromi commented on my question:
You should probably have created a new branch off of master or featureC (called featuresABC, say), and merged each into it, leaving the feature branches intact. It's good to preserve the independent history of various feature branches.
* To be precise, the old commit objects are simply left in the repository. They will eventually be pruned, since you don't want a repo full of old dangling commits; this will happen the first time git gc
is run and the commits are at least two weeks old (configured by gc.pruneExpire
).