X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=git-add--interactive.perl;h=da768ee7acc22e6480f0a067e109239561d43927;hb=d36f8679e94c2a0d4d15d6adcea434634af6d627;hp=a4234d39e94bc0fdf85ac93cfb696828d3644e5b;hpb=0beee4c6dec15292415e3d56075c16a76a22af54;p=git.git diff --git a/git-add--interactive.perl b/git-add--interactive.perl index a4234d39e..da768ee7a 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -18,6 +18,18 @@ my ($fraginfo_color) = $diff_use_color ? ( $repo->get_color('color.diff.frag', 'cyan'), ) : (); +my ($diff_plain_color) = + $diff_use_color ? ( + $repo->get_color('color.diff.plain', ''), + ) : (); +my ($diff_old_color) = + $diff_use_color ? ( + $repo->get_color('color.diff.old', 'red'), + ) : (); +my ($diff_new_color) = + $diff_use_color ? ( + $repo->get_color('color.diff.new', 'green'), + ) : (); my $normal_color = $repo->get_color("", "reset"); @@ -42,7 +54,7 @@ sub colored { my $patch_mode; sub run_cmd_pipe { - if ($^O eq 'MSWin32') { + if ($^O eq 'MSWin32' || $^O eq 'msys') { my @invalid = grep {m/[":*]/} @_; die "$^O does not support: @invalid\n" if @invalid; my @args = map { m/ /o ? "\"$_\"": $_ } @_; @@ -394,9 +406,9 @@ sub list_and_choose { if ($choice =~ s/^-//) { $choose = 0; } - # A range can be specified like 5-7 - if ($choice =~ /^(\d+)-(\d+)$/) { - ($bottom, $top) = ($1, $2); + # A range can be specified like 5-7 or 5-. + if ($choice =~ /^(\d+)-(\d*)$/) { + ($bottom, $top) = ($1, length($2) ? $2 : 1 + @stuff); } elsif ($choice =~ /^\d+$/) { $bottom = $top = $choice; @@ -683,6 +695,105 @@ sub split_hunk { } +sub color_diff { + return map { + colored((/^@/ ? $fraginfo_color : + /^\+/ ? $diff_new_color : + /^-/ ? $diff_old_color : + $diff_plain_color), + $_); + } @_; +} + +sub edit_hunk_manually { + my ($oldtext) = @_; + + my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff"; + my $fh; + open $fh, '>', $hunkfile + or die "failed to open hunk edit file for writing: " . $!; + print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n"; + print $fh @$oldtext; + print $fh <config("core.editor") + || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; + system('sh', '-c', $editor.' "$@"', $editor, $hunkfile); + + open $fh, '<', $hunkfile + or die "failed to open hunk edit file for reading: " . $!; + my @newtext = grep { !/^#/ } <$fh>; + close $fh; + unlink $hunkfile; + + # Abort if nothing remains + if (!grep { /\S/ } @newtext) { + return undef; + } + + # Reinsert the first hunk header if the user accidentally deleted it + if ($newtext[0] !~ /^@/) { + unshift @newtext, $oldtext->[0]; + } + return \@newtext; +} + +sub diff_applies { + my $fh; + open $fh, '| git apply --recount --cached --check'; + for my $h (@_) { + print $fh @{$h->{TEXT}}; + } + return close $fh; +} + +sub prompt_yesno { + my ($prompt) = @_; + while (1) { + print colored $prompt_color, $prompt; + my $line = ; + return 0 if $line =~ /^n/i; + return 1 if $line =~ /^y/i; + } +} + +sub edit_hunk_loop { + my ($head, $hunk, $ix) = @_; + my $text = $hunk->[$ix]->{TEXT}; + + while (1) { + $text = edit_hunk_manually($text); + if (!defined $text) { + return undef; + } + my $newhunk = { TEXT => $text, USE => 1 }; + if (diff_applies($head, + @{$hunk}[0..$ix-1], + $newhunk, + @{$hunk}[$ix+1..$#{$hunk}])) { + $newhunk->{DISPLAY} = [color_diff(@{$text})]; + return $newhunk; + } + else { + prompt_yesno( + 'Your edited hunk does not apply. Edit again ' + . '(saying "no" discards!) [y/n]? ' + ) or return undef; + } + } +} + sub help_patch_cmd { print colored $help_color, <<\EOF ; y - stage this hunk @@ -694,6 +805,7 @@ J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided hunk K - leave this hunk undecided, see previous hunk s - split the current hunk into smaller hunks +e - manually edit the current hunk ? - print help EOF } @@ -798,6 +910,7 @@ sub patch_update_file { if (hunk_splittable($hunk[$ix]{TEXT})) { $other .= '/s'; } + $other .= '/e'; for (@{$hunk[$ix]{DISPLAY}}) { print; } @@ -862,6 +975,12 @@ sub patch_update_file { $num = scalar @hunk; next; } + elsif ($line =~ /^e/) { + my $newhunk = edit_hunk_loop($head, \@hunk, $ix); + if (defined $newhunk) { + splice @hunk, $ix, 1, $newhunk; + } + } else { help_patch_cmd($other); next;