]> de.git.xonotic.org Git - xonotic/div0-gittools.git/blobdiff - git-branch-manager
when rejecting/unrejecting, say so in the commit message
[xonotic/div0-gittools.git] / git-branch-manager
index f551155ccbadc7e12982cc340cc5418085197fcd..5e771ccd4e420740519e0e735122dc5709af0a78 100755 (executable)
@@ -9,15 +9,31 @@ my %color =
        '' => "\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",
@@ -47,16 +63,20 @@ sub run(@)
 }
 
 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 '')
@@ -64,6 +84,9 @@ 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($)
@@ -83,6 +106,98 @@ 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\n$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\n$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) = @_;
@@ -121,13 +236,18 @@ sub merge_commit($)
        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: $!";
        }
 }
 
@@ -143,15 +263,12 @@ sub unmerge_commit($)
                $logcache = undef;
                my $msg = backtick 'git', 'log', '-1', '--pretty=fuller', $r
                        or die "git-log: $!";
-               my $cmsg = "";
-               my $author = "";
-               my $email = "";
-               my $date = "";
                for(split /\n/, $msg)
                {
-                       if(/^Author:\s*(.*)/)
+                       if(/^Author:\s*(.*) <(.*)>/)
                        {
                                $author = $1;
+                               $email = $2;
                        }
                        elsif(/^AuthorDate:\s*(.*)/)
                        {
@@ -164,7 +281,7 @@ sub unmerge_commit($)
                }
                open my $fh, '>', '.commitmsg'
                        or die ">.commitmsg: $!";
-               print $fh "UNMERGE\n$cmsg" . "::stable-branch::merge=$r\n"
+               print $fh "UNMERGE\n$cmsg" . "::stable-branch::unmerge=$r\n"
                        or die ">.commitmsg: $!";
                close $fh
                        or die ">.commitmsg: $!";
@@ -172,13 +289,18 @@ sub unmerge_commit($)
        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: $!";
        }
 }
 
@@ -194,7 +316,11 @@ sub rebase_log($$)
 
        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}[$_]];
                }
@@ -202,10 +328,14 @@ sub rebase_log($$)
 
        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}[$_]];
@@ -261,7 +391,7 @@ sub parse_log()
        }
        $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+)/))
                {
@@ -292,6 +422,14 @@ sub parse_log()
                {
                        push @logdata, ['merge', $1];
                }
+               elsif($data =~ /::stable-branch::reject=(\S+)/)
+               {
+                       push @logdata, ['reject', $1];
+               }
+               elsif($data =~ /::stable-branch::unreject=(\S+)/)
+               {
+                       push @logdata, ['unreject', $1];
+               }
                elsif($data =~ /::stable-branch::reset=(\S+)/)
                {
                        @logdata = ();
@@ -335,6 +473,14 @@ sub parse_log()
                        {
                                $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
@@ -426,7 +572,7 @@ sub run_script(@)
                        {
                                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};
                        }
@@ -438,12 +584,36 @@ sub run_script(@)
                        {
                                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')
                {
                }
@@ -471,7 +641,7 @@ sub opt_rebase($$)
                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";
@@ -496,7 +666,19 @@ sub opt_rebase($$)
        };
 }
 
+sub escapeHTML {
+         my ($toencode,$newlinestoo) = @_;
+         return undef unless defined($toencode);
+         $toencode =~ s{&}{&amp;}gso;
+         $toencode =~ s{<}{&lt;}gso;
+         $toencode =~ s{>}{&gt;}gso;
+        $toencode =~ s{"}{&quot;}gso;
+         return $toencode;
+}
+
+
 my $histsize = 20;
+my $cgi_url = undef;
 sub opt_list($$)
 {
        ++$done;
@@ -541,13 +723,28 @@ sub opt_list($$)
                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;
+               }
        }
 }
 
@@ -595,12 +792,16 @@ my $result = GetOptions(
        "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)
 {