in git-add--interactive.perl [1488:1788]
sub patch_update_file {
my $quit = 0;
my ($ix, $num);
my $path = shift;
my ($head, @hunk) = parse_diff($path);
($head, my $mode, my $deletion, my $addition) = parse_diff_header($head);
for (@{$head->{DISPLAY}}) {
print;
}
if (@{$mode->{TEXT}}) {
unshift @hunk, $mode;
}
if (@{$deletion->{TEXT}}) {
foreach my $hunk (@hunk) {
push @{$deletion->{TEXT}}, @{$hunk->{TEXT}};
push @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};
}
@hunk = ($deletion);
}
$num = scalar @hunk;
$ix = 0;
while (1) {
my ($prev, $next, $other, $undecided, $i);
$other = '';
last if ($ix and !$num);
if ($num <= $ix) {
$ix = 0;
}
for ($i = 0; $i < $ix; $i++) {
if (!defined $hunk[$i]{USE}) {
$prev = 1;
$other .= ',k';
last;
}
}
if ($ix) {
$other .= ',K';
}
for ($i = $ix + 1; $i < $num; $i++) {
if (!defined $hunk[$i]{USE}) {
$next = 1;
$other .= ',j';
last;
}
}
if ($ix < $num - 1) {
$other .= ',J';
}
if ($num > 1) {
$other .= ',g,/';
}
for ($i = 0; $i < $num; $i++) {
if (!defined $hunk[$i]{USE}) {
$undecided = 1;
last;
}
}
last if (!$undecided && ($num || !$addition));
if ($num) {
if ($hunk[$ix]{TYPE} eq 'hunk' &&
hunk_splittable($hunk[$ix]{TEXT})) {
$other .= ',s';
}
if ($hunk[$ix]{TYPE} eq 'hunk') {
$other .= ',e';
}
for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
}
my $type = $num ? $hunk[$ix]{TYPE} : $head->{TYPE};
print colored $prompt_color, "(", ($ix+1), "/", ($num ? $num : 1), ") ",
sprintf(__($patch_update_prompt_modes{$patch_mode}{$type}), $other);
my $line = prompt_single_character;
last unless defined $line;
if ($line) {
if ($line =~ /^y/i) {
if ($num) {
$hunk[$ix]{USE} = 1;
} else {
$head->{USE} = 1;
}
}
elsif ($line =~ /^n/i) {
if ($num) {
$hunk[$ix]{USE} = 0;
} else {
$head->{USE} = 0;
}
}
elsif ($line =~ /^a/i) {
if ($num) {
while ($ix < $num) {
if (!defined $hunk[$ix]{USE}) {
$hunk[$ix]{USE} = 1;
}
$ix++;
}
} else {
$head->{USE} = 1;
$ix++;
}
next;
}
elsif ($line =~ /^g(.*)/) {
my $response = $1;
unless ($other =~ /g/) {
error_msg __("No other hunks to goto\n");
next;
}
my $no = $ix > 10 ? $ix - 10 : 0;
while ($response eq '') {
$no = display_hunks(\@hunk, $no);
if ($no < $num) {
print __("go to which hunk (<ret> to see more)? ");
} else {
print __("go to which hunk? ");
}
$response = <STDIN>;
if (!defined $response) {
$response = '';
}
chomp $response;
}
if ($response !~ /^\s*\d+\s*$/) {
error_msg sprintf(__("Invalid number: '%s'\n"),
$response);
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
error_msg sprintf(__n("Sorry, only %d hunk available.\n",
"Sorry, only %d hunks available.\n", $num), $num);
}
next;
}
elsif ($line =~ /^d/i) {
if ($num) {
while ($ix < $num) {
if (!defined $hunk[$ix]{USE}) {
$hunk[$ix]{USE} = 0;
}
$ix++;
}
} else {
$head->{USE} = 0;
$ix++;
}
next;
}
elsif ($line =~ /^q/i) {
if ($num) {
for ($i = 0; $i < $num; $i++) {
if (!defined $hunk[$i]{USE}) {
$hunk[$i]{USE} = 0;
}
}
} elsif (!defined $head->{USE}) {
$head->{USE} = 0;
}
$quit = 1;
last;
}
elsif ($line =~ m|^/(.*)|) {
my $regex = $1;
unless ($other =~ m|/|) {
error_msg __("No other hunks to search\n");
next;
}
if ($regex eq "") {
print colored $prompt_color, __("search for regex? ");
$regex = <STDIN>;
if (defined $regex) {
chomp $regex;
}
}
my $search_string;
eval {
$search_string = qr{$regex}m;
};
if ($@) {
my ($err,$exp) = ($@, $1);
$err =~ s/ at .*git-add--interactive line \d+, <STDIN> line \d+.*$//;
error_msg sprintf(__("Malformed search regexp %s: %s\n"), $exp, $err);
next;
}
my $iy = $ix;
while (1) {
my $text = join ("", @{$hunk[$iy]{TEXT}});
last if ($text =~ $search_string);
$iy++;
$iy = 0 if ($iy >= $num);
if ($ix == $iy) {
error_msg __("No hunk matches the given pattern\n");
last;
}
}
$ix = $iy;
next;
}
elsif ($line =~ /^K/) {
if ($other =~ /K/) {
$ix--;
}
else {
error_msg __("No previous hunk\n");
}
next;
}
elsif ($line =~ /^J/) {
if ($other =~ /J/) {
$ix++;
}
else {
error_msg __("No next hunk\n");
}
next;
}
elsif ($line =~ /^k/) {
if ($other =~ /k/) {
while (1) {
$ix--;
last if (!$ix ||
!defined $hunk[$ix]{USE});
}
}
else {
error_msg __("No previous hunk\n");
}
next;
}
elsif ($line =~ /^j/) {
if ($other !~ /j/) {
error_msg __("No next hunk\n");
next;
}
}
elsif ($line =~ /^s/) {
unless ($other =~ /s/) {
error_msg __("Sorry, cannot split this hunk\n");
next;
}
my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
if (1 < @split) {
print colored $header_color, sprintf(
__n("Split into %d hunk.\n",
"Split into %d hunks.\n",
scalar(@split)), scalar(@split));
}
splice (@hunk, $ix, 1, @split);
$num = scalar @hunk;
next;
}
elsif ($line =~ /^e/) {
unless ($other =~ /e/) {
error_msg __("Sorry, cannot edit this hunk\n");
next;
}
my $newhunk = edit_hunk_loop($head, \@hunk, $ix);
if (defined $newhunk) {
splice @hunk, $ix, 1, $newhunk;
}
}
else {
help_patch_cmd($other);
next;
}
# soft increment
while (1) {
$ix++;
last if ($ix >= $num ||
!defined $hunk[$ix]{USE});
}
}
}
@hunk = coalesce_overlapping_hunks(@hunk) if ($num);
my $n_lofs = 0;
my @result = ();
for (@hunk) {
if ($_->{USE}) {
push @result, @{$_->{TEXT}};
}
}
if (@result or $head->{USE}) {
my @patch = reassemble_patch($head->{TEXT}, @result);
my $apply_routine = $patch_mode_flavour{APPLY};
&$apply_routine(@patch);
refresh();
}
print "\n";
return $quit;
}