Re: Rebasing Multiple branches at once...
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);
}
}