2 # usage: tb-list [<patch-spec>]
3 # lists all patches matching <patch-spec> and other criteria
11 #----- option parsing -----
13 Getopt::Long::Configure(qw(bundling));
22 GetOptions("d|deleted!" => \$deleted, # including deleted patches
23 "deleted-only!" => \$deleted_only, # only deleted patches
24 "r|related=s" => \$relatedto, # only patches related to this one
25 "l|last|leaf|leaves" => \$leaves, # only leaf patches
27 ) or die "bad options\n";
32 $spec = parse_patch_spec($ARGV[0]);
35 die "too many arguments\n";
38 our @sort = grep { /./ } split m/,/, $sort;
39 push @sort, 'topo' if !$spec;
40 push @sort, 'created';
42 foreach $sort (@sort) {
43 die "bad sort $sort\n" unless grep { $_ eq $sort }
44 qw(fullname created nick topo);
45 $toposort=1 if $sort eq $toposort;
48 $relatedto = $relatedto ? parse_patch_spec($relatedto) : undef;
50 #----- list patches -----
54 foreach_patch($relatedto || $leaves || !$spec ? { } : $spec,
55 $deleted || $deleted_only,
56 [0, !!$leaves, 0, $toposort || !!$relatedto],
58 my ($patch,$parsedname,@info) = @_;
59 $patches{$patch}{Info} = \@info;
60 $patches{$patch}{ParsedName} = $parsedname;
63 #----- selection -----
67 foreach my $p (keys %patches) {
70 next if $v->{Info}[0]{Deleted};
71 foreach my $dep (keys %{ $v->{Info}[1] }) {
72 debug("leaves $p $dep");
73 next unless exists $patches{$dep};
74 $patches{$dep}{NotLeaf} = 1;
80 foreach my $p (keys %patches) {
82 # mark Related=1 if any patch matching $relatedto includes us
83 foreach my $dep (keys %{ $v->{Info}[3] }) {
84 next unless exists $patches{$dep};
85 my $depv = $patches{$dep};
86 next unless patch_matches_spec($depv->{ParsedName}, $relatedto);
90 if (patch_matches_spec($v->{ParsedName}, $relatedto)) {
91 # if we match $relatedto, mark all our inclusions as Related=1
92 foreach my $dep (keys %{ $v->{Info}[3] }) {
93 next unless exists $patches{$dep};
94 $patches{$dep}{Related} = 1;
96 # oh, and mark ourselves as Related=1 too!
104 foreach my $p (keys %patches) {
105 my $v = $patches{$p};
106 next if !$deleted && $v->{Info}[0]{Deleted};
107 next if $deleted_only && !$v->{Info}[0]{Deleted};
108 next if $leaves && $v->{NotLeaf};
109 next if $relatedto && !$v->{Related};
110 next if $spec && !patch_matches_spec($v->{ParsedName}, $spec);
117 my $txt = "sub sort_cmp {\n my \$r;\n";
121 foreach my $ab (qw(a b)) {
122 $txt .= " my \$v$ab = \$patches{\$$ab};\n";
127 $txt .= " \$r = $_[0];\n return \$r if \$r;\n";
129 my $by_parsed = sub {
131 $by_r->("\$va->{ParsedName}{$_[0]} cmp \$vb->{ParsedName}{$_[0]}");
134 foreach my $sort (@sort) {
135 next if $done{$sort}++;
136 if ($sort eq 'fullname') {
137 $by_r->('$a cmp $b');
138 } elsif ($sort eq 'created') {
139 $by_parsed->('Date');
140 } elsif ($sort eq 'nick') {
141 $by_parsed->('Nick');
142 } elsif ($sort eq 'topo') {
144 foreach my $ix (qw(0 1)) {
145 my $ab = (qw(a b))[$ix];
146 my $ba = (qw(b a))[$ix];
147 my $r = (qw(-1 1))[$ix];
148 $txt .= " return $r if \$v${ab}->{Info}[3]{\$$ba};\n";
154 $txt .= " return 0;\n}\n";
155 debug("sortsub | $_") foreach split /\n/, $txt;
158 eval sortsub()." 1;" or die "$@ ?";
160 @output = sort sort_cmp @output;
162 #----- printing -----
164 my $current_branch = current_branch();
165 my $current_patch = $current_branch->{Kind} eq 'tip'
166 ? $current_branch->{Fullname} : '';
168 foreach my $p (@output) {
169 my $v = $patches{$p};
170 my $pa = $v->{ParsedName};
171 my ($msgkind, $msg) = git_get_object("$pa->{Ref}:.topbloke/msg");
172 die "$p $msgkind ?" unless $msgkind eq 'blob';
174 $msg =~ m/^Subject:\s*(?:\[[^][]*\]\s*)?(.*\S)\s*$/mi
175 ? $1 : "[no subject]";
176 printf("%1s%1s %s\@%s/%s/%-20s %s\n",
177 $p eq $current_patch ? '>' : '',
178 $v->{Info}[0]{Deleted} ? 'D' : '',
179 $pa->{Email}, $pa->{Domain}, $pa->{Date}, $pa->{Nick},