I had a history that looked like this (ordered by number/letter):
c1a - c1b - c2a - c2b - c4 - c5 - c6 - c8 (master)
| \
| c9 (branch2)
\
c3 - c10 (branch3)
\
c7 - c11 (branch4)
I wanted to squash commit c2a en c2b, but I couldn't find a branch independent way to do so, so I had to rebase 4 times, and not with the result I was expecting (stars represent copies due to rebasing):
c1a - c1b - c2ab - c4 - c5 - c6 - c8 (master)
||\
|| c2ab* - c4* - c5* - c6* - c9 (branch2)
|\
| c2ab** - c3 - c10 (branch3)
\
c2ab*** - c3* - c7 - c11(branch4)
in stead of
c1a - c1b - c2ab - c4 - c5 - c6 - c8 (master)
| \
| c9 (branch2)
\
c3 - c10 (branch3)
\
c7 - c11 (branch4)
All modifications to the history are both local and remote
My questions are:
1) You need to run git rebase
at least 3 more times for branches 2, 3, and 4
2) You can't. if you change a commit you have to rewrite every descendant commit, and all refs associated with those commits (via rebase). There is no way around this.
Your middle graph suggests something went wrong with your original attempts at rebasing, so this one time you will end up with twice the rebase operations you would normally have.
Generally speaking, if you have a published history with that many commits and refs based off of it I would not recommend mucking around with it.
Starting from your original diagram - you have 3 SHAs that act as anchors for your branches. C2B which leads to Master and Branch3, C3 which leads to Branch 4, and C6 which leads to Branch2. Once you rewrite the SHA for C2B, all those other SHAs are going to change as well, which makes this whole process really convoluted and hard to automate without keeping track of intermediate SHAs directly. The following should work for this specific situation.
Note that this requires you to have specific knowledge of the commits on the various branches. You can attempt to do the rebases without the commit limiters, but I have occasionally had bad experiences doing this (where Git rebases more than it should and I end up having to ctrl-c out of it and then abort and try again)
git checkout master
git rebase -i HEAD~5
# edit rebase todo list to combine c2a/c2b
# once rebase has finished, master is OK. Use git log to get the SHA that represents C2AB
git checkout branch2
git rebase --onto master~1 HEAD~1 # this will relocate the one unique commit on branch2 and hang it off of C6'
git checkout branch3
git rebase --onto C2AB_SHA HEAD~2 # this will take the two commits on branch3 and put them onto C2AB
git checkout branch4
git rebase --onto branch3~1 HEAD~2 # this will take the two commits on branch4 and put them on C3'
Normally when you do something like this you would rebase the other branches to be at your new branch tip instead of trying to preserve the original parentage. It makes things much easier.
Your recovery process is virtually the same, but with slightly different --onto and limiting specifiers.