Line # Revision Author
1 106 dpavlin #!/usr/bin/perl -w
2
3 # cherry-pick merge multiple changes from one subversion directory tree to
4 # another inside same repository
5 #
6 # 2005-01-01 Dobrica Pavlinusic <dpavlin@rot13.org> original shell version
7 # 2005-04-10 rewritten in perl, become really cherry-pick like
8
9 use strict;
10 use XML::Simple;
11 use Data::Dumper;
12
13 my ($from, $to, $rev) = @ARGV;
14
15 die "usage: $0 from_svn_path to_svn_path start_rev\n" unless ($from and $to and $rev);
16
17 ###
18 ### subs
19 ###
20
21 sub cmd($) {
22 my $cmd = shift;
23 107 dpavlin print "## $cmd\n";
24 106 dpavlin open(my $fh, '-|', $cmd . ' 2>&1') or die "failed to execute $cmd: $?";
25 my $out;
26 while(<$fh>) {
27 $out .= $_;
28 }
29 close($fh) || die "can't close $cmd: $!";
30 return $out;
31 }
32
33
34 sub cmd_regex($$) {
35 my ($cmd, $regex) = @_;
36
37 my $out = cmd($cmd);
38 return unless ($out);
39 return $out =~ $regex;
40 }
41
42 sub svn_update($) {
43 my $path = shift;
44
45 my $rev = cmd_regex("cd $path && svn update", qr/revision\s+(\d+)/);
46 return $rev;
47 }
48
49 my %rev2i;
50
51 sub svn_log {
52 my $path = shift;
53
54 my $cmd = "svn log -v ".join(" ",@_)." $path";
55 print "## $cmd\n";
56
57 open(my $fh, '-|', $cmd) || die "svn log failed: $!";
58 my @log_arr;
59 my $log;
60 while(<$fh>) {
61 if (/^-+$/) {
62 if ($log) {
63 $rev2i{$1} = @log_arr if ($log =~ m/^r(\d+)\s/);
64 push @log_arr, $log;
65 }
66 undef $log;
67 } else {
68 $log .= $_;
69 }
70 }
71 close($fh) || die "can't close log: $!";
72 return @log_arr;
73 }
74
75 sub readln {
76 my $msg = shift;
77 my $default = shift;
78 print "${msg}: " if ($msg);
79 my $tmp = <STDIN>;
80 chomp($tmp);
81 $tmp ||= $default;
82 return $tmp;
83 }
84
85 ###
86 ###
87 ###
88
89 #my $from_rev = svn_update($from);
90 #my $to_rev = svn_update($to);
91 #die "src/dest revisions not same: ${from_rev}:${to_rev}\n" unless ($from_rev == $to_rev);
92
93 my @src_log = svn_log($from, '-r '.$rev.':HEAD');
94 my $editor = $ENV{'EDITOR'} || 'vi';
95
96 # move away old log file
97 rename 'log', 'log.old' if -e ('log');
98
99 # position in changelog
100 my $i = 0;
101
102 my $l = 'foo';
103
104 107 dpavlin sub inc_i { if ($i < (@src_log-1) ) { $i++; } else { print "!! end of changelog\n"; } }
105 106 dpavlin sub dec_i { if ($i > 0) { $i--; } else { print "!! allready on first revision!\n"; } }
106
107 my %applied;
108
109 while ($l && $l !~ m/^q/) {
110
111 # commit message
112 my $log = $src_log[$i] || die "no log message $i";
113 # revision
114 my $r = $1 if ($log =~ m/^r(\d+)\s/);
115 print "-" x 76, "\n$log\n";
116
117 my $un = '';
118 $un = 'un' if ($r && $applied{$r});
119
120 107 dpavlin my $l = readln("# ".(join(",",sort keys %applied) || 'none')." [${un}apply/diff/commit/msg/list/revert/+-]",'');
121 106 dpavlin print "\n";
122
123 if ($l =~ m/^a/i) {
124 print cmd("svn merge -r ".($r-1).":${r} $from $to");
125
126 open(LOG, '>>', 'log') || die "can't open log: $!";
127 print LOG $log;
128 close(LOG);
129
130 $applied{$r} = $i;
131
132 inc_i();
133 } elsif ($l =~ m/^u/i) {
134 print cmd("svn merge -r ${r}:".($r-1)." $from $to");
135 delete($applied{$r});
136 inc_i();
137 } elsif ($l =~ m/^d/i) {
138 107 dpavlin system "cd $to && svn status && svn diff | $editor -R -";
139 106 dpavlin } elsif ($l =~ m/^m/i) {
140 system "$editor log" or warn "editor: $?";
141 } elsif ($l =~ m/^c/i) {
142 107 dpavlin open(LOG, '>>', 'log') || die "can't append changed files";
143 print LOG "--This line, and those below, will be ignored--\n\n";
144 system "svn status -q $to >> log && $editor log && svn commit -F log $to && mv log log.commited";
145 %applied = ();
146 } elsif ($l =~ m/^[\+>]/i) {
147 106 dpavlin inc_i();
148 107 dpavlin } elsif ($l =~ m/^[\-<]/i) {
149 106 dpavlin dec_i();
150 } elsif ($l =~ m/^l/i) {
151 my $sep = "-" x 76;
152 $sep .= "\n";
153 my $log = join($sep, @src_log);
154 if (open(my $vim, "|-", $editor." -R -")) {
155 print $vim $log;
156 close($vim);
157 } else {
158 print $log;
159 }
160 } elsif ($l =~ m/^r*(\d+)$/) {
161 if (defined($rev2i{$1})) {
162 $i = $rev2i{$1};
163 } else {
164 print "!! can't find revision $1\n";
165 }
166 107 dpavlin } elsif ($l =~ m/^R/) {
167 %applied = ();
168 system "svn status -q $to | cut -c7- | xargs -i svn revert {}";
169 106 dpavlin }
170 }
171