I have a master branch in my project, that I use to pull changes from other people. From that, I usually have several topic branches on which I'm currently working.
My question is: Is there a way for me to pull new changes into my master and then rebase ALL of my topic branches onto that at once?
This is the situation:
D--E topic1 / A--B--C master \ F--G topic2
And I want to accomplish this with one single command (H came from upstream) :
D'--E' topic1 / A--B--C--H master \ F'--G' topic2
Now, I know I can accomplish this by rebasing topic1 and topic2 onto master, and I could even write a script to automate this. But what if I have several other branches, create new ones and delete others frequently and I receive upstream changes all the time?
This operation (several rebases), when done by hand, is both tiring and error-prone.
Is there an easier way?
Git merge and rebase serve the same purpose. They are designed to integrate changes from one or multiple branches into one. Although the final goal is the same, those two methods achieve it in different ways, and it's helpful to know the difference. Let's start with the following graph to understand the differences.
Yes, you can rebase more than once. After rebasing, you get a fresh set of commits. These commits are exactly like all other commits and hold no record of having been rebased. The main thing you need to be careful for is the possibility of rebase conflicts.
For individuals, rebasing makes a lot of sense. If you want to see the history completely same as it happened, you should use merge. Merge preserves history whereas rebase rewrites it . Rebasing is better to streamline a complex history, you are able to change the commit history by interactive rebase.
I'm fairly sure that there isn't a way to automatically do this. Remember that "git rebase master" can also drop you back to the shell needing you to resolve merge conflicts, so if you want to write a script to automate all this you need to take that into account.
You can fairly easily track which branches need updating, though. Hmm, for any branch, "git rev-list branch..master" will produce output if the branch is not up-to-date wrt (i.e. just commits on top of) master. So you need to loop through all the local heads except master to produce a report (nb "git show-branch" will approximately do this):
git for-each-ref 'refs/heads/*' | \ while read rev type ref; do branch=$(expr "$ref" : 'refs/heads/\(.*\)' ) revs=$(git rev-list $rev..master) if [ -n "$revs" ]; then echo $branch needs update git diff --summary --shortstat -M -C -C $rev master fi done
So if you were feeling brave, you could replace that "git diff" with something like "git checkout $branch && git rebase master" (or maybe just "git pull --rebase" if you've set that up). I think you'd then have to check for the existence of a ".git/rebase-apply" directory or check the index for unmerged files ("git ls-files -u") to test if we've been left waiting to do a merge.
Of course, if there are no conflicts, then it's easy... it's producing something that also works when it's not easy that's the problem :p
And this doesn't necessarily address what happens if one of your branches is based on something else... that's why I mentioned using "git pull --rebase" instead, because that would rebase according to the branch configuration, not blindly from master. Although the detection isn't based on the branch configuration... maybe it would be easiest just to check out each branch and do "git pull" and let the branch configuration handle everything, including whether to rebase or merge?
You can always just write a shell one-liner like this:
for branch in topic1 topic2 topic3;do git rebase master $branch;done
Since the topic branches you would like to rebase will probably change over time, this is a quick-and-dir^H^H^Hflexible solution :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With