| Revision 107 (by dpavlin, 2005/04/10 20:50:32) |
better command output, added revert (R), move with < >
|
#!/usr/bin/perl -w
# cherry-pick merge multiple changes from one subversion directory tree to
# another inside same repository
#
# 2005-01-01 Dobrica Pavlinusic <dpavlin@rot13.org> original shell version
# 2005-04-10 rewritten in perl, become really cherry-pick like
use strict;
use XML::Simple;
use Data::Dumper;
my ($from, $to, $rev) = @ARGV;
die "usage: $0 from_svn_path to_svn_path start_rev\n" unless ($from and $to and $rev);
###
### subs
###
sub cmd($) {
my $cmd = shift;
print "## $cmd\n";
open(my $fh, '-|', $cmd . ' 2>&1') or die "failed to execute $cmd: $?";
my $out;
while(<$fh>) {
$out .= $_;
}
close($fh) || die "can't close $cmd: $!";
return $out;
}
sub cmd_regex($$) {
my ($cmd, $regex) = @_;
my $out = cmd($cmd);
return unless ($out);
return $out =~ $regex;
}
sub svn_update($) {
my $path = shift;
my $rev = cmd_regex("cd $path && svn update", qr/revision\s+(\d+)/);
return $rev;
}
my %rev2i;
sub svn_log {
my $path = shift;
my $cmd = "svn log -v ".join(" ",@_)." $path";
print "## $cmd\n";
open(my $fh, '-|', $cmd) || die "svn log failed: $!";
my @log_arr;
my $log;
while(<$fh>) {
if (/^-+$/) {
if ($log) {
$rev2i{$1} = @log_arr if ($log =~ m/^r(\d+)\s/);
push @log_arr, $log;
}
undef $log;
} else {
$log .= $_;
}
}
close($fh) || die "can't close log: $!";
return @log_arr;
}
sub readln {
my $msg = shift;
my $default = shift;
print "${msg}: " if ($msg);
my $tmp = <STDIN>;
chomp($tmp);
$tmp ||= $default;
return $tmp;
}
###
###
###
#my $from_rev = svn_update($from);
#my $to_rev = svn_update($to);
#die "src/dest revisions not same: ${from_rev}:${to_rev}\n" unless ($from_rev == $to_rev);
my @src_log = svn_log($from, '-r '.$rev.':HEAD');
my $editor = $ENV{'EDITOR'} || 'vi';
# move away old log file
rename 'log', 'log.old' if -e ('log');
# position in changelog
my $i = 0;
my $l = 'foo';
sub inc_i { if ($i < (@src_log-1) ) { $i++; } else { print "!! end of changelog\n"; } }
sub dec_i { if ($i > 0) { $i--; } else { print "!! allready on first revision!\n"; } }
my %applied;
while ($l && $l !~ m/^q/) {
# commit message
my $log = $src_log[$i] || die "no log message $i";
# revision
my $r = $1 if ($log =~ m/^r(\d+)\s/);
print "-" x 76, "\n$log\n";
my $un = '';
$un = 'un' if ($r && $applied{$r});
my $l = readln("# ".(join(",",sort keys %applied) || 'none')." [${un}apply/diff/commit/msg/list/revert/+-]",'');
print "\n";
if ($l =~ m/^a/i) {
print cmd("svn merge -r ".($r-1).":${r} $from $to");
open(LOG, '>>', 'log') || die "can't open log: $!";
print LOG $log;
close(LOG);
$applied{$r} = $i;
inc_i();
} elsif ($l =~ m/^u/i) {
print cmd("svn merge -r ${r}:".($r-1)." $from $to");
delete($applied{$r});
inc_i();
} elsif ($l =~ m/^d/i) {
system "cd $to && svn status && svn diff | $editor -R -";
} elsif ($l =~ m/^m/i) {
system "$editor log" or warn "editor: $?";
} elsif ($l =~ m/^c/i) {
open(LOG, '>>', 'log') || die "can't append changed files";
print LOG "--This line, and those below, will be ignored--\n\n";
system "svn status -q $to >> log && $editor log && svn commit -F log $to && mv log log.commited";
%applied = ();
} elsif ($l =~ m/^[\+>]/i) {
inc_i();
} elsif ($l =~ m/^[\-<]/i) {
dec_i();
} elsif ($l =~ m/^l/i) {
my $sep = "-" x 76;
$sep .= "\n";
my $log = join($sep, @src_log);
if (open(my $vim, "|-", $editor." -R -")) {
print $vim $log;
close($vim);
} else {
print $log;
}
} elsif ($l =~ m/^r*(\d+)$/) {
if (defined($rev2i{$1})) {
$i = $rev2i{$1};
} else {
print "!! can't find revision $1\n";
}
} elsif ($l =~ m/^R/) {
%applied = ();
system "svn status -q $to | cut -c7- | xargs -i svn revert {}";
}
}