diff options
Diffstat (limited to 'git-add--interactive.perl')
-rwxr-xr-x | git-add--interactive.perl | 212 |
1 files changed, 186 insertions, 26 deletions
diff --git a/git-add--interactive.perl b/git-add--interactive.perl index b0223c3419..5f129a4203 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -12,6 +12,13 @@ my ($prompt_color, $header_color, $help_color) = $repo->get_color('color.interactive.header', 'bold'), $repo->get_color('color.interactive.help', 'red bold'), ) : (); +my $error_color = (); +if ($menu_use_color) { + my $help_color_spec = ($repo->config('color.interactive.help') or + 'red bold'); + $error_color = $repo->get_color('color.interactive.error', + $help_color_spec); +} my $diff_use_color = $repo->get_colorbool('color.diff'); my ($fraginfo_color) = @@ -33,6 +40,17 @@ my ($diff_new_color) = my $normal_color = $repo->get_color("", "reset"); +my $use_readkey = 0; +sub ReadMode; +sub ReadKey; +if ($repo->config_bool("interactive.singlekey")) { + eval { + require Term::ReadKey; + Term::ReadKey->import; + $use_readkey = 1; + }; +} + sub colored { my $color = shift; my $string = join("", @_); @@ -325,6 +343,10 @@ sub highlight_prefix { return "$prompt_color$prefix$normal_color$remainder"; } +sub error_msg { + print STDERR colored $error_color, @_; +} + sub list_and_choose { my ($opts, @stuff) = @_; my (@chosen, @return); @@ -420,12 +442,12 @@ sub list_and_choose { else { $bottom = $top = find_unique($choice, @stuff); if (!defined $bottom) { - print "Huh ($choice)?\n"; + error_msg "Huh ($choice)?\n"; next TOPLOOP; } } if ($opts->{SINGLETON} && $bottom != $top) { - print "Huh ($choice)?\n"; + error_msg "Huh ($choice)?\n"; next TOPLOOP; } for ($i = $bottom-1; $i <= $top-1; $i++) { @@ -758,11 +780,32 @@ sub diff_applies { return close $fh; } +sub _restore_terminal_and_die { + ReadMode 'restore'; + print "\n"; + exit 1; +} + +sub prompt_single_character { + if ($use_readkey) { + local $SIG{TERM} = \&_restore_terminal_and_die; + local $SIG{INT} = \&_restore_terminal_and_die; + ReadMode 'cbreak'; + my $key = ReadKey 0; + ReadMode 'restore'; + print "$key" if defined $key; + print "\n"; + return $key; + } else { + return <STDIN>; + } +} + sub prompt_yesno { my ($prompt) = @_; while (1) { print colored $prompt_color, $prompt; - my $line = <STDIN>; + my $line = prompt_single_character; return 0 if $line =~ /^n/i; return 1 if $line =~ /^y/i; } @@ -800,6 +843,8 @@ y - stage this hunk n - do not stage this hunk a - stage this and all the remaining hunks in the file d - do not stage this hunk nor any of the remaining hunks in the file +g - select a hunk to go to +/ - search for a hunk matching the given regex j - leave this hunk undecided, see next undecided hunk J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided hunk @@ -836,6 +881,47 @@ sub patch_update_cmd { } } +# Generate a one line summary of a hunk. +sub summarize_hunk { + my $rhunk = shift; + my $summary = $rhunk->{TEXT}[0]; + + # Keep the line numbers, discard extra context. + $summary =~ s/@@(.*?)@@.*/$1 /s; + $summary .= " " x (20 - length $summary); + + # Add some user context. + for my $line (@{$rhunk->{TEXT}}) { + if ($line =~ m/^[+-].*\w/) { + $summary .= $line; + last; + } + } + + chomp $summary; + return substr($summary, 0, 80) . "\n"; +} + + +# Print a one-line summary of each hunk in the array ref in +# the first argument, starting wih the index in the 2nd. +sub display_hunks { + my ($hunks, $i) = @_; + my $ctr = 0; + $i ||= 0; + for (; $i < @$hunks && $ctr < 20; $i++, $ctr++) { + my $status = " "; + if (defined $hunks->[$i]{USE}) { + $status = $hunks->[$i]{USE} ? "+" : "-"; + } + printf "%s%2d: %s", + $status, + $i + 1, + summarize_hunk($hunks->[$i]); + } + return $i; +} + sub patch_update_file { my ($ix, $num); my $path = shift; @@ -850,7 +936,7 @@ sub patch_update_file { print @{$mode->{DISPLAY}}; print colored $prompt_color, "Stage mode change [y/n/a/d/?]? "; - my $line = <STDIN>; + my $line = prompt_single_character; if ($line =~ /^y/i) { $mode->{USE} = 1; last; @@ -887,22 +973,25 @@ sub patch_update_file { for ($i = 0; $i < $ix; $i++) { if (!defined $hunk[$i]{USE}) { $prev = 1; - $other .= '/k'; + $other .= ',k'; last; } } if ($ix) { - $other .= '/K'; + $other .= ',K'; } for ($i = $ix + 1; $i < $num; $i++) { if (!defined $hunk[$i]{USE}) { $next = 1; - $other .= '/j'; + $other .= ',j'; last; } } if ($ix < $num - 1) { - $other .= '/J'; + $other .= ',J'; + } + if ($num > 1) { + $other .= ',g'; } for ($i = 0; $i < $num; $i++) { if (!defined $hunk[$i]{USE}) { @@ -913,14 +1002,14 @@ sub patch_update_file { last if (!$undecided); if (hunk_splittable($hunk[$ix]{TEXT})) { - $other .= '/s'; + $other .= ',s'; } - $other .= '/e'; + $other .= ',e'; for (@{$hunk[$ix]{DISPLAY}}) { print; } - print colored $prompt_color, "Stage this hunk [y/n/a/d$other/?]? "; - my $line = <STDIN>; + print colored $prompt_color, "Stage this hunk [y,n,a,d,/$other,?]? "; + my $line = prompt_single_character; if ($line) { if ($line =~ /^y/i) { $hunk[$ix]{USE} = 1; @@ -937,6 +1026,31 @@ sub patch_update_file { } next; } + elsif ($other =~ /g/ && $line =~ /^g(.*)/) { + my $response = $1; + my $no = $ix > 10 ? $ix - 10 : 0; + while ($response eq '') { + my $extra = ""; + $no = display_hunks(\@hunk, $no); + if ($no < $num) { + $extra = " (<ret> to see more)"; + } + print "go to which hunk$extra? "; + $response = <STDIN>; + if (!defined $response) { + $response = ''; + } + chomp $response; + } + if ($response !~ /^\s*\d+\s*$/) { + error_msg "Invalid number: '$response'\n"; + } elsif (0 < $response && $response <= $num) { + $ix = $response - 1; + } else { + error_msg "Sorry, only $num hunks available.\n"; + } + next; + } elsif ($line =~ /^d/i) { while ($ix < $num) { if (!defined $hunk[$ix]{USE}) { @@ -946,30 +1060,76 @@ sub patch_update_file { } next; } - elsif ($other =~ /K/ && $line =~ /^K/) { - $ix--; - next; - } - elsif ($other =~ /J/ && $line =~ /^J/) { - $ix++; + elsif ($line =~ m|^/(.*)|) { + my $regex = $1; + if ($1 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 "Malformed search regexp $exp: $err\n"; + 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 ($other =~ /k/ && $line =~ /^k/) { - while (1) { + elsif ($line =~ /^K/) { + if ($other =~ /K/) { $ix--; - last if (!$ix || - !defined $hunk[$ix]{USE}); + } + else { + error_msg "No previous hunk\n"; } next; } - elsif ($other =~ /j/ && $line =~ /^j/) { - while (1) { + elsif ($line =~ /^J/) { + if ($other =~ /J/) { $ix++; - last if ($ix >= $num || - !defined $hunk[$ix]{USE}); + } + 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 ($other =~ /s/ && $line =~ /^s/) { my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY}); if (1 < @split) { |