UWC: do not lose description for graduated topics
[git/spearce.git] / git-topic.perl
blobf875d0d5d81c8390e95ff534f104a9e04043c76b
1 #!/usr/bin/perl -w
3 # Copyright (c) 2006 Junio C Hamano
6 use strict;
7 use Getopt::Long;
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;
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;
26 if (@custom_stage) { @stage = @custom_stage; }
27 if (@custom_mark) { @mark = @custom_mark; }
28 my @nomerges = $merges ? qw(--no-merges) : ();
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, $_;
39 close(REVS);
40 return @revs;
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];
54 close(REVS);
55 return @revs;
58 sub rebase_marker {
59 my ($topic, $stage, $in_next) = @_;
60 my @not_in_topic = read_revs_short('^master', "^$topic", "$stage");
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 ' ';
70 if (read_revs_short('master', "^$topic")) {
71 # there is something that is in master but not in topic.
72 return '^';
74 # topic is up to date.
75 return '*';
78 sub describe_topic {
79 my ($topic) = @_;
80 open(CONF, '-|', qw(git repo-config --get),
81 "branch.$topic.description")
82 or die;
83 my $it = join('',<CONF>);
84 close(CONF);
85 chomp($it);
86 if ($it) {
87 wrap_print(" $it");
91 sub wrap_print {
92 my ($string) = @_;
93 format STDOUT =
94 ~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
95 $string
96 ~~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
97 $string
99 write;
102 open(TOPIC, '-|', qw(git for-each-ref),
103 '--sort=-authordate',
104 '--format=%(objectname) %(authordate) %(refname)',
105 "refs/heads/$topic_pattern")
106 or die;
108 my @in_next = read_revs_short('^master', $stage[0]);
110 while (<TOPIC>) {
111 chomp;
112 my ($sha1, $date, $topic) = m|^([0-9a-f]{40})\s(.*?)\srefs/heads/(.+)$|
113 or next;
114 my @revs = read_revs($base, $sha1, (1<<@stage)-1);
115 next unless (@revs || $all);
117 my %revs = map { $_->[0] => $_ } @revs; # fast index
118 for (my $i = 0; $i < @stage; $i++) {
119 for my $item (read_revs_short("^$stage[$i]", $sha1)) {
120 if (exists $revs{$item}) {
121 $revs{$item}[2] &= ~(1 << $i);
126 print '*' . rebase_marker($sha1, $stage[0], \@in_next);
127 my $count = "";
128 if (1 < @revs) {
129 $count = " " . (scalar @revs) . " commits";
131 elsif (@revs) {
132 $count = " 1 commit";
134 print " $topic ($date)$count\n";
135 describe_topic($topic);
136 for my $item (@revs) {
137 my $mark = $item->[2];
138 if ($mark < @mark) {
139 $mark = $mark[$mark];
141 wrap_print("$mark $item->[1]");