On Fri, Oct 17 2008, Rick Moynihan wrote: > 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); } }