threads / discuss / 15926

Rebasing Multiple branches at once...

Subject: Rebasing Multiple branches at once...

## tl;dr

9 messages between Oct 16, 2008 and Oct 17, 2008.

replies: 8people: 6as markdown or json

Rick Moynihan· Oct 16, 2008, 12:17 UTC · lore
Hi,

I have a master branch, a dev branch and a number of feature branches from dev. And I was wondering if there was an easy way to rebase dev and all of it's sub-branches onto master.

I know I can run this as a series of commands, and use --onto to do this, but was wondering if there was an easier way. As running:

git rebase master
when on the dev branch only rebases dev and not it's dependent branches.
Thanks!
R.
Miklos Vajna· Oct 16, 2008, 13:59 UTC · re: Rick Moynihan · lore

Re: Rebasing Multiple branches at once...

On Thu, Oct 16, 2008 at 01:17:20PM +0100, Rick Moynihan <rick@calicojack.co.uk> wrote:
> I have a master branch, a dev branch and a number of feature branches from 
> dev.  And I was wondering if there was an easy way to rebase dev and all of 
> it's sub-branches onto master.

In general this is considered harmful. Why do you rebase your branch from time to time? For example in git.git, topic branches are based on master, then merged to master when they are ready, but they are never rebased.

Rick Moynihan· Oct 16, 2008, 14:48 UTC · re: Miklos Vajna · lore

Re: Rebasing Multiple branches at once...

Miklos Vajna wrote:
Show 9 quoted lines
> On Thu, Oct 16, 2008 at 01:17:20PM +0100, Rick Moynihan <rick@calicojack.co.uk> wrote:
>> I have a master branch, a dev branch and a number of feature branches from 
>> dev.  And I was wondering if there was an easy way to rebase dev and all of 
>> it's sub-branches onto master.
> 
> In general this is considered harmful. Why do you rebase your branch
> from time to time? For example in git.git, topic branches are based on
> master, then merged to master when they are ready, but they are never
> rebased.

Yes, but my understanding is that it's only harmful if you publish the branch (or dependent branches) which are being rebased.

So rebasing is very bad in these circumstances, but I fail to see why it's bad if these branches are kept private.

R.
Miklos Vajna· Oct 16, 2008, 21:00 UTC · re: Rick Moynihan · lore

Re: Rebasing Multiple branches at once...

On Thu, Oct 16, 2008 at 03:48:11PM +0100, Rick Moynihan <rick@calicojack.co.uk> wrote:
Show 5 quoted lines
> Yes, but my understanding is that it's only harmful if you publish the 
> branch (or dependent branches) which are being rebased.
> 
> So rebasing is very bad in these circumstances, but I fail to see why it's 
> bad if these branches are kept private.
Ah, I thought you publish your branches.

One reason may be that if you use merge, no history is lost, if you use rebase, history is in the reflogs, so it'll be lost after some time. But if you know what you are doing, then this is not a problem.

Junio C Hamano· Oct 17, 2008, 02:00 UTC · re: Miklos Vajna · lore

Re: Rebasing Multiple branches at once...

Miklos Vajna <vmiklos@frugalware.org> writes:
Show 9 quoted lines
> On Thu, Oct 16, 2008 at 01:17:20PM +0100, Rick Moynihan <rick@calicojack.co.uk> wrote:
>> I have a master branch, a dev branch and a number of feature branches from 
>> dev.  And I was wondering if there was an easy way to rebase dev and all of 
>> it's sub-branches onto master.
>
> In general this is considered harmful. Why do you rebase your branch
> from time to time? For example in git.git, topic branches are based on
> master, then merged to master when they are ready, but they are never
> rebased.
I do not think it is harmful per-se as long as they are not published.

But rebasing topic branches regularly (i.e. without reasons better than "the master has more commits than before") is _pointless_. The whole point of a topic branch is to house the development related to one single topic. If you keep rebasing it, its progress (when you look at the differences between topic@{1} and topic, topic@{2} and topic@{1}, ...) would not be about the single topic, but about the single topic _and all the other random things that happened on the master branch_.

When you merge a topic branch that forked from an older version of 'master', you may have conflicts, and you may want to resolve it earlier and that might be why you would want to rebase. But if you _know_ you are going to have conflicts, you wouldn't wish to rebase all of them at once to begin with, as you would need to resolve such conflicts yourself anyway. On the other hand, if you expect there won't be any conflict, then there absolutely is no point in rebasing them. If you want to make sure that your topics would all work with the updated 'master', you are much better off creating trial tree, merging your topics on top of the updated 'master' one by one, than rebasing all of your topic branches.

David Kastrup· Oct 16, 2008, 13:59 UTC · re: Rick Moynihan · lore

Re: Rebasing Multiple branches at once...

Rick Moynihan <rick@calicojack.co.uk> writes:
Show 12 quoted lines
> Hi,
>
> I have a master branch, a dev branch and a number of feature branches
> from dev.  And I was wondering if there was an easy way to rebase dev
> and all of it's sub-branches onto master.
>
> I know I can run this as a series of commands, and use --onto to do
> this, but was wondering if there was an easier way.  As running:
>
> git rebase master
>
> when on the dev branch only rebases dev and not it's dependent branches.

Rebasing has no relation to dependent branches. It creates a new branch from the branch point. After it finishes, it just reseats HEAD of the branch to the new one. There is no operation that would work implicitly on originally dependent branches.

-- 
David Kastrup
Rick Moynihan· Oct 16, 2008, 14:57 UTC · re: David Kastrup · lore

Re: Rebasing Multiple branches at once...

David Kastrup wrote:
Show 19 quoted lines
> Rick Moynihan <rick@calicojack.co.uk> writes:
> 
>> Hi,
>>
>> I have a master branch, a dev branch and a number of feature branches
>> from dev.  And I was wondering if there was an easy way to rebase dev
>> and all of it's sub-branches onto master.
>>
>> I know I can run this as a series of commands, and use --onto to do
>> this, but was wondering if there was an easier way.  As running:
>>
>> git rebase master
>>
>> when on the dev branch only rebases dev and not it's dependent branches.
> 
> Rebasing has no relation to dependent branches.  It creates a new branch
> from the branch point.  After it finishes, it just reseats HEAD of the
> branch to the new one.  There is no operation that would work implicitly
> on originally dependent branches.

This appears to be true of the current implementation, but shouldn't it be possible to do this as a single operation?

e.g. when the situation is this with dev being the current branch.
o---o---o---o---o  master
      \
       o---o---o---o---o  dev (*)
                        \
                         o---o---o  topic
Running the hypothetical command:
git rebase master --all
Would produce this:
o---o---o---o---o  master
                  \
                   o---o---o---o---o  dev (*)
                                    \
                                     o---o---o  topic

I think this can be performed right now with a rebase followed by a rebase --onto

I can see how if there were conflicts in the rebase from dev, then you would need to resolve them all the way up your topic branches also. Is there anything else that makes this a bad idea?

R.
Robin Burchell· Oct 16, 2008, 15:02 UTC · re: Rick Moynihan · lore

Re: Rebasing Multiple branches at once...

On Thu, Oct 16, 2008 at 3:57 PM, Rick Moynihan <rick@calicojack.co.uk> wrote:
Show 32 quoted lines
>
> This appears to be true of the current implementation, but shouldn't it be
> possible to do this as a single operation?
>
> e.g. when the situation is this with dev being the current branch.
>
> o---o---o---o---o  master
>     \
>      o---o---o---o---o  dev (*)
>                       \
>                        o---o---o  topic
>
> Running the hypothetical command:
>
> git rebase master --all
>
> Would produce this:
>
> o---o---o---o---o  master
>                 \
>                  o---o---o---o---o  dev (*)
>                                   \
>                                    o---o---o  topic
>
> I think this can be performed right now with a rebase followed by a rebase
> --onto
>
> I can see how if there were conflicts in the rebase from dev, then you would
> need to resolve them all the way up your topic branches also.  Is there
> anything else that makes this a bad idea?
>
> R.

Rebase is indeed useful IMHO in situations like this with multiple related topic branches when needing to pull a single fix or two from somewhere without messy merges (especially when that will end up with rather a lot of merge commits in history - one or so for each branch, which is not exactly desirable).

(resent after I learned how to use 'reply to all', sorry Rick)
Toby Allsopp· Oct 16, 2008, 20:27 UTC · re: Rick Moynihan · lore

Re: Rebasing Multiple branches at once...

On Fri, Oct 17 2008, Rick Moynihan wrote:
Show 13 quoted lines
> Hi,
>
> I have a master branch, a dev branch and a number of feature branches
> from dev.  And I was wondering if there was an easy way to rebase dev
> and all of it's sub-branches onto master.
>
> I know I can run this as a series of commands, and use --onto to do
> this, but was wondering if there was an easier way.  As running:
>
> git rebase master
>
> when on the dev branch only rebases dev and not it's dependent
> branches.

I have a Perl script I use to rebase a number of topic branches as the remote tracking branches they're based on move. It handles the case of topic based on other topics. It is designed specifically for my workflow, which is tracking a central Subversion repository using git-svn, but I don't think it relies on using git-svn. Anyway, you might find it useful for inspiration.

The script outputs a sequence of commands and leaves the running of them up to you because you may need to resolve conflicts at any point.

Regards, Toby.

#!/usr/bin/perl

# Rebases master and everything based on master to the new trunk. Use # after a git-svn-fetch.

use strict; use warnings;

use Getopt::Long; use List::Util qw(first);

use Git;
my $dry_run;
GetOptions("dry-run|n" => \$dry_run)
  or die "usage error";
sub ref2branch {
    my $ref = shift;
    $ref =~ s,^refs/heads/,,
      or die "Not a branch: '$ref'";
    return $ref;
}
my $repo = Git->repository();

my %remotes_by_name; my %remotes_by_hash; my %remote_revs;

for ($repo->command('for-each-ref', 'refs/remotes')) {
    my ($hash, undef, $ref) = split;
    $remotes_by_name{$ref} = $hash;
    $remotes_by_hash{$hash} = $ref;
    $remote_revs{$ref} = [$repo->command('rev-list', $ref)];
}

my %heads_by_name; my %heads_by_hash;

for ($repo->command('for-each-ref', 'refs/heads')) {
    my ($hash, undef, $ref) = split;
    $heads_by_name{$ref} = $hash;
    $heads_by_hash{$hash} = $ref;
}

my %roots; my %heads_by_parent;

for my $head (sort keys %heads_by_name) {
    #print STDERR "Considering $head\n";
    my $parent;
    my $last_rev;
    for my $rev ($repo->command('rev-list', $head, '--not', keys %remotes_by_name)) {
        my $maybe_parent = $heads_by_hash{$rev};
        if ($maybe_parent && $maybe_parent ne $head) {
            #print STDERR "  found parent $maybe_parent\n";
            $parent = $maybe_parent;
            last;
        }
        $last_rev = $rev;
    }
    if ($parent) {
        push @{$heads_by_parent{$parent}}, $head;
    } elsif ($last_rev) {
        my $remote_base = $repo->command_oneline('rev-parse', "$last_rev^");
        my @remotes;
        #print STDERR "  last rev $last_rev $remote_base\n";
        for my $remote_name (sort keys %remotes_by_name) {
            my $remote = first { $_ eq $remote_base } @{$remote_revs{$remote_name}};
            if (defined($remote) && $remote eq $remote_base) {
                #print STDERR "  found remote $remote_name\n";
                push @remotes, $remote_name;
            }
        }
        if (@remotes == 1) {
            $roots{$head} = $remotes[0];
        } else {
            print STDERR "WARNING: Not exactly one candidate remote for $head: ",
                join(' ', @remotes), "\n";
        }
    }
}
for my $root (sort keys %roots) {
    my $remote = $roots{$root};
    my $short_root = ref2branch($root);
    $remote =~ s,^refs/,,;
    print "git rebase $remote $short_root\n";
    rebase_tree($root);
}
sub rebase_tree {
    my ($parent) = @_;
    for my $head (@{$heads_by_parent{$parent}}) {
        my $short_parent = ref2branch($parent);
        my $short_head = ref2branch($head);
        print "git rebase --onto $short_parent $heads_by_name{$parent} $short_head\n";
        rebase_tree($head);
    }
}

← back to recent threads