#!/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); } }