76

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?

malvim
  • 1,194
  • 1
  • 8
  • 17
  • 1
    [This newer question](http://stackoverflow.com/questions/5600659/rebasing-a-branch-including-all-its-children/5600770#5600770) is probably a duplicate, but I'm not sure it'd ever actually get closed as a duplicate of an older question without a real answer. – Cascabel Apr 12 '11 at 00:19
  • 2
    See also [how I'd rebase a whole subhistory -- several branches, with some links between them resulting from merge](http://stackoverflow.com/a/9706495/94687). – imz -- Ivan Zakharyaschev Mar 14 '12 at 17:02
  • 1
    `git rebase --onto` should also be mentioned in case `topic1` or `topic2` had child branches https://coderwall.com/p/xzsr9g/rebasing-dependent-branches-with-git – quimnuss Jan 12 '18 at 15:18

5 Answers5

23

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?

araqnid
  • 127,052
  • 24
  • 157
  • 134
  • 1
    Thanks for the help! I guess this is just a harder problem than I thought it was, so I'll keep going by with manual rebasing. :) – malvim May 21 '09 at 17:07
  • 3
    I use `git branch --no-merged` to list the branches that I want to bring up to the latest master. – rjmunro Apr 23 '10 at 14:22
  • 1
    @rjmunro nice, I didn't know about that flag. However, I don't think it's appropriate for the case where your branches need rebasing rather than merging. – araqnid Apr 23 '10 at 16:03
13

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 :-)

flacs
  • 3,913
  • 4
  • 19
  • 20
  • 1
    See [this answer](http://stackoverflow.com/a/5632027/396967) to a related question for a "smarter" version, automatically determining branches containing a particular base commit. – kynan May 27 '12 at 23:43
2

If you would like to rebase all local branches onto master, you could try this:

git branch | cut -c 3- | for branch in $(cat); do git rebase master $branch; done
Huang C.
  • 398
  • 6
  • 17
  • Hi @cgl, thanks for your edit, but we don't need to checkout a branch just to do a rebase, see https://stackoverflow.com/a/56840200/3522482 – Huang C. Jan 09 '23 at 18:29
1

I have turned this into a robust script, maintained in my git-extensions repository:

$ git-urebaselocalbr --help
Rebase all / the last committed N local branches (except for the current branch
and master) to the updated upstream head.
Usage: git-urebaselocalbr [--continue|--skip|--abort] [--branches "<branch1> ..."] [N] [-i|--interactive] [options]
Bruno Bieri
  • 9,724
  • 11
  • 63
  • 92
Ingo Karkat
  • 167,457
  • 16
  • 250
  • 324
0
git branch | sed 's/\*/ /g' | xargs -n1 git rebase master
tdrd
  • 7
  • 3
  • 1
    While this code may provide a solution to the question, it's better to add context as to why/how it works. This can help future users learn and eventually apply that knowledge to their own code. You are also likely to have positive-feedback/upvotes from users, when the code is explained. – Amit Verma Feb 18 '21 at 04:38