6 use Git
qw(unquote_path);
9 binmode(STDOUT
, ":raw");
11 my $repo = Git
->repository();
13 my $menu_use_color = $repo->get_colorbool('color.interactive');
14 my ($prompt_color, $header_color, $help_color) =
16 $repo->get_color('color.interactive.prompt', 'bold blue'),
17 $repo->get_color('color.interactive.header', 'bold'),
18 $repo->get_color('color.interactive.help', 'red bold'),
21 if ($menu_use_color) {
22 my $help_color_spec = ($repo->config('color.interactive.help') or
24 $error_color = $repo->get_color('color.interactive.error',
28 my $diff_use_color = $repo->get_colorbool('color.diff');
29 my ($fraginfo_color) =
31 $repo->get_color('color.diff.frag', 'cyan'),
33 my ($diff_context_color) =
35 $repo->get_color($repo->config('color.diff.context') ?
'color.diff.context' : 'color.diff.plain', ''),
37 my ($diff_old_color) =
39 $repo->get_color('color.diff.old', 'red'),
41 my ($diff_new_color) =
43 $repo->get_color('color.diff.new', 'green'),
46 my $normal_color = $repo->get_color("", "reset");
48 my $diff_algorithm = $repo->config('diff.algorithm');
49 my $diff_filter = $repo->config('interactive.difffilter');
57 if ($repo->config_bool("interactive.singlekey")) {
59 require Term
::ReadKey
;
60 Term
::ReadKey
->import;
64 print STDERR
"missing Term::ReadKey, disabling interactive.singlekey\n";
68 my $termcap = Term
::Cap
->Tgetent;
69 foreach (values %$termcap) {
70 $term_escapes{$_} = 1 if /^\e/;
78 my $string = join("", @_);
81 # Put a color code at the beginning of each line, a reset at the end
82 # color after newlines that are not at the end of the string
83 $string =~ s/(\n+)(.)/$1$color$2/g;
84 # reset before newlines
85 $string =~ s/(\n+)/$normal_color$1/g;
86 # codes at beginning and end (if necessary):
87 $string =~ s/^/$color/;
88 $string =~ s/$/$normal_color/ unless $string =~ /\n$/;
93 # command line options
96 my $patch_mode_revision;
99 sub apply_patch_for_checkout_commit
;
100 sub apply_patch_for_stash
;
104 DIFF
=> 'diff-files -p',
105 APPLY
=> sub { apply_patch
'apply --cached', @_; },
106 APPLY_CHECK
=> 'apply --cached',
107 FILTER
=> 'file-only',
111 DIFF
=> 'diff-index -p HEAD',
112 APPLY
=> sub { apply_patch
'apply --cached', @_; },
113 APPLY_CHECK
=> 'apply --cached',
118 DIFF
=> 'diff-index -p --cached',
119 APPLY
=> sub { apply_patch
'apply -R --cached', @_; },
120 APPLY_CHECK
=> 'apply -R --cached',
121 FILTER
=> 'index-only',
125 DIFF
=> 'diff-index -R -p --cached',
126 APPLY
=> sub { apply_patch
'apply --cached', @_; },
127 APPLY_CHECK
=> 'apply --cached',
128 FILTER
=> 'index-only',
131 'checkout_index' => {
132 DIFF
=> 'diff-files -p',
133 APPLY
=> sub { apply_patch
'apply -R', @_; },
134 APPLY_CHECK
=> 'apply -R',
135 FILTER
=> 'file-only',
139 DIFF
=> 'diff-index -p',
140 APPLY
=> sub { apply_patch_for_checkout_commit
'-R', @_ },
141 APPLY_CHECK
=> 'apply -R',
145 'checkout_nothead' => {
146 DIFF
=> 'diff-index -R -p',
147 APPLY
=> sub { apply_patch_for_checkout_commit
'', @_ },
148 APPLY_CHECK
=> 'apply',
153 DIFF
=> 'diff-index -p',
154 APPLY
=> sub { apply_patch
'apply -R', @_ },
155 APPLY_CHECK
=> 'apply -R',
159 'worktree_nothead' => {
160 DIFF
=> 'diff-index -R -p',
161 APPLY
=> sub { apply_patch
'apply', @_ },
162 APPLY_CHECK
=> 'apply',
168 $patch_mode = 'stage';
169 my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
172 if ($^O
eq 'MSWin32') {
173 my @invalid = grep {m/[":*]/} @_;
174 die "$^O does not support: @invalid\n" if @invalid;
175 my @args = map { m/ /o ?
"\"$_\"": $_ } @_;
179 open($fh, '-|', @_) or die;
181 close $fh || die "Cannot close @_ ($!)";
186 my ($GIT_DIR) = run_cmd_pipe
(qw(git rev-parse --git-dir));
188 if (!defined $GIT_DIR) {
189 exit(1); # rev-parse would have already said "not a git repo"
195 open $fh, 'git update-index --refresh |'
198 ;# ignore 'needs update'
208 run_cmd_pipe
(qw(git ls-files --others --exclude-standard --), @ARGV);
211 # TRANSLATORS: you can adjust this to align "git add -i" status menu
212 my $status_fmt = __
('%12s %12s %s');
213 my $status_head = sprintf($status_fmt, __
('staged'), __
('unstaged'), __
('path'));
217 sub is_initial_commit
{
218 $initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0
219 unless defined $initial;
227 return $empty_tree if defined $empty_tree;
229 ($empty_tree) = run_cmd_pipe
(qw(git hash-object -t tree /dev/null));
235 sub get_diff_reference
{
237 if (defined $ref and $ref ne 'HEAD') {
239 } elsif (is_initial_commit
()) {
240 return get_empty_tree
();
246 # Returns list of hashes, contents of each of which are:
248 # BINARY: is a binary path
249 # INDEX: is index different from HEAD?
250 # FILE: is file different from index?
251 # INDEX_ADDDEL: is it add/delete between HEAD and index?
252 # FILE_ADDDEL: is it add/delete between index and file?
253 # UNMERGED: is the path unmerged
258 my ($add, $del, $adddel, $file);
260 my $reference = get_diff_reference
($patch_mode_revision);
261 for (run_cmd_pipe
(qw(git diff-index --cached
262 --numstat --summary), $reference,
264 if (($add, $del, $file) =
265 /^([-\d]+) ([-\d]+) (.*)/) {
267 $file = unquote_path
($file);
268 if ($add eq '-' && $del eq '-') {
269 $change = __
('binary');
273 $change = "+$add/-$del";
278 FILE
=> __
('nothing'),
281 elsif (($adddel, $file) =
282 /^ (create|delete) mode [0-7]+ (.*)$/) {
283 $file = unquote_path
($file);
284 $data{$file}{INDEX_ADDDEL
} = $adddel;
288 for (run_cmd_pipe
(qw(git diff-files --ignore-submodules=dirty --numstat --summary --raw --), @ARGV)) {
289 if (($add, $del, $file) =
290 /^([-\d]+) ([-\d]+) (.*)/) {
291 $file = unquote_path
($file);
293 if ($add eq '-' && $del eq '-') {
294 $change = __
('binary');
298 $change = "+$add/-$del";
300 $data{$file}{FILE
} = $change;
302 $data{$file}{BINARY
} = 1;
305 elsif (($adddel, $file) =
306 /^ (create|delete) mode [0-7]+ (.*)$/) {
307 $file = unquote_path
($file);
308 $data{$file}{FILE_ADDDEL
} = $adddel;
310 elsif (/^:[0-7]+ [0-7]+ [0-9a-f]+ [0-9a-f]+ (.) (.*)$/) {
311 $file = unquote_path
($2);
312 if (!exists $data{$file}) {
314 INDEX
=> __
('unchanged'),
319 $data{$file}{UNMERGED
} = 1;
324 for (sort keys %data) {
328 if ($only eq 'index-only') {
329 next if ($it->{INDEX
} eq __
('unchanged'));
331 if ($only eq 'file-only') {
332 next if ($it->{FILE
} eq __
('nothing'));
344 my ($string, @stuff) = @_;
346 for (my $i = 0; $i < @stuff; $i++) {
350 if ((ref $it) eq 'ARRAY') {
358 if ($it =~ /^$string/) {
362 if (defined $hit && defined $found) {
372 # inserts string into trie and updates count for each character
374 my ($trie, $string) = @_;
375 foreach (split //, $string) {
376 $trie = $trie->{$_} ||= {COUNT
=> 0};
381 # returns an array of tuples (prefix, remainder)
382 sub find_unique_prefixes
{
386 # any single prefix exceeding the soft limit is omitted
387 # if any prefix exceeds the hard limit all are omitted
388 # 0 indicates no limit
392 # build a trie modelling all possible options
394 foreach my $print (@stuff) {
395 if ((ref $print) eq 'ARRAY') {
396 $print = $print->[0];
398 elsif ((ref $print) eq 'HASH') {
399 $print = $print->{VALUE
};
401 update_trie
(\
%trie, $print);
402 push @return, $print;
405 # use the trie to find the unique prefixes
406 for (my $i = 0; $i < @return; $i++) {
407 my $ret = $return[$i];
408 my @letters = split //, $ret;
410 my ($prefix, $remainder);
412 for ($j = 0; $j < @letters; $j++) {
413 my $letter = $letters[$j];
414 if ($search{$letter}{COUNT
} == 1) {
415 $prefix = substr $ret, 0, $j + 1;
416 $remainder = substr $ret, $j + 1;
420 my $prefix = substr $ret, 0, $j;
422 if ($hard_limit && $j + 1 > $hard_limit);
424 %search = %{$search{$letter}};
426 if (ord($letters[0]) > 127 ||
427 ($soft_limit && $j + 1 > $soft_limit)) {
431 $return[$i] = [$prefix, $remainder];
436 # filters out prefixes which have special meaning to list_and_choose()
437 sub is_valid_prefix
{
439 return (defined $prefix) &&
440 !($prefix =~ /[\s,]/) && # separators
441 !($prefix =~ /^-/) && # deselection
442 !($prefix =~ /^\d+/) && # selection
443 ($prefix ne '*') && # "all" wildcard
444 ($prefix ne '?'); # prompt help
447 # given a prefix/remainder tuple return a string with the prefix highlighted
448 # for now use square brackets; later might use ANSI colors (underline, bold)
449 sub highlight_prefix
{
451 my $remainder = shift;
453 if (!defined $prefix) {
457 if (!is_valid_prefix
($prefix)) {
458 return "$prefix$remainder";
461 if (!$menu_use_color) {
462 return "[$prefix]$remainder";
465 return "$prompt_color$prefix$normal_color$remainder";
469 print STDERR colored
$error_color, @_;
472 sub list_and_choose
{
473 my ($opts, @stuff) = @_;
474 my (@chosen, @return);
479 my @prefixes = find_unique_prefixes
(@stuff) unless $opts->{LIST_ONLY
};
485 if ($opts->{HEADER
}) {
486 my $indent = $opts->{LIST_FLAT
} ?
"" : " ";
487 print colored
$header_color, "$indent$opts->{HEADER}\n";
489 for ($i = 0; $i < @stuff; $i++) {
490 my $chosen = $chosen[$i] ?
'*' : ' ';
491 my $print = $stuff[$i];
492 my $ref = ref $print;
493 my $highlighted = highlight_prefix
(@
{$prefixes[$i]})
495 if ($ref eq 'ARRAY') {
496 $print = $highlighted || $print->[0];
498 elsif ($ref eq 'HASH') {
499 my $value = $highlighted || $print->{VALUE
};
500 $print = sprintf($status_fmt,
506 $print = $highlighted || $print;
508 printf("%s%2d: %s", $chosen, $i+1, $print);
509 if (($opts->{LIST_FLAT
}) &&
510 (($i + 1) % ($opts->{LIST_FLAT
}))) {
523 return if ($opts->{LIST_ONLY
});
525 print colored
$prompt_color, $opts->{PROMPT
};
526 if ($opts->{SINGLETON
}) {
535 $opts->{ON_EOF
}->() if $opts->{ON_EOF
};
542 singleton_prompt_help_cmd
() :
546 for my $choice (split(/[\s,]+/, $line)) {
550 # Input that begins with '-'; unchoose
551 if ($choice =~ s/^-//) {
554 # A range can be specified like 5-7 or 5-.
555 if ($choice =~ /^(\d+)-(\d*)$/) {
556 ($bottom, $top) = ($1, length($2) ?
$2 : 1 + @stuff);
558 elsif ($choice =~ /^\d+$/) {
559 $bottom = $top = $choice;
561 elsif ($choice eq '*') {
566 $bottom = $top = find_unique
($choice, @stuff);
567 if (!defined $bottom) {
568 error_msg
sprintf(__
("Huh (%s)?\n"), $choice);
572 if ($opts->{SINGLETON
} && $bottom != $top) {
573 error_msg
sprintf(__
("Huh (%s)?\n"), $choice);
576 for ($i = $bottom-1; $i <= $top-1; $i++) {
577 next if (@stuff <= $i || $i < 0);
578 $chosen[$i] = $choose;
581 last if ($opts->{IMMEDIATE
} || $line eq '*');
583 for ($i = 0; $i < @stuff; $i++) {
585 push @return, $stuff[$i];
591 sub singleton_prompt_help_cmd
{
592 print colored
$help_color, __
<<'EOF' ;
594 1 - select a numbered item
595 foo - select item based on unique prefix
596 - (empty) select nothing
600 sub prompt_help_cmd
{
601 print colored
$help_color, __
<<'EOF' ;
603 1 - select a single item
604 3-5 - select a range of items
605 2-3,6-9 - select multiple ranges
606 foo - select item based on unique prefix
607 -... - unselect specified items
609 - (empty) finish selecting
614 list_and_choose
({ LIST_ONLY
=> 1, HEADER
=> $status_head },
622 if ($did eq 'added') {
623 printf(__n
("added %d path\n", "added %d paths\n",
625 } elsif ($did eq 'updated') {
626 printf(__n
("updated %d path\n", "updated %d paths\n",
628 } elsif ($did eq 'reverted') {
629 printf(__n
("reverted %d path\n", "reverted %d paths\n",
632 printf(__n
("touched %d path\n", "touched %d paths\n",
638 my @mods = list_modified
('file-only');
641 my @update = list_and_choose
({ PROMPT
=> __
('Update'),
642 HEADER
=> $status_head, },
645 system(qw(git update-index --add --remove --),
646 map { $_->{VALUE
} } @update);
647 say_n_paths
('updated', @update);
653 my @update = list_and_choose
({ PROMPT
=> __
('Revert'),
654 HEADER
=> $status_head, },
657 if (is_initial_commit
()) {
658 system(qw(git rm --cached),
659 map { $_->{VALUE
} } @update);
662 my @lines = run_cmd_pipe
(qw(git ls-tree HEAD --),
663 map { $_->{VALUE
} } @update);
665 open $fh, '| git update-index --index-info'
672 if ($_->{INDEX_ADDDEL
} &&
673 $_->{INDEX_ADDDEL
} eq 'create') {
674 system(qw(git update-index --force-remove --),
676 printf(__
("note: %s is untracked now.\n"), $_->{VALUE
});
681 say_n_paths
('reverted', @update);
686 sub add_untracked_cmd
{
687 my @add = list_and_choose
({ PROMPT
=> __
('Add untracked') },
690 system(qw(git update-index --add --), @add);
691 say_n_paths
('added', @add);
693 print __
("No untracked files.\n");
701 open $fh, '| git ' . $cmd . " --allow-overlap";
708 my @diff_cmd = split(" ", $patch_mode_flavour{DIFF
});
709 if (defined $diff_algorithm) {
710 splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}";
712 if (defined $patch_mode_revision) {
713 push @diff_cmd, get_diff_reference
($patch_mode_revision);
715 my @diff = run_cmd_pipe
("git", @diff_cmd, qw(--no-color --), $path);
717 if ($diff_use_color) {
718 my @display_cmd = ("git", @diff_cmd, qw(--color --), $path);
719 if (defined $diff_filter) {
720 # quotemeta is overkill, but sufficient for shell-quoting
721 my $diff = join(' ', map { quotemeta } @display_cmd);
722 @display_cmd = ("$diff | $diff_filter");
725 @colored = run_cmd_pipe
(@display_cmd);
727 my (@hunk) = { TEXT
=> [], DISPLAY
=> [], TYPE
=> 'header' };
729 if (@colored && @colored != @diff) {
731 "fatal: mismatched output from interactive.diffFilter\n",
732 "hint: Your filter must maintain a one-to-one correspondence\n",
733 "hint: between its input and output lines.\n";
737 for (my $i = 0; $i < @diff; $i++) {
738 if ($diff[$i] =~ /^@@ /) {
739 push @hunk, { TEXT
=> [], DISPLAY
=> [],
742 push @
{$hunk[-1]{TEXT
}}, $diff[$i];
743 push @
{$hunk[-1]{DISPLAY
}},
744 (@colored ?
$colored[$i] : $diff[$i]);
749 sub parse_diff_header
{
752 my $head = { TEXT
=> [], DISPLAY
=> [], TYPE
=> 'header' };
753 my $mode = { TEXT
=> [], DISPLAY
=> [], TYPE
=> 'mode' };
754 my $deletion = { TEXT
=> [], DISPLAY
=> [], TYPE
=> 'deletion' };
757 for (my $i = 0; $i < @
{$src->{TEXT
}}; $i++) {
758 if ($src->{TEXT
}->[$i] =~ /^new file/) {
760 $head->{TYPE
} = 'addition';
763 $src->{TEXT
}->[$i] =~ /^(old|new) mode (\d+)$/ ?
$mode :
764 $src->{TEXT
}->[$i] =~ /^deleted file/ ?
$deletion :
766 push @
{$dest->{TEXT
}}, $src->{TEXT
}->[$i];
767 push @
{$dest->{DISPLAY
}}, $src->{DISPLAY
}->[$i];
769 return ($head, $mode, $deletion, $addition);
772 sub hunk_splittable
{
775 my @s = split_hunk
($text);
779 sub parse_hunk_header
{
781 my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
782 $line =~ /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
783 $o_cnt = 1 unless defined $o_cnt;
784 $n_cnt = 1 unless defined $n_cnt;
785 return ($o_ofs, $o_cnt, $n_ofs, $n_cnt);
788 sub format_hunk_header
{
789 my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = @_;
790 return ("@@ -$o_ofs" .
791 (($o_cnt != 1) ?
",$o_cnt" : '') .
793 (($n_cnt != 1) ?
",$n_cnt" : '') .
798 my ($text, $display) = @_;
800 if (!defined $display) {
803 # If there are context lines in the middle of a hunk,
804 # it can be split, but we would need to take care of
807 my ($o_ofs, undef, $n_ofs) = parse_hunk_header
($text->[0]);
812 my $next_hunk_start = undef;
813 my $i = $hunk_start - 1;
827 while (++$i < @
$text) {
828 my $line = $text->[$i];
829 my $display = $display->[$i];
830 if ($line =~ /^\\/) {
831 push @
{$this->{TEXT
}}, $line;
832 push @
{$this->{DISPLAY
}}, $display;
836 if ($this->{ADDDEL
} &&
837 !defined $next_hunk_start) {
838 # We have seen leading context and
839 # adds/dels and then here is another
840 # context, which is trailing for this
841 # split hunk and leading for the next
843 $next_hunk_start = $i;
845 push @
{$this->{TEXT
}}, $line;
846 push @
{$this->{DISPLAY
}}, $display;
849 if (defined $next_hunk_start) {
856 if (defined $next_hunk_start) {
857 # We are done with the current hunk and
858 # this is the first real change for the
860 $hunk_start = $next_hunk_start;
861 $o_ofs = $this->{OLD
} + $this->{OCNT
};
862 $n_ofs = $this->{NEW
} + $this->{NCNT
};
863 $o_ofs -= $this->{POSTCTX
};
864 $n_ofs -= $this->{POSTCTX
};
868 push @
{$this->{TEXT
}}, $line;
869 push @
{$this->{DISPLAY
}}, $display;
883 for my $hunk (@split) {
884 $o_ofs = $hunk->{OLD
};
885 $n_ofs = $hunk->{NEW
};
886 my $o_cnt = $hunk->{OCNT
};
887 my $n_cnt = $hunk->{NCNT
};
889 my $head = format_hunk_header
($o_ofs, $o_cnt, $n_ofs, $n_cnt);
890 my $display_head = $head;
891 unshift @
{$hunk->{TEXT
}}, $head;
892 if ($diff_use_color) {
893 $display_head = colored
($fraginfo_color, $head);
895 unshift @
{$hunk->{DISPLAY
}}, $display_head;
900 sub find_last_o_ctx
{
902 my $text = $it->{TEXT
};
903 my ($o_ofs, $o_cnt) = parse_hunk_header
($text->[0]);
905 my $last_o_ctx = $o_ofs + $o_cnt;
907 my $line = $text->[$i];
918 my ($prev, $this) = @_;
919 my ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) =
920 parse_hunk_header
($prev->{TEXT
}[0]);
921 my ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) =
922 parse_hunk_header
($this->{TEXT
}[0]);
924 my (@line, $i, $ofs, $o_cnt, $n_cnt);
927 for ($i = 1; $i < @
{$prev->{TEXT
}}; $i++) {
928 my $line = $prev->{TEXT
}[$i];
929 if ($line =~ /^\+/) {
933 } elsif ($line =~ /^\\/) {
938 last if ($o1_ofs <= $ofs);
948 for ($i = 1; $i < @
{$this->{TEXT
}}; $i++) {
949 my $line = $this->{TEXT
}[$i];
950 if ($line =~ /^\+/) {
954 } elsif ($line =~ /^\\/) {
965 my $head = format_hunk_header
($o0_ofs, $o_cnt, $n0_ofs, $n_cnt);
966 @
{$prev->{TEXT
}} = ($head, @line);
969 sub coalesce_overlapping_hunks
{
973 my ($last_o_ctx, $last_was_dirty);
977 if ($_->{TYPE
} ne 'hunk') {
981 my $text = $_->{TEXT
};
982 my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
983 parse_hunk_header
($text->[0]);
985 $ofs_delta += $o_cnt - $n_cnt;
986 # If this hunk has been edited then subtract
987 # the delta that is due to the edit.
988 if ($_->{OFS_DELTA
}) {
989 $ofs_delta -= $_->{OFS_DELTA
};
994 if ($patch_mode_flavour{IS_REVERSE
}) {
995 $o_ofs -= $ofs_delta;
997 $n_ofs += $ofs_delta;
999 $_->{TEXT
}->[0] = format_hunk_header
($o_ofs, $o_cnt,
1002 # If this hunk was edited then adjust the offset delta
1003 # to reflect the edit.
1004 if ($_->{OFS_DELTA
}) {
1005 $ofs_delta += $_->{OFS_DELTA
};
1007 if (defined $last_o_ctx &&
1008 $o_ofs <= $last_o_ctx &&
1011 merge_hunk
($out[-1], $_);
1016 $last_o_ctx = find_last_o_ctx
($out[-1]);
1017 $last_was_dirty = $_->{DIRTY
};
1022 sub reassemble_patch
{
1026 # Include everything in the header except the beginning of the diff.
1027 push @patch, (grep { !/^[-+]{3}/ } @
$head);
1029 # Then include any headers from the hunk lines, which must
1030 # come before any actual hunk.
1031 while (@_ && $_[0] !~ /^@/) {
1035 # Then begin the diff.
1036 push @patch, grep { /^[-+]{3}/ } @
$head;
1038 # And then the actual hunks.
1046 colored
((/^@/ ?
$fraginfo_color :
1047 /^\+/ ?
$diff_new_color :
1048 /^-/ ?
$diff_old_color :
1049 $diff_context_color),
1054 my %edit_hunk_manually_modes = (
1056 "If the patch applies cleanly, the edited hunk will immediately be
1057 marked for staging."),
1059 "If the patch applies cleanly, the edited hunk will immediately be
1060 marked for stashing."),
1062 "If the patch applies cleanly, the edited hunk will immediately be
1063 marked for unstaging."),
1064 reset_nothead
=> N__
(
1065 "If the patch applies cleanly, the edited hunk will immediately be
1066 marked for applying."),
1067 checkout_index
=> N__
(
1068 "If the patch applies cleanly, the edited hunk will immediately be
1069 marked for discarding."),
1070 checkout_head
=> N__
(
1071 "If the patch applies cleanly, the edited hunk will immediately be
1072 marked for discarding."),
1073 checkout_nothead
=> N__
(
1074 "If the patch applies cleanly, the edited hunk will immediately be
1075 marked for applying."),
1076 worktree_head
=> N__
(
1077 "If the patch applies cleanly, the edited hunk will immediately be
1078 marked for discarding."),
1079 worktree_nothead
=> N__
(
1080 "If the patch applies cleanly, the edited hunk will immediately be
1081 marked for applying."),
1084 sub recount_edited_hunk
{
1086 my ($oldtext, $newtext) = @_;
1087 my ($o_cnt, $n_cnt) = (0, 0);
1088 for (@
{$newtext}[1..$#{$newtext}]) {
1089 my $mode = substr($_, 0, 1);
1092 } elsif ($mode eq '+') {
1094 } elsif ($mode eq ' ' or $mode eq "\n") {
1099 my ($o_ofs, undef, $n_ofs, undef) =
1100 parse_hunk_header
($newtext->[0]);
1101 $newtext->[0] = format_hunk_header
($o_ofs, $o_cnt, $n_ofs, $n_cnt);
1102 my (undef, $orig_o_cnt, undef, $orig_n_cnt) =
1103 parse_hunk_header
($oldtext->[0]);
1104 # Return the change in the number of lines inserted by this hunk
1105 return $orig_o_cnt - $orig_n_cnt - $o_cnt + $n_cnt;
1108 sub edit_hunk_manually
{
1111 my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
1113 open $fh, '>', $hunkfile
1114 or die sprintf(__
("failed to open hunk edit file for writing: %s"), $!);
1115 print $fh Git
::comment_lines __
("Manual hunk edit mode -- see bottom for a quick guide.\n");
1116 print $fh @
$oldtext;
1117 my $is_reverse = $patch_mode_flavour{IS_REVERSE
};
1118 my ($remove_plus, $remove_minus) = $is_reverse ?
('-', '+') : ('+', '-');
1119 my $comment_line_char = Git
::get_comment_line_char
;
1120 print $fh Git
::comment_lines
sprintf(__
<<EOF, $remove_minus, $remove_plus, $comment_line_char),
1122 To remove '%s' lines, make them ' ' lines (context).
1123 To remove '%s' lines, delete them.
1124 Lines starting with %s will be removed.
1126 __
($edit_hunk_manually_modes{$patch_mode}),
1127 # TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
1129 If it does not apply cleanly, you will be given an opportunity to
1130 edit again. If all lines of the hunk are removed, then the edit is
1131 aborted and the hunk is left unchanged.
1135 chomp(my ($editor) = run_cmd_pipe(qw(git var GIT_EDITOR)));
1136 system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
1142 open $fh, '<', $hunkfile
1143 or die sprintf(__
("failed to open hunk edit file for reading: %s"), $!);
1144 my @newtext = grep { !/^\Q$comment_line_char\E/ } <$fh>;
1148 # Abort if nothing remains
1149 if (!grep { /\S/ } @newtext) {
1153 # Reinsert the first hunk header if the user accidentally deleted it
1154 if ($newtext[0] !~ /^@/) {
1155 unshift @newtext, $oldtext->[0];
1161 return run_git_apply
($patch_mode_flavour{APPLY_CHECK
} . ' --check',
1162 map { @
{$_->{TEXT
}} } @_);
1165 sub _restore_terminal_and_die
{
1171 sub prompt_single_character
{
1173 local $SIG{TERM
} = \
&_restore_terminal_and_die
;
1174 local $SIG{INT
} = \
&_restore_terminal_and_die
;
1176 my $key = ReadKey
0;
1179 if ($use_termcap and $key eq "\e") {
1180 while (!defined $term_escapes{$key}) {
1181 my $next = ReadKey
0.5;
1182 last if (!defined $next);
1199 print colored
$prompt_color, $prompt;
1200 my $line = prompt_single_character
;
1201 return undef unless defined $line;
1202 return 0 if $line =~ /^n/i;
1203 return 1 if $line =~ /^y/i;
1207 sub edit_hunk_loop
{
1208 my ($head, $hunks, $ix) = @_;
1209 my $hunk = $hunks->[$ix];
1210 my $text = $hunk->{TEXT
};
1213 my $newtext = edit_hunk_manually
($text);
1214 if (!defined $newtext) {
1219 TYPE
=> $hunk->{TYPE
},
1223 $newhunk->{OFS_DELTA
} = recount_edited_hunk
($text, $newtext);
1224 # If this hunk has already been edited then add the
1225 # offset delta of the previous edit to get the real
1226 # delta from the original unedited hunk.
1227 $hunk->{OFS_DELTA
} and
1228 $newhunk->{OFS_DELTA
} += $hunk->{OFS_DELTA
};
1229 if (diff_applies
($head,
1230 @
{$hunks}[0..$ix-1],
1232 @
{$hunks}[$ix+1..$#{$hunks}])) {
1233 $newhunk->{DISPLAY
} = [color_diff
(@
{$newtext})];
1238 # TRANSLATORS: do not translate [y/n]
1239 # The program will only accept that input
1241 # Consider translating (saying "no" discards!) as
1242 # (saying "n" for "no" discards!) if the translation
1243 # of the word "no" does not start with n.
1244 __
('Your edited hunk does not apply. Edit again '
1245 . '(saying "no" discards!) [y/n]? ')
1251 my %help_patch_modes = (
1253 "y - stage this hunk
1254 n - do not stage this hunk
1255 q - quit; do not stage this hunk or any of the remaining ones
1256 a - stage this hunk and all later hunks in the file
1257 d - do not stage this hunk or any of the later hunks in the file"),
1259 "y - stash this hunk
1260 n - do not stash this hunk
1261 q - quit; do not stash this hunk or any of the remaining ones
1262 a - stash this hunk and all later hunks in the file
1263 d - do not stash this hunk or any of the later hunks in the file"),
1265 "y - unstage this hunk
1266 n - do not unstage this hunk
1267 q - quit; do not unstage this hunk or any of the remaining ones
1268 a - unstage this hunk and all later hunks in the file
1269 d - do not unstage this hunk or any of the later hunks in the file"),
1270 reset_nothead
=> N__
(
1271 "y - apply this hunk to index
1272 n - do not apply this hunk to index
1273 q - quit; do not apply this hunk or any of the remaining ones
1274 a - apply this hunk and all later hunks in the file
1275 d - do not apply this hunk or any of the later hunks in the file"),
1276 checkout_index
=> N__
(
1277 "y - discard this hunk from worktree
1278 n - do not discard this hunk from worktree
1279 q - quit; do not discard this hunk or any of the remaining ones
1280 a - discard this hunk and all later hunks in the file
1281 d - do not discard this hunk or any of the later hunks in the file"),
1282 checkout_head
=> N__
(
1283 "y - discard this hunk from index and worktree
1284 n - do not discard this hunk from index and worktree
1285 q - quit; do not discard this hunk or any of the remaining ones
1286 a - discard this hunk and all later hunks in the file
1287 d - do not discard this hunk or any of the later hunks in the file"),
1288 checkout_nothead
=> N__
(
1289 "y - apply this hunk to index and worktree
1290 n - do not apply this hunk to index and worktree
1291 q - quit; do not apply this hunk or any of the remaining ones
1292 a - apply this hunk and all later hunks in the file
1293 d - do not apply this hunk or any of the later hunks in the file"),
1294 worktree_head
=> N__
(
1295 "y - discard this hunk from worktree
1296 n - do not discard this hunk from worktree
1297 q - quit; do not discard this hunk or any of the remaining ones
1298 a - discard this hunk and all later hunks in the file
1299 d - do not discard this hunk or any of the later hunks in the file"),
1300 worktree_nothead
=> N__
(
1301 "y - apply this hunk to worktree
1302 n - do not apply this hunk to worktree
1303 q - quit; do not apply this hunk or any of the remaining ones
1304 a - apply this hunk and all later hunks in the file
1305 d - do not apply this hunk or any of the later hunks in the file"),
1308 sub help_patch_cmd
{
1310 my $other = $_[0] . ",?";
1311 print colored
$help_color, __
($help_patch_modes{$patch_mode}), "\n",
1312 map { "$_\n" } grep {
1313 my $c = quotemeta(substr($_, 0, 1));
1315 } split "\n", __
<<EOF ;
1316 g - select a hunk to go to
1317 / - search for a hunk matching the given regex
1318 j - leave this hunk undecided, see next undecided hunk
1319 J - leave this hunk undecided, see next hunk
1320 k - leave this hunk undecided, see previous undecided hunk
1321 K - leave this hunk undecided, see previous hunk
1322 s - split the current hunk into smaller hunks
1323 e - manually edit the current hunk
1330 my $ret = run_git_apply
$cmd, @_;
1337 sub apply_patch_for_checkout_commit
{
1338 my $reverse = shift;
1339 my $applies_index = run_git_apply
'apply '.$reverse.' --cached --check', @_;
1340 my $applies_worktree = run_git_apply
'apply '.$reverse.' --check', @_;
1342 if ($applies_worktree && $applies_index) {
1343 run_git_apply
'apply '.$reverse.' --cached', @_;
1344 run_git_apply
'apply '.$reverse, @_;
1346 } elsif (!$applies_index) {
1347 print colored
$error_color, __
("The selected hunks do not apply to the index!\n");
1348 if (prompt_yesno __
("Apply them to the worktree anyway? ")) {
1349 return run_git_apply
'apply '.$reverse, @_;
1351 print colored
$error_color, __
("Nothing was applied.\n");
1360 sub patch_update_cmd
{
1361 my @all_mods = list_modified
($patch_mode_flavour{FILTER
});
1362 error_msg
sprintf(__
("ignoring unmerged: %s\n"), $_->{VALUE
})
1363 for grep { $_->{UNMERGED
} } @all_mods;
1364 @all_mods = grep { !$_->{UNMERGED
} } @all_mods;
1366 my @mods = grep { !($_->{BINARY
}) } @all_mods;
1371 print STDERR __
("Only binary files changed.\n");
1373 print STDERR __
("No changes.\n");
1377 if ($patch_mode_only) {
1381 @them = list_and_choose
({ PROMPT
=> __
('Patch update'),
1382 HEADER
=> $status_head, },
1386 return 0 if patch_update_file
($_->{VALUE
});
1390 # Generate a one line summary of a hunk.
1391 sub summarize_hunk
{
1393 my $summary = $rhunk->{TEXT
}[0];
1395 # Keep the line numbers, discard extra context.
1396 $summary =~ s/@@(.*?)@@.*/$1 /s;
1397 $summary .= " " x
(20 - length $summary);
1399 # Add some user context.
1400 for my $line (@
{$rhunk->{TEXT
}}) {
1401 if ($line =~ m/^[+-].*\w/) {
1408 return substr($summary, 0, 80) . "\n";
1412 # Print a one-line summary of each hunk in the array ref in
1413 # the first argument, starting with the index in the 2nd.
1415 my ($hunks, $i) = @_;
1418 for (; $i < @
$hunks && $ctr < 20; $i++, $ctr++) {
1420 if (defined $hunks->[$i]{USE
}) {
1421 $status = $hunks->[$i]{USE
} ?
"+" : "-";
1426 summarize_hunk
($hunks->[$i]);
1431 my %patch_update_prompt_modes = (
1433 mode
=> N__
("Stage mode change [y,n,q,a,d%s,?]? "),
1434 deletion
=> N__
("Stage deletion [y,n,q,a,d%s,?]? "),
1435 addition
=> N__
("Stage addition [y,n,q,a,d%s,?]? "),
1436 hunk
=> N__
("Stage this hunk [y,n,q,a,d%s,?]? "),
1439 mode
=> N__
("Stash mode change [y,n,q,a,d%s,?]? "),
1440 deletion
=> N__
("Stash deletion [y,n,q,a,d%s,?]? "),
1441 addition
=> N__
("Stash addition [y,n,q,a,d%s,?]? "),
1442 hunk
=> N__
("Stash this hunk [y,n,q,a,d%s,?]? "),
1445 mode
=> N__
("Unstage mode change [y,n,q,a,d%s,?]? "),
1446 deletion
=> N__
("Unstage deletion [y,n,q,a,d%s,?]? "),
1447 addition
=> N__
("Unstage addition [y,n,q,a,d%s,?]? "),
1448 hunk
=> N__
("Unstage this hunk [y,n,q,a,d%s,?]? "),
1451 mode
=> N__
("Apply mode change to index [y,n,q,a,d%s,?]? "),
1452 deletion
=> N__
("Apply deletion to index [y,n,q,a,d%s,?]? "),
1453 addition
=> N__
("Apply addition to index [y,n,q,a,d%s,?]? "),
1454 hunk
=> N__
("Apply this hunk to index [y,n,q,a,d%s,?]? "),
1457 mode
=> N__
("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
1458 deletion
=> N__
("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
1459 addition
=> N__
("Discard addition from worktree [y,n,q,a,d%s,?]? "),
1460 hunk
=> N__
("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
1463 mode
=> N__
("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
1464 deletion
=> N__
("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
1465 addition
=> N__
("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
1466 hunk
=> N__
("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
1468 checkout_nothead
=> {
1469 mode
=> N__
("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
1470 deletion
=> N__
("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
1471 addition
=> N__
("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
1472 hunk
=> N__
("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
1475 mode
=> N__
("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
1476 deletion
=> N__
("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
1477 addition
=> N__
("Discard addition from worktree [y,n,q,a,d%s,?]? "),
1478 hunk
=> N__
("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
1480 worktree_nothead
=> {
1481 mode
=> N__
("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
1482 deletion
=> N__
("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
1483 addition
=> N__
("Apply addition to worktree [y,n,q,a,d%s,?]? "),
1484 hunk
=> N__
("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
1488 sub patch_update_file
{
1492 my ($head, @hunk) = parse_diff
($path);
1493 ($head, my $mode, my $deletion, my $addition) = parse_diff_header
($head);
1494 for (@
{$head->{DISPLAY
}}) {
1498 if (@
{$mode->{TEXT
}}) {
1499 unshift @hunk, $mode;
1501 if (@
{$deletion->{TEXT
}}) {
1502 foreach my $hunk (@hunk) {
1503 push @
{$deletion->{TEXT
}}, @
{$hunk->{TEXT
}};
1504 push @
{$deletion->{DISPLAY
}}, @
{$hunk->{DISPLAY
}};
1506 @hunk = ($deletion);
1509 $num = scalar @hunk;
1513 my ($prev, $next, $other, $undecided, $i);
1516 last if ($ix and !$num);
1520 for ($i = 0; $i < $ix; $i++) {
1521 if (!defined $hunk[$i]{USE
}) {
1530 for ($i = $ix + 1; $i < $num; $i++) {
1531 if (!defined $hunk[$i]{USE
}) {
1537 if ($ix < $num - 1) {
1543 for ($i = 0; $i < $num; $i++) {
1544 if (!defined $hunk[$i]{USE
}) {
1549 last if (!$undecided && ($num || !$addition));
1552 if ($hunk[$ix]{TYPE
} eq 'hunk' &&
1553 hunk_splittable
($hunk[$ix]{TEXT
})) {
1556 if ($hunk[$ix]{TYPE
} eq 'hunk') {
1559 for (@
{$hunk[$ix]{DISPLAY
}}) {
1563 my $type = $num ?
$hunk[$ix]{TYPE
} : $head->{TYPE
};
1564 print colored
$prompt_color, "(", ($ix+1), "/", ($num ?
$num : 1), ") ",
1565 sprintf(__
($patch_update_prompt_modes{$patch_mode}{$type}), $other);
1567 my $line = prompt_single_character
;
1568 last unless defined $line;
1570 if ($line =~ /^y/i) {
1572 $hunk[$ix]{USE
} = 1;
1577 elsif ($line =~ /^n/i) {
1579 $hunk[$ix]{USE
} = 0;
1584 elsif ($line =~ /^a/i) {
1586 while ($ix < $num) {
1587 if (!defined $hunk[$ix]{USE
}) {
1588 $hunk[$ix]{USE
} = 1;
1598 elsif ($line =~ /^g(.*)/) {
1600 unless ($other =~ /g/) {
1601 error_msg __
("No other hunks to goto\n");
1604 my $no = $ix > 10 ?
$ix - 10 : 0;
1605 while ($response eq '') {
1606 $no = display_hunks
(\
@hunk, $no);
1608 print __
("go to which hunk (<ret> to see more)? ");
1610 print __
("go to which hunk? ");
1612 $response = <STDIN
>;
1613 if (!defined $response) {
1618 if ($response !~ /^\s*\d+\s*$/) {
1619 error_msg
sprintf(__
("Invalid number: '%s'\n"),
1621 } elsif (0 < $response && $response <= $num) {
1622 $ix = $response - 1;
1624 error_msg
sprintf(__n
("Sorry, only %d hunk available.\n",
1625 "Sorry, only %d hunks available.\n", $num), $num);
1629 elsif ($line =~ /^d/i) {
1631 while ($ix < $num) {
1632 if (!defined $hunk[$ix]{USE
}) {
1633 $hunk[$ix]{USE
} = 0;
1643 elsif ($line =~ /^q/i) {
1645 for ($i = 0; $i < $num; $i++) {
1646 if (!defined $hunk[$i]{USE
}) {
1650 } elsif (!defined $head->{USE
}) {
1656 elsif ($line =~ m
|^/(.*)|) {
1658 unless ($other =~ m
|/|) {
1659 error_msg __
("No other hunks to search\n");
1663 print colored
$prompt_color, __
("search for regex? ");
1665 if (defined $regex) {
1671 $search_string = qr{$regex}m;
1674 my ($err,$exp) = ($@
, $1);
1675 $err =~ s/ at .*git-add--interactive line \d+, <STDIN> line \d+.*$//;
1676 error_msg
sprintf(__
("Malformed search regexp %s: %s\n"), $exp, $err);
1681 my $text = join ("", @
{$hunk[$iy]{TEXT
}});
1682 last if ($text =~ $search_string);
1684 $iy = 0 if ($iy >= $num);
1686 error_msg __
("No hunk matches the given pattern\n");
1693 elsif ($line =~ /^K/) {
1694 if ($other =~ /K/) {
1698 error_msg __
("No previous hunk\n");
1702 elsif ($line =~ /^J/) {
1703 if ($other =~ /J/) {
1707 error_msg __
("No next hunk\n");
1711 elsif ($line =~ /^k/) {
1712 if ($other =~ /k/) {
1716 !defined $hunk[$ix]{USE
});
1720 error_msg __
("No previous hunk\n");
1724 elsif ($line =~ /^j/) {
1725 if ($other !~ /j/) {
1726 error_msg __
("No next hunk\n");
1730 elsif ($line =~ /^s/) {
1731 unless ($other =~ /s/) {
1732 error_msg __
("Sorry, cannot split this hunk\n");
1735 my @split = split_hunk
($hunk[$ix]{TEXT
}, $hunk[$ix]{DISPLAY
});
1737 print colored
$header_color, sprintf(
1738 __n
("Split into %d hunk.\n",
1739 "Split into %d hunks.\n",
1740 scalar(@split)), scalar(@split));
1742 splice (@hunk, $ix, 1, @split);
1743 $num = scalar @hunk;
1746 elsif ($line =~ /^e/) {
1747 unless ($other =~ /e/) {
1748 error_msg __
("Sorry, cannot edit this hunk\n");
1751 my $newhunk = edit_hunk_loop
($head, \
@hunk, $ix);
1752 if (defined $newhunk) {
1753 splice @hunk, $ix, 1, $newhunk;
1757 help_patch_cmd
($other);
1763 last if ($ix >= $num ||
1764 !defined $hunk[$ix]{USE
});
1769 @hunk = coalesce_overlapping_hunks
(@hunk) if ($num);
1775 push @result, @
{$_->{TEXT
}};
1779 if (@result or $head->{USE
}) {
1780 my @patch = reassemble_patch
($head->{TEXT
}, @result);
1781 my $apply_routine = $patch_mode_flavour{APPLY
};
1782 &$apply_routine(@patch);
1791 my @mods = list_modified
('index-only');
1792 @mods = grep { !($_->{BINARY
}) } @mods;
1794 my (@them) = list_and_choose
({ PROMPT
=> __
('Review diff'),
1796 HEADER
=> $status_head, },
1799 my $reference = (is_initial_commit
()) ? get_empty_tree
() : 'HEAD';
1800 system(qw(git diff -p --cached), $reference, '--',
1801 map { $_->{VALUE
} } @them);
1810 # TRANSLATORS: please do not translate the command names
1811 # 'status', 'update', 'revert', etc.
1812 print colored
$help_color, __
<<'EOF' ;
1813 status - show paths with changes
1814 update - add working tree state to the staged set of changes
1815 revert - revert staged set of changes back to the HEAD version
1816 patch - pick hunks and update selectively
1817 diff - view diff between HEAD and index
1818 add untracked - add contents of untracked files to the staged set of changes
1823 return unless @ARGV;
1824 my $arg = shift @ARGV;
1825 if ($arg =~ /--patch(?:=(.*))?/) {
1827 if ($1 eq 'reset') {
1828 $patch_mode = 'reset_head';
1829 $patch_mode_revision = 'HEAD';
1830 $arg = shift @ARGV or die __
("missing --");
1832 $patch_mode_revision = $arg;
1834 # NEEDSWORK: Instead of comparing to the literal "HEAD",
1835 # compare the commit objects instead so that other ways of
1836 # saying the same thing (such as "@") are also handled
1839 # This applies to the cases below too.
1840 $patch_mode = ($arg eq 'HEAD' ?
1841 'reset_head' : 'reset_nothead');
1842 $arg = shift @ARGV or die __
("missing --");
1844 } elsif ($1 eq 'checkout') {
1845 $arg = shift @ARGV or die __
("missing --");
1847 $patch_mode = 'checkout_index';
1849 $patch_mode_revision = $arg;
1850 $patch_mode = ($arg eq 'HEAD' ?
1851 'checkout_head' : 'checkout_nothead');
1852 $arg = shift @ARGV or die __
("missing --");
1854 } elsif ($1 eq 'worktree') {
1855 $arg = shift @ARGV or die __
("missing --");
1857 $patch_mode = 'checkout_index';
1859 $patch_mode_revision = $arg;
1860 $patch_mode = ($arg eq 'HEAD' ?
1861 'worktree_head' : 'worktree_nothead');
1862 $arg = shift @ARGV or die __
("missing --");
1864 } elsif ($1 eq 'stage' or $1 eq 'stash') {
1866 $arg = shift @ARGV or die __
("missing --");
1868 die sprintf(__
("unknown --patch mode: %s"), $1);
1871 $patch_mode = 'stage';
1872 $arg = shift @ARGV or die __
("missing --");
1874 die sprintf(__
("invalid argument %s, expecting --"),
1875 $arg) unless $arg eq "--";
1876 %patch_mode_flavour = %{$patch_modes{$patch_mode}};
1877 $patch_mode_only = 1;
1879 elsif ($arg ne "--") {
1880 die sprintf(__
("invalid argument %s, expecting --"), $arg);
1885 my @cmd = ([ 'status', \
&status_cmd
, ],
1886 [ 'update', \
&update_cmd
, ],
1887 [ 'revert', \
&revert_cmd
, ],
1888 [ 'add untracked', \
&add_untracked_cmd
, ],
1889 [ 'patch', \
&patch_update_cmd
, ],
1890 [ 'diff', \
&diff_cmd
, ],
1891 [ 'quit', \
&quit_cmd
, ],
1892 [ 'help', \
&help_cmd
, ],
1895 my ($it) = list_and_choose
({ PROMPT
=> __
('What now'),
1898 HEADER
=> __
('*** Commands ***'),
1899 ON_EOF
=> \
&quit_cmd
,
1900 IMMEDIATE
=> 1 }, @cmd);
1914 if ($patch_mode_only) {