Update MaintNotes in preparation for 1.5.4
[git] / git-topic.perl
1 #!/usr/bin/perl -w
2 #
3 # Copyright (c) 2006 Junio C Hamano
4 #
5
6 use strict;
7 use Getopt::Long;
8
9 my $topic_pattern = '??*/*';
10 my $base = 'next';
11 my @stage = qw(next pu);
12 my @mark = ('.', '?', '-', '+');
13 my $all = 0;
14 my $merges = 0;
15
16 my @custom_stage;
17 my @custom_mark;
18 GetOptions("topic=s" => \$topic_pattern,
19            "base=s" => \$base,
20            "stage=s" => \@custom_stage,
21            "mark=s" => \@custom_mark,
22            "merges!" => \$merges,
23            "all!" => \$all)
24     or die;
25
26 if (@custom_stage) { @stage = @custom_stage; }
27 if (@custom_mark) { @mark = @custom_mark; }
28 my @nomerges = $merges ? qw(--no-merges) : ();
29
30 sub read_revs_short {
31         my (@args) = @_;
32         my @revs;
33         open(REVS, '-|', qw(git rev-list), @nomerges, @args)
34             or die;
35         while (<REVS>) {
36                 chomp;
37                 push @revs, $_;
38         }
39         close(REVS);
40         return @revs;
41 }
42
43 sub read_revs {
44         my ($bottom, $top, $mask) = @_;
45         my @revs;
46         open(REVS, '-|', qw(git rev-list --pretty=oneline), @nomerges,
47              "$bottom..$top")
48             or die;
49         while (<REVS>) {
50                 chomp;
51                 my ($sha1, $topic) = /^([0-9a-f]{40}) (.*)$/;
52                 push @revs, [$sha1, $topic, $mask];
53         }
54         close(REVS);
55         return @revs;
56 }
57
58 sub rebase_marker {
59         my ($topic, $stage, $in_next) = @_;
60         my @not_in_topic = read_revs_short('^master', "^$topic", "$stage");
61
62         # @$in_next is what is in $stage but not in $base.
63         # @not_in_topic excludes what came from $topic from @$in_next.
64         # $topic can be rebased if these two set matches, because
65         # no commits in $topic has been merged to $stage yet.
66         if (@not_in_topic != @$in_next) {
67                 # we cannot rebase it anymore
68                 return ' ';
69         }
70         if (read_revs_short('master', "^$topic")) {
71                 # there is something that is in master but not in topic.
72                 return '^';
73         }
74         # topic is up to date.
75         return '*';
76 }
77
78 sub describe_topic {
79         my ($topic) = @_;
80
81         open(CONF, '-|', qw(git repo-config --get),
82              "branch.$topic.description")
83             or die;
84         my $it = join('',<CONF>);
85         close(CONF);
86         chomp($it);
87         if ($it) {
88                 wrap_print("  $it");
89         }
90 }
91
92 my @in_next = read_revs_short('^master', $stage[0]);
93
94 open(TOPIC, '-|', qw(git for-each-ref),
95     '--sort=-authordate',
96     '--format=%(objectname) %(authordate) %(refname)',
97     "refs/heads/$topic_pattern")
98     or die;
99
100 my @topic = ();
101 while (<TOPIC>) {
102         chomp;
103         my ($sha1, $date, $topic) = m|^([0-9a-f]{40})\s(.*?)\srefs/heads/(.+)$|
104             or next;
105         push @topic, [$sha1, $date, $topic];
106 }
107
108 my @last_merge_to_next = ();
109
110 for (@topic) {
111         my ($sha1, $date, $topic) = @$_;
112         my @revs = read_revs($base, $sha1, (1<<@stage)-1);
113         next unless (@revs || $all);
114
115         my %revs = map { $_->[0] => $_ } @revs; # fast index
116         for (my $i = 0; $i < @stage; $i++) {
117                 for my $item (read_revs_short("^$stage[$i]", $sha1)) {
118                         if (exists $revs{$item}) {
119                                 $revs{$item}[2] &= ~(1 << $i);
120                         }
121                 }
122         }
123
124         print '*' . rebase_marker($sha1, $stage[0], \@in_next);
125         my $count = "";
126         if (1 < @revs) {
127                 $count = " " . (scalar @revs) . " commits";
128         }
129         elsif (@revs) {
130                 $count = " 1 commit";
131         }
132         print " $topic ($date)$count\n";
133         describe_topic($topic);
134         for my $item (@revs) {
135                 my $mark = $item->[2];
136                 if ($mark < @mark) {
137                         $mark = $mark[$mark];
138                 }
139                 wrap_print("$mark $item->[1]");
140         }
141 }
142
143 sub wrap_print {
144         my ($string) = @_;
145         format STDOUT =
146 ~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
147         $string
148  ~~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
149         $string
150 .
151         write;
152 }