'' => "\e[m",
'outstanding' => "\e[1;33m",
'unmerge' => "\e[1;31m",
+ 'reject' => "\e[31m",
+ 'unreject' => "\e[31m",
'merge' => "\e[32m",
'base' => "\e[1;34m",
'previous' => "\e[34m",
);
+my %html_style =
+(
+ '' => "color: black; background-color: black",
+ 'outstanding' => "color: black; background-color: yellow",
+ 'unmerge' => "color: black; background-color: lightred",
+ 'reject' => "color: black; background-color: red",
+ 'unreject' => "color: black; background-color: red",
+ 'merge' => "color: black; background-color: green",
+ 'base' => "color: black; background-color: lightblue",
+ 'previous' => "color: black; background-color: blue",
+);
+
my %name =
(
'outstanding' => "OUTSTANDING",
'unmerge' => "UNMERGED",
+ 'reject' => "REJECTED",
+ 'unreject' => "UNREJECTED",
'merge' => "MERGED",
'base' => "BASE",
'previous' => "PREVIOUS",
}
my $width = ($ENV{COLUMNS} || backtick 'tput', 'cols' || 80);
-chomp(my $branch = backtick 'git', 'symbolic-ref', 'HEAD');
- $branch =~ s/^refs\/heads\///
- or die "Not in a branch";
+my $branch = $ENV{GIT_BRANCH};
+if(not $branch)
+{
+ chomp($branch = backtick 'git', 'symbolic-ref', 'HEAD');
+ $branch =~ s/^refs\/heads\///
+ or die "Not in a branch";
+}
chomp(my $master = (backtick 'git', 'config', '--get', "branch-manager.$branch.master" or 'master'));
chomp(my $datefilter = (backtick 'git', 'config', '--get', "branch-manager.$branch.startdate" or ''));
my @datefilter = ();
my $revprefix = "";
if($datefilter eq 'mergebase')
{
- chomp($revprefix = check_defined "git-merge-base: $!", backtick 'git', 'merge-base', $master, "HEAD");
+ chomp($revprefix = check_defined "git-merge-base: $!", backtick 'git', 'merge-base', $master, $branch);
$revprefix .= "^..";
}
elsif($datefilter ne '')
@datefilter = "--since=$datefilter";
}
+# if set, don't actually merge/revert changes, just mark as such
+my $skip = 0;
+
our $do_commit = 1;
my $logcache = undef;
sub reset_to_commit($)
}
}
+sub reject_commit($)
+{
+ # reject == merge but skip
+ my ($r) = @_;
+ my $cmsg = "";
+ my $author = "";
+ my $email = "";
+ my $date = "";
+ if($do_commit)
+ {
+ $logcache = undef;
+ my $msg = backtick 'git', 'log', '-1', '--pretty=fuller', $r
+ or die "git-log: $!";
+ for(split /\n/, $msg)
+ {
+ if(/^Author:\s*(.*) <(.*)>/)
+ {
+ $author = $1;
+ $email = $2;
+ }
+ elsif(/^AuthorDate:\s*(.*)/)
+ {
+ $date = $1;
+ }
+ elsif(/^ (.*)/)
+ {
+ $cmsg .= "$1\n";
+ }
+ }
+ open my $fh, '>', '.commitmsg'
+ or die ">.commitmsg: $!";
+ print $fh "REJECT! $cmsg" . "::stable-branch::reject=$r\n"
+ or die ">.commitmsg: $!";
+ close $fh
+ or die ">.commitmsg: $!";
+ }
+ local $ENV{GIT_AUTHOR_NAME} = $author;
+ local $ENV{GIT_AUTHOR_EMAIL} = $email;
+ local $ENV{GIT_AUTHOR_DATE} = $date;
+ if($do_commit)
+ {
+ run 'git', 'commit', '--allow-empty', '-F', '.commitmsg'
+ or die "git-commit: $!";
+ }
+}
+
+sub unreject_commit($)
+{
+ # reject == merge but skip
+ my ($r) = @_;
+ my $cmsg = "";
+ my $author = "";
+ my $email = "";
+ my $date = "";
+ if($do_commit)
+ {
+ $logcache = undef;
+ my $msg = backtick 'git', 'log', '-1', '--pretty=fuller', $r
+ or die "git-log: $!";
+ for(split /\n/, $msg)
+ {
+ if(/^Author:\s*(.*) <(.*)>/)
+ {
+ $author = $1;
+ $email = $2;
+ }
+ elsif(/^AuthorDate:\s*(.*)/)
+ {
+ $date = $1;
+ }
+ elsif(/^ (.*)/)
+ {
+ $cmsg .= "$1\n";
+ }
+ }
+ open my $fh, '>', '.commitmsg'
+ or die ">.commitmsg: $!";
+ print $fh "UNREJECT! $cmsg" . "::stable-branch::unreject=$r\n"
+ or die ">.commitmsg: $!";
+ close $fh
+ or die ">.commitmsg: $!";
+ }
+ local $ENV{GIT_AUTHOR_NAME} = $author;
+ local $ENV{GIT_AUTHOR_EMAIL} = $email;
+ local $ENV{GIT_AUTHOR_DATE} = $date;
+ if($do_commit)
+ {
+ run 'git', 'commit', '--allow-empty', '-F', '.commitmsg'
+ or die "git-commit: $!";
+ }
+}
+
sub merge_commit($)
{
my ($r) = @_;
local $ENV{GIT_AUTHOR_NAME} = $author;
local $ENV{GIT_AUTHOR_EMAIL} = $email;
local $ENV{GIT_AUTHOR_DATE} = $date;
- run 'git', 'cherry-pick', '-n', $r
- or run 'git', 'mergetool'
- or die "git-mergetool: $!";
+ if(!$skip)
+ {
+ run 'git', 'cherry-pick', '-n', $r
+ or run 'git', 'mergetool'
+ or die "git-mergetool: $!";
+ }
if($do_commit)
{
- run 'git', 'commit', '-F', '.commitmsg'
- or die "git-commit: $!";
+ run 'git', 'commit', '--allow-empty', '-F', '.commitmsg'
+ or (run 'git', 'mergetool'
+ and run 'git', 'commit', '--allow-empty', '-F', '.commitmsg')
+ or die "git-commit: $!";
}
}
or die "git-log: $!";
for(split /\n/, $msg)
{
- if(/^Author:\s*(.*)/)
+ if(/^Author:\s*(.*) <(.*)>/)
{
$author = $1;
+ $email = $2;
}
elsif(/^AuthorDate:\s*(.*)/)
{
}
open my $fh, '>', '.commitmsg'
or die ">.commitmsg: $!";
- print $fh "UNMERGE\n$cmsg" . "::stable-branch::merge=$r\n"
+ print $fh "UNMERGE! $cmsg" . "::stable-branch::unmerge=$r\n"
or die ">.commitmsg: $!";
close $fh
or die ">.commitmsg: $!";
local $ENV{GIT_AUTHOR_NAME} = $author;
local $ENV{GIT_AUTHOR_EMAIL} = $email;
local $ENV{GIT_AUTHOR_DATE} = $date;
- run 'git', 'revert', '-n', $r
- or run 'git', 'mergetool'
- or die "git-mergetool: $!";
+ if(!$skip)
+ {
+ run 'git', 'revert', '-n', $r
+ or run 'git', 'mergetool'
+ or die "git-mergetool: $!";
+ }
if($do_commit)
{
- run 'git', 'commit', '-F', '.commitmsg'
- or die "git-commit: $!";
+ run 'git', 'commit', '--allow-empty', '-F', '.commitmsg'
+ or (run 'git', 'mergetool'
+ and run 'git', 'commit', '--allow-empty', '-F', '.commitmsg')
+ or die "git-commit: $!";
}
}
for(0..$newbase_id)
{
- if(!$log->{bitmap}[$_])
+ if($log->{bitmap}[$_] < 0)
+ {
+ unshift @rlog, ['reject', $log->{order_a}[$_]];
+ }
+ elsif($log->{bitmap}[$_] == 0)
{
unshift @rlog, ['unmerge', $log->{order_a}[$_]];
}
for($newbase_id+1 .. @{$log->{order_a}}-1)
{
- if($log->{bitmap}[$_])
+ if($log->{bitmap}[$_] > 0)
{
push @rlog, ['merge', $log->{order_a}[$_]];
}
+ elsif($log->{bitmap}[$_] < 0)
+ {
+ push @rlog, ['reject', $log->{order_a}[$_]];
+ }
else
{
push @outstanding, ['outstanding', $log->{order_a}[$_]];
}
$cur_commit = $cur_msg = undef;
my @commits = ();
- for((split /\n/, check_defined "git-log: $!", backtick 'git', 'log', '--topo-order', '--reverse', '--pretty=fuller', @datefilter, "$revprefix"."HEAD"), undef)
+ for((split /\n/, check_defined "git-log: $!", backtick 'git', 'log', '--topo-order', '--reverse', '--pretty=fuller', @datefilter, "$revprefix$branch"), undef)
{
if(defined $cur_commit and (not defined $_ or /^commit (\S+)/))
{
my $data = $logmsg{$_};
if($data =~ /::stable-branch::unmerge=(\S+)/)
{
+ next if not defined $history{$1};
push @logdata, ['unmerge', $1];
}
elsif($data =~ /::stable-branch::merge=(\S+)/)
{
+ next if not defined $history{$1};
push @logdata, ['merge', $1];
}
+ elsif($data =~ /::stable-branch::reject=(\S+)/)
+ {
+ next if not defined $history{$1};
+ push @logdata, ['reject', $1];
+ }
+ elsif($data =~ /::stable-branch::unreject=(\S+)/)
+ {
+ next if not defined $history{$1};
+ push @logdata, ['unreject', $1];
+ }
elsif($data =~ /::stable-branch::reset=(\S+)/)
{
+ next if not defined $history{$1};
@logdata = ();
$base = $1;
}
elsif($data =~ /::stable-branch::rebase=(\S+)/)
{
+ next if not defined $history{$1};
$lastrebase->[0] = 'ignore'
if defined $lastrebase;
push @logdata, ($lastrebase = ['rebase', $1]);
{
$bitmap[$history{$data}] = 0;
}
+ elsif($cmd eq 'reject')
+ {
+ $bitmap[$history{$data}] = -1;
+ }
+ elsif($cmd eq 'unreject')
+ {
+ $bitmap[$history{$data}] = 0;
+ }
elsif($cmd eq 'rebase')
{
# the bitmap is fine, but generate a new log from the bitmap
{
my $l = parse_log();
die "PEBKAC: invalid revision number, cannot reset"
- unless defined $l->{order_h}{$r} and not $l->{bitmap}[$l->{order_h}{$r}];
+ unless defined $l->{order_h}{$r} and $l->{bitmap}[$l->{order_h}{$r}] == 0;
die "PEBKAC: not initialized"
unless defined $l->{base};
}
{
my $l = parse_log();
die "PEBKAC: invalid revision number, cannot reset"
- unless defined $l->{order_h}{$r} and $l->{bitmap}[$l->{order_h}{$r}];
+ unless defined $l->{order_h}{$r} and $l->{bitmap}[$l->{order_h}{$r}] > 0;
die "PEBKAC: not initialized"
unless defined $l->{base};
}
unmerge_commit $r;
}
+ elsif($cmd eq 'reject')
+ {
+ if($pebkac)
+ {
+ my $l = parse_log();
+ die "PEBKAC: invalid revision number, cannot reset"
+ unless defined $l->{order_h}{$r} and $l->{bitmap}[$l->{order_h}{$r}] == 0;
+ die "PEBKAC: not initialized"
+ unless defined $l->{base};
+ }
+ reject_commit $r;
+ }
+ elsif($cmd eq 'unreject')
+ {
+ if($pebkac)
+ {
+ my $l = parse_log();
+ die "PEBKAC: invalid revision number, cannot reset"
+ unless defined $l->{order_h}{$r} and $l->{bitmap}[$l->{order_h}{$r}] < 0;
+ die "PEBKAC: not initialized"
+ unless defined $l->{base};
+ }
+ unreject_commit $r;
+ }
elsif($cmd eq 'outstanding')
{
}
die "PEBKAC: not initialized"
unless defined $l->{base};
}
- my $msg = backtick 'git', 'log', '-1', '--pretty=fuller', @datefilter, 'HEAD'
+ my $msg = backtick 'git', 'log', '-1', '--pretty=fuller', @datefilter, $branch
or die "git-log: $!";
$msg =~ /^commit (\S+)/s
or die "Invalid git log output";
};
}
+sub escapeHTML {
+ my ($toencode,$newlinestoo) = @_;
+ return undef unless defined($toencode);
+ $toencode =~ s{&}{&}gso;
+ $toencode =~ s{<}{<}gso;
+ $toencode =~ s{>}{>}gso;
+ $toencode =~ s{"}{"}gso;
+ return $toencode;
+}
+
+
my $histsize = 20;
+my $cgi_url = undef;
sub opt_list($$)
{
++$done;
my %seen = ();
@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);
}
- for(@l)
+ if(defined $cgi_url)
+ {
+ print "Content-Type: text/html\n\n<table border>\n";
+ for(@l)
+ {
+ my ($action, $r) = @$_;
+ my $m = $l->{logmsg}->{$r};
+ my $m_short = join ' ', map { s/^ (?!git-svn-id)(.)/$1/ ? $_ : () } split /\n/, $m;
+ 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);
+ }
+ print "</table>\n";
+ }
+ else
{
- my ($action, $r) = @$_;
- my $m = $l->{logmsg}->{$r};
- my $m_short = join ' ', map { s/^ (?!git-svn-id)(.)/$1/ ? $_ : () } split /\n/, $m;
- $m_short = substr $m_short, 0, $width - 11 - 1 - 40 - 1;
- printf "%s%-11s%s %s %s\n", $color{$action}, $name{$action}, $color{''}, $r, $m_short;
+ for(@l)
+ {
+ my ($action, $r) = @$_;
+ my $m = $l->{logmsg}->{$r};
+ my $m_short = join ' ', map { s/^ (?!git-svn-id)(.)/$1/ ? $_ : () } split /\n/, $m;
+ $m_short = substr $m_short, 0, $width - 11 - 1 - 40 - 1;
+ printf "%s%-11s%s %s %s\n", $color{$action}, $name{$action}, $color{''}, $r, $m_short;
+ }
}
}
"log|l:s", handler \&opt_list,
"outstanding|o:s", handler \&opt_list,
"rebase|b=s", handler \&opt_rebase,
+ "skip", handler \$skip,
"merge|m=s{,}", handler sub { run_script ['merge', $_[1]]; },
"unmerge|u=s{,}", handler sub { run_script ['unmerge', $_[1]]; },
+ "reject|r=s{,}", handler sub { run_script ['reject', $_[1]]; },
+ "unreject|U=s{,}", handler sub { run_script ['unreject', $_[1]]; },
"reset|R=s", handler sub { run_script ['reset', $_[1]]; },
"hardreset|H=s", handler sub { run_script ['hardreset', $_[1]]; },
"help|h", handler \&opt_help,
- "histsize|s=i", \$histsize
+ "histsize|s=i", \$histsize,
+ "cgi=s", \$cgi_url
);
if(!$done)
{