]> git.donarmstrong.com Git - debbugs.git/blobdiff - Debbugs/Control.pm
update debugging functions
[debbugs.git] / Debbugs / Control.pm
index e5a1dd24b2d0ce8e05d2dbec026574dba02d3b96..80e0c24156687296948ef68ac197f438f4c9d376 100644 (file)
@@ -96,6 +96,7 @@ BEGIN{
                     block   => [qw(set_blocks)],
                     merge   => [qw(set_merged)],
                     tag     => [qw(set_tag)],
+                    clone   => [qw(clone_bug)],
                     archive => [qw(bug_archive bug_unarchive),
                                ],
                     log     => [qw(append_action_to_log),
@@ -108,7 +109,7 @@ BEGIN{
 
 use Debbugs::Config qw(:config);
 use Debbugs::Common qw(:lock buglog :misc get_hashname sort_versions);
-use Debbugs::Status qw(bug_archiveable :read :hook writebug splitpackages split_status_fields get_bug_status);
+use Debbugs::Status qw(bug_archiveable :read :hook writebug new_bug splitpackages split_status_fields get_bug_status);
 use Debbugs::CGI qw(html_escape);
 use Debbugs::Log qw(:misc :write);
 use Debbugs::Recipients qw(:add);
@@ -117,6 +118,7 @@ use Debbugs::Packages qw(:versions :mapping);
 use Data::Dumper qw();
 use Params::Validate qw(validate_with :types);
 use File::Path qw(mkpath);
+use File::Copy qw(copy);
 use IO::File;
 
 use Debbugs::Text qw(:templates);
@@ -897,6 +899,24 @@ sub set_done {
                $warn_fixed = 0;
            }
        }
+       $action = "Bug reopened";
+       for my $data (@data) {
+           my $old_data = dclone($data);
+           $data->{done} = '';
+           append_action_to_log(bug => $data->{bug_num},
+                                command => 'done',
+                                new_data => $data,
+                                old_data => $old_data,
+                                get_lock => 0,
+                                __return_append_to_log_options(
+                                                               %param,
+                                                               action => $action,
+                                                              ),
+                               )
+               if not exists $param{append_log} or $param{append_log};
+           writebug($data->{bug_num},$data);
+       }
+       print {$transcript} "$action\n";
        __end_control(%info);
        if (exists $param{submitter}) {
            set_submitter(bug => $param{bug},
@@ -921,6 +941,15 @@ sub set_done {
        my %submitter_notified;
        my $requester_notified = 0;
        my $orig_report_set = 0;
+       for my $data (@data) {
+           if (exists $data->{done} and
+               defined $data->{done} and
+               length $data->{done}) {
+               print {$transcript} "Bug $data->{bug_num} is already marked as done; not doing anything.\n";
+               __end_control(%info);
+               return;
+           }
+       }
        for my $data (@data) {
            my $old_data = dclone($data);
            my $hash = get_hashname($data->{bug_num});
@@ -939,13 +968,6 @@ sub set_done {
                $orig_report_set = 1;
            }
 
-           if (exists $data->{done} and
-               defined $data->{done} and
-               length $data->{done}) {
-               print {$transcript} "Bug $data->{bug_num} is already marked as done; not doing anything.\n";
-               __end_control(%info);
-               return;
-           }
            $action = "Marked $config{bug} as done";
 
            # set done to the requester
@@ -962,6 +984,7 @@ sub set_done {
                                )
                if not exists $param{append_log} or $param{append_log};
            writebug($data->{bug_num},$data);
+           print {$transcript} "$action\n";
            # get the original report
            if ($param{notify_submitter}) {
                my $submitter_message;
@@ -1010,6 +1033,7 @@ sub set_done {
                                    );
            }
        }
+       __end_control(%info);
        if (exists $param{fixed}) {
            set_fixed(fixed => $param{fixed},
                      bug => $param{bug},
@@ -2051,7 +2075,7 @@ sub set_merged {
            # figure out the problems
            print {$transcript} "Unable to merge bugs because:\n";
            for my $change (@{$disallowed_changes}) {
-               print {$transcript} "$change->{field} of #$change->{bug} is '$change->{orig_value}' not '$change->{value}'\n";
+               print {$transcript} "$change->{field} of #$change->{bug} is '$change->{text_orig_value}' not '$change->{text_value}'\n";
            }
            if ($attempts > 0) {
                croak "Some bugs were altered while attempting to merge";
@@ -2115,8 +2139,8 @@ sub set_merged {
        $locks += $n_locks;
        %data = %{$data};
        @data = values %data;
-       ($merge_status,$bugs_to_merge) =
-           __calculate_merge_status(\@data,\%data,$param{bug});
+       ($merge_status,$bugs_to_merge) =
+           __calculate_merge_status(\@data,\%data,$param{bug},$merge_status);
        ($disallowed_changes,$changes) = 
            __calculate_merge_changes(\@data,$merge_status,\%param);
        $attempts = max(values %bug_changed);
@@ -2167,8 +2191,9 @@ sub set_merged {
 sub __allow_relocking{
     my ($locks,$bugs) = @_;
 
-    for my $bug (@{$bugs}) {
-       my @lockfiles = grep {m{/\Q$bug\E$}} keys %{$locks->{locks}};
+    my @locks = (@{$bugs},'merge');
+    for my $lock (@locks) {
+       my @lockfiles = grep {m{/\Q$lock\E$}} keys %{$locks->{locks}};
        next unless @lockfiles;
        $locks->{relockable}{$lockfiles[0]} = 0;
     }
@@ -2249,8 +2274,8 @@ sub __lock_and_load_merged_bugs{
 
 
 sub __calculate_merge_status{
-    my ($data_a,$data_h,$master_bug,$merge) = @_;
-    my %merge_status;
+    my ($data_a,$data_h,$master_bug,$merge_status) = @_;
+    my %merge_status = %{$merge_status // {}};
     my %merged_bugs;
     my $bugs_to_merge = 0;
     for my $data (@{$data_a}) {
@@ -2268,9 +2293,9 @@ sub __calculate_merge_status{
                $merge_status{$_} = $data->{$_}
            }
        }
-       if (not $merge) {
-           next unless $data->{bug_num} == $master_bug;
-       }
+       if (defined $merge_status) {
+           next unless $data->{bug_num} == $master_bug;
+       }
        $merge_status{tag} = {} if not exists $merge_status{tag};
        for my $tag (split /\s+/, $data->{keywords}) {
            $merge_status{tag}{$tag} = 1;
@@ -2349,10 +2374,12 @@ sub __calculate_merge_changes{
                           },
             fixed_versions => {func => \&set_fixed,
                                key => 'fixed',
+                               modify_value => sub {(defined $_[0] and ref($_[0]) eq 'HASH')?[sort keys %{$_[0]}]:$_[0]},
                                allowed => 1,
                               },
             found_versions => {func => \&set_found,
                                key   => 'found',
+                               modify_value => sub {(defined $_[0] and ref($_[0]) eq 'HASH')?[sort keys %{$_[0]}]:$_[0]},
                                allowed => 1,
                               },
            );
@@ -2382,10 +2409,12 @@ sub __calculate_merge_changes{
                 function => $force_functions{$field}{func},
                 key      => $force_functions{$field}{key},
                 options  => $force_functions{$field}{options},
-                allowed  => exists $force_functions{$field}{allowed} ? 0 : $force_functions{$field}{allowed},
+                allowed  => exists $force_functions{$field}{allowed} ? $force_functions{$field}{allowed} : 0,
                };
-           if ($param->{force}) {
-               if ($field ne 'package') {
+           $change->{text_value} = ref($change->{func_value}) eq 'ARRAY'?join(' ',@{$change->{func_value}}):$change->{func_value};
+           $change->{text_orig_value} = ref($change->{orig_value}) eq 'ARRAY'?join(' ',@{$change->{orig_value}}):$change->{orig_value};
+           if ($param->{force} or $change->{allowed}) {
+               if ($field ne 'package' or $change->{allowed}) {
                    push @{$changes{$data->{bug_num}}},$change;
                    next;
                }
@@ -2734,6 +2763,138 @@ sub summary {
 
 
 
+=head2 clone_bug
+
+     eval {
+           clone_bug(bug          => $ref,
+                     transcript   => $transcript,
+                     ($dl > 0 ? (debug => $transcript):()),
+                     requester    => $header{from},
+                     request_addr => $controlrequestaddr,
+                     message      => \@log,
+                      affected_packages => \%affected_packages,
+                     recipients   => \%recipients,
+                    );
+       };
+       if ($@) {
+           $errors++;
+           print {$transcript} "Failed to clone bug $ref bar: $@";
+       }
+
+Clones the given bug.
+
+We currently don't support cloning merged bugs, but this could be
+handled by internally unmerging, cloning, then remerging the bugs.
+
+=cut
+
+sub clone_bug {
+    my %param = validate_with(params => \@_,
+                             spec   => {bug => {type   => SCALAR,
+                                                regex  => qr/^\d+$/,
+                                               },
+                                        new_bugs => {type => ARRAYREF,
+                                                    },
+                                        new_clones => {type => HASHREF,
+                                                       default => {},
+                                                      },
+                                        %common_options,
+                                        %append_action_options,
+                                       },
+                            );
+    my %info =
+       __begin_control(%param,
+                       command  => 'clone'
+                      );
+    my ($debug,$transcript) =
+       @info{qw(debug transcript)};
+    my @data = @{$info{data}};
+    my @bugs = @{$info{bugs}};
+
+    my $action = '';
+    for my $data (@data) {
+       if (length($data->{mergedwith})) {
+           die "Bug is marked as being merged with others. Use an existing clone.\n";
+       }
+    }
+    if (@data != 1) {
+       die "Not exactly one bug‽ This shouldn't happen.";
+    }
+    my $data = $data[0];
+    my %clones;
+    for my $newclone_id (@{$param{new_bugs}}) {
+       my $new_bug_num = new_bug(copy => $data->{bug_num});
+       $param{new_clones}{$newclone_id} = $new_bug_num;
+       $clones{$newclone_id} = $new_bug_num;
+    }
+    my @new_bugs = sort values %clones;
+    my @collapsed_ids;
+    for my $new_bug (@new_bugs) {
+       # no collapsed ids or the higher collapsed id is not one less
+       # than the next highest new bug
+       if (not @collapsed_ids or 
+           $collapsed_ids[-1][1]+1 != $new_bug) {
+           push @collapsed_ids,[$new_bug,$new_bug];
+       }
+       else {
+           $collapsed_ids[-1][1] = $new_bug;
+       }
+    }
+    my @collapsed;
+    for my $ci (@collapsed_ids) {
+       if ($ci->[0] == $ci->[1]) {
+           push @collapsed,$ci->[0];
+       }
+       else {
+           push @collapsed,$ci->[0].'-'.$ci->[1]
+       }
+    }
+    my $collapsed_str = english_join(\@collapsed);
+    $action = "Bug $data->{bug_num} cloned as bug".(@new_bugs > 1?'s':'').' '.$collapsed_str;
+    for my $new_bug (@new_bugs) {
+       append_action_to_log(bug => $new_bug,
+                            get_lock => 1,
+                            __return_append_to_log_options(
+                                                           %param,
+                                                           action => $action,
+                                                          ),
+                           )
+           if not exists $param{append_log} or $param{append_log};
+    }
+    append_action_to_log(bug => $data->{bug_num},
+                        get_lock => 0,
+                        __return_append_to_log_options(
+                                                       %param,
+                                                       action => $action,
+                                                      ),
+                       )
+       if not exists $param{append_log} or $param{append_log};
+    writebug($data->{bug_num},$data);
+    print {$transcript} "$action\n";
+    __end_control(%info);
+    # bugs that this bug is blocking are also blocked by the new clone(s)
+    for my $bug (split ' ', $data->{blocks}) {
+       for my $new_bug (@new_bugs) {
+           set_blocks(bug => $new_bug,
+                      blocks => $bug,
+                      hash_slice(%param,
+                                 keys %common_options,
+                                 keys %append_action_options),
+                     );
+       }
+    }
+    # bugs that this bug is blocked by are also blocking the new clone(s)
+    for my $bug (split ' ', $data->{blockedby}) {
+       for my $new_bug (@new_bugs) {
+           set_blocks(bug => $bug,
+                      blocks => $new_bug,
+                      hash_slice(%param,
+                                 keys %common_options,
+                                 keys %append_action_options),
+                     );
+       }
+    }
+}
 
 
 
@@ -2899,7 +3060,6 @@ sub bug_archive {
          print {$transcript} "Bug $param{bug} cannot be archived\n";
          die "Bug $param{bug} cannot be archived";
      }
-     print {$debug} "$param{bug} considering\n";
      if (not $param{archive_unarchived} and
         not exists $data[0]{unarchived}
        ) {
@@ -2955,7 +3115,7 @@ sub bug_archive {
               print {$transcript} "archived $bug to archive/$dir (from $param{bug})\n";
          }
          unlink(map {"$config{spool_dir}/db-h/$dir/$_"} @files_to_remove);
-         print {$transcript} "deleted $bug (from $param{bug})\n";
+         print {$debug} "deleted $bug (from $param{bug})\n";
      }
      bughook_archive(@bugs);
      __end_control(%info);
@@ -3413,7 +3573,8 @@ sub __begin_control {
                             );
     my $new_locks;
     my ($debug,$transcript) = __handle_debug_transcript(@_);
-    print {$debug} "$param{bug} considering\n";
+    print {$debug} "considering bug $param{bug} for ".(exists $param{command}?$param{command}:scalar caller())."\n";
+    print {$debug} Data::Dumper->Dump([caller(1),\%param],[qw(caller param)])."\n";
     $lockhash = $param{locks} if exists $param{locks};
     my @data = ();
     my $old_die = $SIG{__DIE__};