5 use Getopt::Long qw/:config no_ignore_case no_auto_abbrev gnu_compat/;
10 'outstanding' => "\e[1;33m",
11 'unmerge' => "\e[1;31m",
14 'previous' => "\e[34m",
19 '' => "color: black; background-color: black",
20 'outstanding' => "color: black; background-color: yellow",
21 'unmerge' => "color: black; background-color: red",
22 'merge' => "color: black; background-color: green",
23 'base' => "color: black; background-color: lightblue",
24 'previous' => "color: black; background-color: blue",
29 'outstanding' => "OUTSTANDING",
30 'unmerge' => "UNMERGED",
33 'previous' => "PREVIOUS",
38 my ($msg, $data) = @_;
39 return $data if defined $data;
59 my $width = ($ENV{COLUMNS} || backtick 'tput', 'cols' || 80);
60 my $branch = $ENV{GIT_BRANCH};
63 chomp($branch = backtick 'git', 'symbolic-ref', 'HEAD');
64 $branch =~ s/^refs\/heads\///
65 or die "Not in a branch";
67 chomp(my $master = (backtick 'git', 'config', '--get', "branch-manager.$branch.master" or 'master'));
68 chomp(my $datefilter = (backtick 'git', 'config', '--get', "branch-manager.$branch.startdate" or ''));
71 if($datefilter eq 'mergebase')
73 chomp($revprefix = check_defined "git-merge-base: $!", backtick 'git', 'merge-base', $master, $branch);
76 elsif($datefilter ne '')
78 @datefilter = "--since=$datefilter";
81 # if set, don't actually merge/revert changes, just mark as such
86 sub reset_to_commit($)
89 #run 'git', 'merge', '-s', 'ours', '--no-commit', $r
90 # or die "git-merge: $!";
91 run 'git', 'checkout', $r, '--', '.'
92 or die "git-checkout: $!";
96 run 'git', 'update-ref', 'MERGE_HEAD', $r
97 or die "git-update-ref: $!";
98 run 'git', 'commit', '--allow-empty', '-m', "::stable-branch::reset=$r"
99 or die "git-commit: $!";
113 my $msg = backtick 'git', 'log', '-1', '--pretty=fuller', $r
114 or die "git-log: $!";
115 for(split /\n/, $msg)
117 if(/^Author:\s*(.*) <(.*)>/)
122 elsif(/^AuthorDate:\s*(.*)/)
131 open my $fh, '>', '.commitmsg'
132 or die ">.commitmsg: $!";
133 print $fh "$cmsg" . "::stable-branch::merge=$r\n"
134 or die ">.commitmsg: $!";
136 or die ">.commitmsg: $!";
138 local $ENV{GIT_AUTHOR_NAME} = $author;
139 local $ENV{GIT_AUTHOR_EMAIL} = $email;
140 local $ENV{GIT_AUTHOR_DATE} = $date;
143 run 'git', 'cherry-pick', '-n', $r
144 or run 'git', 'mergetool'
145 or die "git-mergetool: $!";
149 run 'git', 'commit', '-F', '.commitmsg'
150 or (run 'git', 'mergetool'
151 and run 'git', 'commit', '-F', '.commitmsg')
152 or die "git-commit: $!";
156 sub unmerge_commit($)
166 my $msg = backtick 'git', 'log', '-1', '--pretty=fuller', $r
167 or die "git-log: $!";
168 for(split /\n/, $msg)
170 if(/^Author:\s*(.*)/)
174 elsif(/^AuthorDate:\s*(.*)/)
183 open my $fh, '>', '.commitmsg'
184 or die ">.commitmsg: $!";
185 print $fh "UNMERGE\n$cmsg" . "::stable-branch::unmerge=$r\n"
186 or die ">.commitmsg: $!";
188 or die ">.commitmsg: $!";
190 local $ENV{GIT_AUTHOR_NAME} = $author;
191 local $ENV{GIT_AUTHOR_EMAIL} = $email;
192 local $ENV{GIT_AUTHOR_DATE} = $date;
195 run 'git', 'revert', '-n', $r
196 or run 'git', 'mergetool'
197 or die "git-mergetool: $!";
201 run 'git', 'commit', '-F', '.commitmsg'
202 or (run 'git', 'mergetool'
203 and run 'git', 'commit', '-F', '.commitmsg')
204 or die "git-commit: $!";
212 my @applied = (0) x @{$log->{order_a}};
213 my $newbase_id = $log->{order_h}{$r};
216 my @outstanding = ();
220 if(!$log->{bitmap}[$_])
222 unshift @rlog, ['unmerge', $log->{order_a}[$_]];
226 for($newbase_id+1 .. @{$log->{order_a}}-1)
228 if($log->{bitmap}[$_])
230 push @rlog, ['merge', $log->{order_a}[$_]];
234 push @outstanding, ['outstanding', $log->{order_a}[$_]];
251 return $logcache if defined $logcache;
263 my $cur_commit = undef;
265 for((split /\n/, check_defined "git-log: $!", backtick 'git', 'log', '--topo-order', '--reverse', '--pretty=fuller', @datefilter, "$revprefix$master"), undef)
267 if(defined $cur_commit and (not defined $_ or /^commit (\S+)/))
269 $cur_msg =~ s/\s+$//s;
270 $history{$cur_commit} = scalar @history;
271 $logmsg{$cur_commit} = $cur_msg;
272 push @history, $cur_commit;
273 $cur_commit = $cur_msg = undef;
275 last if not defined $_;
285 $cur_commit = $cur_msg = undef;
287 for((split /\n/, check_defined "git-log: $!", backtick 'git', 'log', '--topo-order', '--reverse', '--pretty=fuller', @datefilter, "$revprefix$branch"), undef)
289 if(defined $cur_commit and (not defined $_ or /^commit (\S+)/))
291 $cur_msg =~ s/\s+$//s;
292 $logmsg{$cur_commit} = $cur_msg;
293 push @commits, $cur_commit;
294 $cur_commit = $cur_msg = undef;
296 last if not defined $_;
306 my $lastrebase = undef;
309 my $data = $logmsg{$_};
310 if($data =~ /::stable-branch::unmerge=(\S+)/)
312 push @logdata, ['unmerge', $1];
314 elsif($data =~ /::stable-branch::merge=(\S+)/)
316 push @logdata, ['merge', $1];
318 elsif($data =~ /::stable-branch::reset=(\S+)/)
323 elsif($data =~ /::stable-branch::rebase=(\S+)/)
325 $lastrebase->[0] = 'ignore'
326 if defined $lastrebase;
327 push @logdata, ($lastrebase = ['rebase', $1]);
331 if(not defined $base)
333 warn 'This branch is not yet managed by git-branch-manager';
337 order_a => \@history,
338 order_h => \%history,
343 my $baseid = $history{$base};
352 my ($cmd, $data) = @{$logdata[$i]};
355 $bitmap[$history{$data}] = 1;
357 elsif($cmd eq 'unmerge')
359 $bitmap[$history{$data}] = 0;
361 elsif($cmd eq 'rebase')
363 # the bitmap is fine, but generate a new log from the bitmap
366 order_a => \@history,
367 order_h => \%history,
370 my $rebasedlog = rebase_log $data, $pseudolog;
371 my @l = grep { $_->[0] ne 'outstanding' } @{$rebasedlog->{log}};
372 splice @logdata, 0, $i+1, @l;
375 $baseid = $history{$base};
380 my @outstanding = ();
381 for($baseid+1 .. @history-1)
383 push @outstanding, ['outstanding', $history[$_]]
390 order_a => \@history,
391 order_h => \%history,
417 $r = backtick 'git', 'rev-parse', $r
418 or die "git-rev-parse: $!"
423 print "Executing: $cmd $r\n";
429 die "PEBKAC: invalid revision number, cannot reset"
430 unless defined $l->{order_h}{$r};
434 elsif($cmd eq 'hardreset')
439 die "PEBKAC: invalid revision number, cannot reset"
440 unless defined $l->{order_h}{$r};
442 run 'git', 'reset', '--hard', $r
443 or die "git-reset: $!";
446 elsif($cmd eq 'merge')
451 die "PEBKAC: invalid revision number, cannot reset"
452 unless defined $l->{order_h}{$r} and not $l->{bitmap}[$l->{order_h}{$r}];
453 die "PEBKAC: not initialized"
454 unless defined $l->{base};
458 elsif($cmd eq 'unmerge')
463 die "PEBKAC: invalid revision number, cannot reset"
464 unless defined $l->{order_h}{$r} and $l->{bitmap}[$l->{order_h}{$r}];
465 die "PEBKAC: not initialized"
466 unless defined $l->{base};
470 elsif($cmd eq 'outstanding')
475 die "Invalid command: $cmd $r";
486 $r = backtick 'git', 'rev-parse', $r
487 or die "git-rev-parse: $!"
492 die "PEBKAC: invalid revision number, cannot reset"
493 unless defined $l->{order_h}{$r};
494 die "PEBKAC: not initialized"
495 unless defined $l->{base};
497 my $msg = backtick 'git', 'log', '-1', '--pretty=fuller', @datefilter, $branch
498 or die "git-log: $!";
499 $msg =~ /^commit (\S+)/s
500 or die "Invalid git log output";
502 my $l = rebase_log $r, parse_log();
504 local $do_commit = 0;
508 run_script @{$l->{log}};
509 run 'git', 'commit', '--allow-empty', '-m', "::stable-branch::rebase=$r"
510 or die "git-commit: $!";
516 run 'git', 'reset', '--hard', $commit_id
517 or die "$err, and then git-reset failed: $!";
523 my ($toencode,$newlinestoo) = @_;
524 return undef unless defined($toencode);
525 $toencode =~ s{&}{&}gso;
526 $toencode =~ s{<}{<}gso;
527 $toencode =~ s{>}{>}gso;
528 $toencode =~ s{"}{"}gso;
539 $r = undef if $r eq '';
542 ($r = backtick 'git', 'rev-parse', $r
543 or die "git-rev-parse: $!")
548 die "PEBKAC: invalid revision number, cannot reset"
549 unless !defined $r or defined $l->{order_h}{$r};
550 die "PEBKAC: not initialized"
551 unless defined $l->{base};
554 $l = rebase_log $r, $l
556 my $last = $l->{order_h}{$l->{base}};
557 my $first = $last - $histsize;
566 (map { $seen{$l->{order_a}[$_]} ? () : ['previous', $l->{order_a}[$_]] } $first..($last-1)),
567 ['base', $l->{base}],
570 if($cmd eq 'chronology')
572 @l = map { [$_->[1], $_->[2]] } sort { $l->{order_h}{$a->[2]} <=> $l->{order_h}{$b->[2]} or $a->[0] <=> $b->[0] } map { [$_, $l[$_]->[0], $l[$_]->[1]] } 0..(@l-1);
574 elsif($cmd eq 'outstanding')
577 @l = reverse grep { !$seen{$_->[1]}++ && !$l->{bitmap}->[$l->{order_h}->{$_->[1]}] } reverse map { [$_->[1], $_->[2]] } sort { $l->{order_h}{$a->[2]} <=> $l->{order_h}{$b->[2]} or $a->[0] <=> $b->[0] } map { [$_, $l[$_]->[0], $l[$_]->[1]] } 0..(@l-1);
581 print "Content-Type: text/html\n\n<table border>\n";
584 my ($action, $r) = @$_;
585 my $m = $l->{logmsg}->{$r};
586 my $m_short = join ' ', map { s/^ (?!git-svn-id)(.)/$1/ ? $_ : () } split /\n/, $m;
587 printf "<tr style=\"%s\"><td>%s</td><td><a href=\"%s%s\">%s</a></td><td style=\"white-space: pre\">%s</td></tr>\n", $html_style{$action}, $name{$action}, escapeHTML($cgi_url), escapeHTML($r), escapeHTML($r), escapeHTML($m_short);
595 my ($action, $r) = @$_;
596 my $m = $l->{logmsg}->{$r};
597 my $m_short = join ' ', map { s/^ (?!git-svn-id)(.)/$1/ ? $_ : () } split /\n/, $m;
598 $m_short = substr $m_short, 0, $width - 11 - 1 - 40 - 1;
599 printf "%s%-11s%s %s %s\n", $color{$action}, $name{$action}, $color{''}, $r, $m_short;
606 my ($cmd, $one) = @_;
609 $0 [{--histsize|-s} n] {--chronology|-c}
610 $0 [{--histsize|-s} n] {--chronology|-c} revision-hash
611 $0 [{--histsize|-s} n] {--log|-l}
612 $0 [{--histsize|-s} n] {--log|-l} revision-hash
613 $0 {--merge|-m} revision-hash
614 $0 {--unmerge|-u} revision-hash
615 $0 {--reset|-R} revision-hash
616 $0 {--hardreset|-H} revision-hash
617 $0 {--rebase|-b} revision-hash
643 my $result = GetOptions(
644 "chronology|c:s", handler \&opt_list,
645 "log|l:s", handler \&opt_list,
646 "outstanding|o:s", handler \&opt_list,
647 "rebase|b=s", handler \&opt_rebase,
648 "skip", handler \$skip,
649 "merge|m=s{,}", handler sub { run_script ['merge', $_[1]]; },
650 "unmerge|u=s{,}", handler sub { run_script ['unmerge', $_[1]]; },
651 "reset|R=s", handler sub { run_script ['reset', $_[1]]; },
652 "hardreset|H=s", handler sub { run_script ['hardreset', $_[1]]; },
653 "help|h", handler \&opt_help,
654 "histsize|s=i", \$histsize,
659 opt_list("outstanding", "");