X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Debbugs%2FControl.pm;h=80e0c24156687296948ef68ac197f438f4c9d376;hb=eb202251624f2c08e2ead494710e696832f24f90;hp=e5a1dd24b2d0ce8e05d2dbec026574dba02d3b96;hpb=2d93ee4f41b008924b0de5e8eddc302a5a1267e7;p=debbugs.git diff --git a/Debbugs/Control.pm b/Debbugs/Control.pm index e5a1dd2..80e0c24 100644 --- a/Debbugs/Control.pm +++ b/Debbugs/Control.pm @@ -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__};