Here's a patch to implement bug dependencies. The patch adds two new fields to the summary files, Blocks and Blocked-By. This information (should be) kept in sync between blocker and blockee, so only one summary file needs to be read to determine what bugs a given bug is blocking, and which ones are blocking it.
The control interface gets a new "blocks" command, which can be used to tell which bugs a given bug blocks, which is modeled on the tags command. So: blocks 10 19 18 17 blocks 10 - 19 blocks 10 = 11 Bugs cannot be merged unless their Blocks and Blocks-By fields are in the same state. I hope that won't turn out to be *too* annoying. The alternatives I considered seemed to lead to some bad cases. Limitations include: - does not check for cycles - only adds blocking data to bug report pages, not index pages I've tested this lightly, and I doubt that any coding bugs will express themselves outside of, at worst, bad data in the new fields, so I hope this will an easy patch to accept. -- see shy jo
Index: cgi/bugreport.cgi =================================================================== RCS file: /cvs/debbugs/source/cgi/bugreport.cgi,v retrieving revision 1.65 diff -u -r1.65 bugreport.cgi --- cgi/bugreport.cgi 1 Jun 2004 00:41:26 -0000 1.65 +++ cgi/bugreport.cgi 16 Mar 2005 02:12:25 -0000 @@ -196,6 +196,24 @@ . "</strong>" if length($status{tags}); +my @blockedby= split(/ /, $status{blockedby}); +if (@blockedby && $status{"pending"} ne 'fixed' && ! length($status{done})) { + for my $b (@blockedby) { + my %s = %{getbugstatus($b)}; + next if $s{"pending"} eq 'fixed' || length $s{done}; + push @descstates, "fix blocked by <a href=\"" . bugurl($b) . "\">#$b</a>: ".htmlsanit($s{subject}); + } +} + +my @blocks= split(/ /, $status{blocks}); +if (@blocks && $status{"pending"} ne 'fixed' && ! length($status{done})) { + for my $b (@blocks) { + my %s = %{getbugstatus($b)}; + next if $s{"pending"} eq 'fixed' || length $s{done}; + push @descstates, "blocking fix for <a href=\"" . bugurl($b) . "\">#$b</a>: ".htmlsanit($s{subject}); + } +} + my @merged= split(/ /,$status{mergedwith}); if (@merged) { my $descmerged = 'merged with '; Index: html/server-control.html.in =================================================================== RCS file: /cvs/debbugs/source/html/server-control.html.in,v retrieving revision 1.14 diff -u -r1.14 server-control.html.in --- html/server-control.html.in 11 Jun 2004 16:45:45 -0000 1.14 +++ html/server-control.html.in 16 Mar 2005 02:12:26 -0000 @@ -219,6 +219,20 @@ <p>For <a href="Developer.html#tags">their meanings</a> please consult the general developers' documentation for the $gBug system. +<dt><code>blocks</code> <var>bugnumber</var> [ <code>+</code> | <code>-</code> | <code>=</code> ] <var>bug</var> [ <var>bug</var> ... ] + + <dd>Use to note that one bug blocks another bug from being fixed. + The first listed bug is the blocker, and it is followed by the bug or bugs + that it blocks. Like the tags command, the list of bugs can be preceded by + <code>+</code>, <code>-</code>, or <code>=</code> to add, remove, or set + afresh the list of blocked bugs. The default is adding. + + <p>Example usage:</p> + + <pre> + # indicates that 7890 cannot be fixed until 123456 is fixed + blocks 123456 7890 + </pre> <dt><code>close</code> <var>bugnumber</var> (deprecated) <dd>Close $gBug report #<var>bugnumber</var>. Index: html/server-refcard.html.in =================================================================== RCS file: /cvs/debbugs/source/html/server-refcard.html.in,v retrieving revision 1.5 diff -u -r1.5 server-refcard.html.in --- html/server-refcard.html.in 12 Sep 2003 22:27:19 -0000 1.5 +++ html/server-refcard.html.in 16 Mar 2005 02:12:26 -0000 @@ -70,6 +70,8 @@ <li><code>retitle</code> <var>bugnumber</var> <var>new-title</var> <li><code>merge</code> <var>bugnumber</var> <var>bugnumber</var> ... <li><code>unmerge</code> <var>bugnumber</var> + <li><code>tags</code> <var>bugnumber</var> [+-=] <var>tag</var> ... + <li><code>blocks</code> <var>bugnumber</var> [+-=] <var>bugnumber</var> ... </ul> <p><code>reopen</code> with <code>=</code> or no originator address leaves Index: scripts/errorlib.in =================================================================== RCS file: /cvs/debbugs/source/scripts/errorlib.in,v retrieving revision 1.41 diff -u -r1.41 errorlib.in --- scripts/errorlib.in 15 Feb 2004 16:12:00 -0000 1.41 +++ scripts/errorlib.in 16 Mar 2005 02:12:26 -0000 @@ -84,6 +84,8 @@ mergedwith => 'merged-with', severity => 'severity', owner => 'owner', + blocks => 'blocks', + blockedby => 'blocked-by', ); sub readbug { Index: scripts/service.in =================================================================== RCS file: /cvs/debbugs/source/scripts/service.in,v retrieving revision 1.98 diff -u -r1.98 service.in --- scripts/service.in 15 Mar 2005 21:40:41 -0000 1.98 +++ scripts/service.in 16 Mar 2005 02:12:28 -0000 @@ -491,6 +491,100 @@ $data->{keywords} =~ s/\s*$//; } while (&getnextbug); } + } elsif (m/^blocks?\s+\#?(-?\d+)\s+(([=+-])\s*)?(\S.*)?$/i) { + $ok++; + my $bugnum = $1; $addsubcode = $3; $blocks = $4; + $addsub = "add"; + if (defined $addsubcode) { + $addsub = "sub" if ($addsubcode eq "-"); + $addsub = "add" if ($addsubcode eq "+"); + $addsub = "set" if ($addsubcode eq "="); + } + + my @okayblocks; + my @badblocks; + foreach my $b (split /[\s,]+/, $blocks) { + $b=~s/^\#//; + if ($b=~/[0-9]+/) { + $ref=$b; + if (&getbug) { + push @okayblocks, $b; + &cancelbug; # done checking the bug exists + } + else { + ¬foundbug; + push @badblcoks, $b; + } + } + else { + push @badblcoks, $b; + } + } + if (@badblocks) { + &transcript("Unknown blocked bug/s: ".join(', ', @badbugs).".\n"); + } + + $ref=$bugnum; + if (&setbug) { + if ($data->{blocks} eq '') { + &transcript("Was not blocking any bugs.\n"); + } else { + &transcript("Was blocking: $data->{blocks}\n"); + } + if ($addsub eq "set") { + $action= "Blocked bugs set to: " . join(", ", @okayblocks); + } elsif ($addsub eq "add") { + $action= "Blocked bugs added: " . join(", ", @okayblocks); + } elsif ($addsub eq "sub") { + $action= "Blocked bugs removed: " . join(", ", @okayblocks); + } + my %removedblocks; + my %addedblocks; + do { + &addmaintainers($data); + my @oldblocklist = split ' ', $data->{blocks}; + $data->{blocks} = '' if ($addsub eq "set"); + foreach my $b (@okayblocks) { + $data->{blocks} = join ' ', grep $_ ne $b, + split ' ', $data->{blocks}; + $data->{blocks} = "$b $data->{blocks}" unless $addsub eq "sub"; + } + $data->{blocks} =~ s/\s*$//; + + foreach my $b (@oldblocklist) { + if (! grep { $_ eq $b } split ' ', $data->{blocks}) { + push @{$removedblocks{$b}}, $ref; + } + } + foreach my $b (split ' ', $data->{blocks}) { + if (! grep { $_ eq $b } @oldblocklist) { + push @{$addedblocks{$b}}, $ref; + } + } + } while (&getnextbug); + + # Now that the blocks data is updated, change blocked-by data + # to match the changes. + foreach $ref (keys %addedblocks) { + if (&getbug) { + foreach my $b (@{$addedblocks{$ref}}) { + $data->{blockedby} = join ' ', grep $_ ne $b, + split ' ', $data->{blockedby}; + $data->{blockedby} = "$b $data->{blockedby}"; + } + &savebug; + } + } + foreach $ref (keys %removedblocks) { + if (&getbug) { + foreach my $b (@{$addedblocks{$ref}}) { + $data->{blockedby} = join ' ', grep $_ ne $b, + split ' ', $data->{blockedby}; + } + &savebug; + } + } + } } elsif (m/^retitle\s+\#?(-?\d+)\s+(\S.*\S)\s*$/i) { $ok++; $ref= $1; $newtitle= $2; @@ -551,6 +645,8 @@ &checkmatch('forwarded addr','m_forwarded',$data->{forwarded}); $data->{severity} = '$gDefaultSeverity' if $data->{severity} eq ''; &checkmatch('severity','m_severity',$data->{severity}); + &checkmatch('blocks','m_blocks',$data->{blocks}); + &checkmatch('blocked-by','m_blocked-by',$data->{blockedby}); &checkmatch('done mark','m_done',length($data->{done}) ? 'done' : 'open'); &checkmatch('owner','m_owner',$data->{owner}); foreach my $t (split /\s+/, $data->{keywords}) { $tags{$t} = 1; }
signature.asc
Description: Digital signature