Merge branch 'jc/retire-compaction-heuristics'
authorJunio C Hamano <gitster@pobox.com>
Tue, 10 Jan 2017 23:24:27 +0000 (15:24 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 10 Jan 2017 23:24:27 +0000 (15:24 -0800)
"git diff" and its family had two experimental heuristics to shift
the contents of a hunk to make the patch easier to read. One of
them turns out to be better than the other, so leave only the
"--indent-heuristic" option and remove the other one.

* jc/retire-compaction-heuristics:
diff: retire "compaction" heuristics

1  2 
diff.c
git-add--interactive.perl
diff --combined diff.c
index 84dba60c405b588ed794a2539ce3cddaa1fbaf8b,741ce8c68dc7f4e6e860db03391055b4ac589cc3..e2eb6d66a9a633e699398146e37dcbcc0b7ec81f
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -28,7 -28,6 +28,6 @@@
  
  static int diff_detect_rename_default;
  static int diff_indent_heuristic; /* experimental */
- static int diff_compaction_heuristic; /* experimental */
  static int diff_rename_limit_default = 400;
  static int diff_suppress_blank_empty;
  static int diff_use_color_default = -1;
@@@ -223,16 -222,8 +222,8 @@@ void init_diff_ui_defaults(void
  
  int git_diff_heuristic_config(const char *var, const char *value, void *cb)
  {
-       if (!strcmp(var, "diff.indentheuristic")) {
+       if (!strcmp(var, "diff.indentheuristic"))
                diff_indent_heuristic = git_config_bool(var, value);
-               if (diff_indent_heuristic)
-                       diff_compaction_heuristic = 0;
-       }
-       if (!strcmp(var, "diff.compactionheuristic")) {
-               diff_compaction_heuristic = git_config_bool(var, value);
-               if (diff_compaction_heuristic)
-                       diff_indent_heuristic = 0;
-       }
        return 0;
  }
  
@@@ -2023,7 -2014,7 +2014,7 @@@ static void show_dirstat(struct diff_op
                if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
                        diff_populate_filespec(p->one, 0);
                        diff_populate_filespec(p->two, 0);
 -                      diffcore_count_changes(p->one, p->two, NULL, NULL, 0,
 +                      diffcore_count_changes(p->one, p->two, NULL, NULL,
                                               &copied, &added);
                        diff_free_filespec_data(p->one);
                        diff_free_filespec_data(p->two);
@@@ -3106,8 -3097,7 +3097,8 @@@ static const char *diff_abbrev_oid(cons
                        abbrev = FALLBACK_DEFAULT_ABBREV;
                if (abbrev > GIT_SHA1_HEXSZ)
                        die("BUG: oid abbreviation out of range: %d", abbrev);
 -              hex[abbrev] = '\0';
 +              if (abbrev)
 +                      hex[abbrev] = '\0';
                return hex;
        }
  }
@@@ -3365,7 -3355,6 +3356,7 @@@ void diff_setup(struct diff_options *op
  
        options->file = stdout;
  
 +      options->abbrev = DEFAULT_ABBREV;
        options->line_termination = '\n';
        options->break_opt = -1;
        options->rename_limit = -1;
        options->xdl_opts |= diff_algorithm;
        if (diff_indent_heuristic)
                DIFF_XDL_SET(options, INDENT_HEURISTIC);
-       else if (diff_compaction_heuristic)
-               DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
  
        options->orderfile = diff_order_file_cfg;
  
@@@ -3878,16 -3865,10 +3867,10 @@@ int diff_opt_parse(struct diff_options 
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(arg, "--ignore-blank-lines"))
                DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
-       else if (!strcmp(arg, "--indent-heuristic")) {
+       else if (!strcmp(arg, "--indent-heuristic"))
                DIFF_XDL_SET(options, INDENT_HEURISTIC);
-               DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
-       } else if (!strcmp(arg, "--no-indent-heuristic"))
-               DIFF_XDL_CLR(options, INDENT_HEURISTIC);
-       else if (!strcmp(arg, "--compaction-heuristic")) {
-               DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
+       else if (!strcmp(arg, "--no-indent-heuristic"))
                DIFF_XDL_CLR(options, INDENT_HEURISTIC);
-       } else if (!strcmp(arg, "--no-compaction-heuristic"))
-               DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
        else if (!strcmp(arg, "--patience"))
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        else if (!strcmp(arg, "--histogram"))
                            offending, optarg);
                return argcount;
        }
 +      else if (!strcmp(arg, "--no-abbrev"))
 +              options->abbrev = 0;
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
        else if (skip_prefix(arg, "--abbrev=", &arg)) {
index 4e0ab5a9bc2f8b22fb7e358b162ebaa93c55e139,5a55d80b9dd848d41daf84771e3b89d55a60ec23..cf6fc926ab9325780eca5b6242cc04a656723451
@@@ -4,7 -4,6 +4,7 @@@ use 5.008
  use strict;
  use warnings;
  use Git;
 +use Git::I18N;
  
  binmode(STDOUT, ":raw");
  
@@@ -47,7 -46,6 +47,6 @@@ my $normal_color = $repo->get_color(""
  
  my $diff_algorithm = $repo->config('diff.algorithm');
  my $diff_indent_heuristic = $repo->config_bool('diff.indentheuristic');
- my $diff_compaction_heuristic = $repo->config_bool('diff.compactionheuristic');
  my $diff_filter = $repo->config('interactive.difffilter');
  
  my $use_readkey = 0;
@@@ -93,7 -91,6 +92,7 @@@ sub colored 
  }
  
  # command line options
 +my $cmd;
  my $patch_mode;
  my $patch_mode_revision;
  
@@@ -106,6 -103,9 +105,6 @@@ my %patch_modes = 
                DIFF => 'diff-files -p',
                APPLY => sub { apply_patch 'apply --cached', @_; },
                APPLY_CHECK => 'apply --cached',
 -              VERB => 'Stage',
 -              TARGET => '',
 -              PARTICIPLE => 'staging',
                FILTER => 'file-only',
                IS_REVERSE => 0,
        },
                DIFF => 'diff-index -p HEAD',
                APPLY => sub { apply_patch 'apply --cached', @_; },
                APPLY_CHECK => 'apply --cached',
 -              VERB => 'Stash',
 -              TARGET => '',
 -              PARTICIPLE => 'stashing',
                FILTER => undef,
                IS_REVERSE => 0,
        },
                DIFF => 'diff-index -p --cached',
                APPLY => sub { apply_patch 'apply -R --cached', @_; },
                APPLY_CHECK => 'apply -R --cached',
 -              VERB => 'Unstage',
 -              TARGET => '',
 -              PARTICIPLE => 'unstaging',
                FILTER => 'index-only',
                IS_REVERSE => 1,
        },
                DIFF => 'diff-index -R -p --cached',
                APPLY => sub { apply_patch 'apply --cached', @_; },
                APPLY_CHECK => 'apply --cached',
 -              VERB => 'Apply',
 -              TARGET => ' to index',
 -              PARTICIPLE => 'applying',
                FILTER => 'index-only',
                IS_REVERSE => 0,
        },
                DIFF => 'diff-files -p',
                APPLY => sub { apply_patch 'apply -R', @_; },
                APPLY_CHECK => 'apply -R',
 -              VERB => 'Discard',
 -              TARGET => ' from worktree',
 -              PARTICIPLE => 'discarding',
                FILTER => 'file-only',
                IS_REVERSE => 1,
        },
                DIFF => 'diff-index -p',
                APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
                APPLY_CHECK => 'apply -R',
 -              VERB => 'Discard',
 -              TARGET => ' from index and worktree',
 -              PARTICIPLE => 'discarding',
                FILTER => undef,
                IS_REVERSE => 1,
        },
                DIFF => 'diff-index -R -p',
                APPLY => sub { apply_patch_for_checkout_commit '', @_ },
                APPLY_CHECK => 'apply',
 -              VERB => 'Apply',
 -              TARGET => ' to index and worktree',
 -              PARTICIPLE => 'applying',
                FILTER => undef,
                IS_REVERSE => 0,
        },
  );
  
 -my %patch_mode_flavour = %{$patch_modes{stage}};
 +$patch_mode = 'stage';
 +my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
  
  sub run_cmd_pipe {
        if ($^O eq 'MSWin32') {
@@@ -235,9 -252,8 +234,9 @@@ sub list_untracked 
        run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
  }
  
 -my $status_fmt = '%12s %12s %s';
 -my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
 +# TRANSLATORS: you can adjust this to align "git add -i" status menu
 +my $status_fmt = __('%12s %12s %s');
 +my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), __('path'));
  
  {
        my $initial;
@@@ -295,7 -311,7 +294,7 @@@ sub list_modified 
                        my ($change, $bin);
                        $file = unquote_path($file);
                        if ($add eq '-' && $del eq '-') {
 -                              $change = 'binary';
 +                              $change = __('binary');
                                $bin = 1;
                        }
                        else {
                        $data{$file} = {
                                INDEX => $change,
                                BINARY => $bin,
 -                              FILE => 'nothing',
 +                              FILE => __('nothing'),
                        }
                }
                elsif (($adddel, $file) =
                        $file = unquote_path($file);
                        my ($change, $bin);
                        if ($add eq '-' && $del eq '-') {
 -                              $change = 'binary';
 +                              $change = __('binary');
                                $bin = 1;
                        }
                        else {
                        $file = unquote_path($2);
                        if (!exists $data{$file}) {
                                $data{$file} = +{
 -                                      INDEX => 'unchanged',
 +                                      INDEX => __('unchanged'),
                                        BINARY => 0,
                                };
                        }
  
                if ($only) {
                        if ($only eq 'index-only') {
 -                              next if ($it->{INDEX} eq 'unchanged');
 +                              next if ($it->{INDEX} eq __('unchanged'));
                        }
                        if ($only eq 'file-only') {
 -                              next if ($it->{FILE} eq 'nothing');
 +                              next if ($it->{FILE} eq __('nothing'));
                        }
                }
                push @return, +{
@@@ -596,12 -612,12 +595,12 @@@ sub list_and_choose 
                        else {
                                $bottom = $top = find_unique($choice, @stuff);
                                if (!defined $bottom) {
 -                                      error_msg "Huh ($choice)?\n";
 +                                      error_msg sprintf(__("Huh (%s)?\n"), $choice);
                                        next TOPLOOP;
                                }
                        }
                        if ($opts->{SINGLETON} && $bottom != $top) {
 -                              error_msg "Huh ($choice)?\n";
 +                              error_msg sprintf(__("Huh (%s)?\n"), $choice);
                                next TOPLOOP;
                        }
                        for ($i = $bottom-1; $i <= $top-1; $i++) {
  }
  
  sub singleton_prompt_help_cmd {
 -      print colored $help_color, <<\EOF ;
 +      print colored $help_color, __ <<'EOF' ;
  Prompt help:
  1          - select a numbered item
  foo        - select item based on unique prefix
@@@ -629,7 -645,7 +628,7 @@@ EO
  }
  
  sub prompt_help_cmd {
 -      print colored $help_color, <<\EOF ;
 +      print colored $help_color, __ <<'EOF' ;
  Prompt help:
  1          - select a single item
  3-5        - select a range of items
@@@ -650,18 -666,12 +649,18 @@@ sub status_cmd 
  sub say_n_paths {
        my $did = shift @_;
        my $cnt = scalar @_;
 -      print "$did ";
 -      if (1 < $cnt) {
 -              print "$cnt paths\n";
 -      }
 -      else {
 -              print "one path\n";
 +      if ($did eq 'added') {
 +              printf(__n("added %d path\n", "added %d paths\n",
 +                         $cnt), $cnt);
 +      } elsif ($did eq 'updated') {
 +              printf(__n("updated %d path\n", "updated %d paths\n",
 +                         $cnt), $cnt);
 +      } elsif ($did eq 'reverted') {
 +              printf(__n("reverted %d path\n", "reverted %d paths\n",
 +                         $cnt), $cnt);
 +      } else {
 +              printf(__n("touched %d path\n", "touched %d paths\n",
 +                         $cnt), $cnt);
        }
  }
  
@@@ -669,7 -679,7 +668,7 @@@ sub update_cmd 
        my @mods = list_modified('file-only');
        return if (!@mods);
  
 -      my @update = list_and_choose({ PROMPT => 'Update',
 +      my @update = list_and_choose({ PROMPT => __('Update'),
                                       HEADER => $status_head, },
                                     @mods);
        if (@update) {
  }
  
  sub revert_cmd {
 -      my @update = list_and_choose({ PROMPT => 'Revert',
 +      my @update = list_and_choose({ PROMPT => __('Revert'),
                                       HEADER => $status_head, },
                                     list_modified());
        if (@update) {
                                    $_->{INDEX_ADDDEL} eq 'create') {
                                        system(qw(git update-index --force-remove --),
                                               $_->{VALUE});
 -                                      print "note: $_->{VALUE} is untracked now.\n";
 +                                      printf(__("note: %s is untracked now.\n"), $_->{VALUE});
                                }
                        }
                }
  }
  
  sub add_untracked_cmd {
 -      my @add = list_and_choose({ PROMPT => 'Add untracked' },
 +      my @add = list_and_choose({ PROMPT => __('Add untracked') },
                                  list_untracked());
        if (@add) {
                system(qw(git update-index --add --), @add);
                say_n_paths('added', @add);
        } else {
 -              print "No untracked files.\n";
 +              print __("No untracked files.\n");
        }
        print "\n";
  }
@@@ -742,8 -752,6 +741,6 @@@ sub parse_diff 
        }
        if ($diff_indent_heuristic) {
                splice @diff_cmd, 1, 0, "--indent-heuristic";
-       } elsif ($diff_compaction_heuristic) {
-               splice @diff_cmd, 1, 0, "--compaction-heuristic";
        }
        if (defined $patch_mode_revision) {
                push @diff_cmd, get_diff_reference($patch_mode_revision);
@@@ -1037,55 -1045,29 +1034,55 @@@ sub color_diff 
        } @_;
  }
  
 +my %edit_hunk_manually_modes = (
 +      stage => N__(
 +"If the patch applies cleanly, the edited hunk will immediately be
 +marked for staging."),
 +      stash => N__(
 +"If the patch applies cleanly, the edited hunk will immediately be
 +marked for stashing."),
 +      reset_head => N__(
 +"If the patch applies cleanly, the edited hunk will immediately be
 +marked for unstaging."),
 +      reset_nothead => N__(
 +"If the patch applies cleanly, the edited hunk will immediately be
 +marked for applying."),
 +      checkout_index => N__(
 +"If the patch applies cleanly, the edited hunk will immediately be
 +marked for discarding"),
 +      checkout_head => N__(
 +"If the patch applies cleanly, the edited hunk will immediately be
 +marked for discarding."),
 +      checkout_nothead => N__(
 +"If the patch applies cleanly, the edited hunk will immediately be
 +marked for applying."),
 +);
 +
  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";
 +              or die sprintf(__("failed to open hunk edit file for writing: %s"), $!);
 +      print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom for a quick guide.\n");
        print $fh @$oldtext;
 -      my $participle = $patch_mode_flavour{PARTICIPLE};
        my $is_reverse = $patch_mode_flavour{IS_REVERSE};
        my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', '-');
 -      print $fh <<EOF;
 -# ---
 -# To remove '$remove_minus' lines, make them ' ' lines (context).
 -# To remove '$remove_plus' lines, delete them.
 -# Lines starting with # will be removed.
 -#
 -# If the patch applies cleanly, the edited hunk will immediately be
 -# marked for $participle. If it does not apply cleanly, you will be given
 -# an opportunity to edit again. If all lines of the hunk are removed,
 -# then the edit is aborted and the hunk is left unchanged.
 +      my $comment_line_char = Git::get_comment_line_char;
 +      print $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, $remove_plus, $comment_line_char),
 +---
 +To remove '%s' lines, make them ' ' lines (context).
 +To remove '%s' lines, delete them.
 +Lines starting with %s will be removed.
  EOF
 +__($edit_hunk_manually_modes{$patch_mode}),
 +# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
 +__ <<EOF2 ;
 +If it does not apply cleanly, you will be given an opportunity to
 +edit again.  If all lines of the hunk are removed, then the edit is
 +aborted and the hunk is left unchanged.
 +EOF2
        close $fh;
  
        chomp(my $editor = run_cmd_pipe(qw(git var GIT_EDITOR)));
        }
  
        open $fh, '<', $hunkfile
 -              or die "failed to open hunk edit file for reading: " . $!;
 -      my @newtext = grep { !/^#/ } <$fh>;
 +              or die sprintf(__("failed to open hunk edit file for reading: %s"), $!);
 +      my @newtext = grep { !/^$comment_line_char/ } <$fh>;
        close $fh;
        unlink $hunkfile;
  
@@@ -1181,66 -1163,22 +1178,66 @@@ sub edit_hunk_loop 
                }
                else {
                        prompt_yesno(
 -                              'Your edited hunk does not apply. Edit again '
 -                              . '(saying "no" discards!) [y/n]? '
 +                              # TRANSLATORS: do not translate [y/n]
 +                              # The program will only accept that input
 +                              # at this point.
 +                              # Consider translating (saying "no" discards!) as
 +                              # (saying "n" for "no" discards!) if the translation
 +                              # of the word "no" does not start with n.
 +                              __('Your edited hunk does not apply. Edit again '
 +                                 . '(saying "no" discards!) [y/n]? ')
                                ) or return undef;
                }
        }
  }
  
 +my %help_patch_modes = (
 +      stage => N__(
 +"y - stage this hunk
 +n - do not stage this hunk
 +q - quit; do not stage this hunk or any of the remaining ones
 +a - stage this hunk and all later hunks in the file
 +d - do not stage this hunk or any of the later hunks in the file"),
 +      stash => N__(
 +"y - stash this hunk
 +n - do not stash this hunk
 +q - quit; do not stash this hunk or any of the remaining ones
 +a - stash this hunk and all later hunks in the file
 +d - do not stash this hunk or any of the later hunks in the file"),
 +      reset_head => N__(
 +"y - unstage this hunk
 +n - do not unstage this hunk
 +q - quit; do not unstage this hunk or any of the remaining ones
 +a - unstage this hunk and all later hunks in the file
 +d - do not unstage this hunk or any of the later hunks in the file"),
 +      reset_nothead => N__(
 +"y - apply this hunk to index
 +n - do not apply this hunk to index
 +q - quit; do not apply this hunk or any of the remaining ones
 +a - apply this hunk and all later hunks in the file
 +d - do not apply this hunk or any of the later hunks in the file"),
 +      checkout_index => N__(
 +"y - discard this hunk from worktree
 +n - do not discard this hunk from worktree
 +q - quit; do not discard this hunk or any of the remaining ones
 +a - discard this hunk and all later hunks in the file
 +d - do not discard this hunk or any of the later hunks in the file"),
 +      checkout_head => N__(
 +"y - discard this hunk from index and worktree
 +n - do not discard this hunk from index and worktree
 +q - quit; do not discard this hunk or any of the remaining ones
 +a - discard this hunk and all later hunks in the file
 +d - do not discard this hunk or any of the later hunks in the file"),
 +      checkout_nothead => N__(
 +"y - apply this hunk to index and worktree
 +n - do not apply this hunk to index and worktree
 +q - quit; do not apply this hunk or any of the remaining ones
 +a - apply this hunk and all later hunks in the file
 +d - do not apply this hunk or any of the later hunks in the file"),
 +);
 +
  sub help_patch_cmd {
 -      my $verb = lc $patch_mode_flavour{VERB};
 -      my $target = $patch_mode_flavour{TARGET};
 -      print colored $help_color, <<EOF ;
 -y - $verb this hunk$target
 -n - do not $verb this hunk$target
 -q - quit; do not $verb this hunk or any of the remaining ones
 -a - $verb this hunk and all later hunks in the file
 -d - do not $verb this hunk or any of the later hunks in the file
 +      print colored $help_color, __($help_patch_modes{$patch_mode}), "\n", __ <<EOF ;
  g - select a hunk to go to
  / - search for a hunk matching the given regex
  j - leave this hunk undecided, see next undecided hunk
@@@ -1272,11 -1210,11 +1269,11 @@@ sub apply_patch_for_checkout_commit 
                run_git_apply 'apply '.$reverse, @_;
                return 1;
        } elsif (!$applies_index) {
 -              print colored $error_color, "The selected hunks do not apply to the index!\n";
 -              if (prompt_yesno "Apply them to the worktree anyway? ") {
 +              print colored $error_color, __("The selected hunks do not apply to the index!\n");
 +              if (prompt_yesno __("Apply them to the worktree anyway? ")) {
                        return run_git_apply 'apply '.$reverse, @_;
                } else {
 -                      print colored $error_color, "Nothing was applied.\n";
 +                      print colored $error_color, __("Nothing was applied.\n");
                        return 0;
                }
        } else {
  
  sub patch_update_cmd {
        my @all_mods = list_modified($patch_mode_flavour{FILTER});
 -      error_msg "ignoring unmerged: $_->{VALUE}\n"
 +      error_msg sprintf(__("ignoring unmerged: %s\n"), $_->{VALUE})
                for grep { $_->{UNMERGED} } @all_mods;
        @all_mods = grep { !$_->{UNMERGED} } @all_mods;
  
  
        if (!@mods) {
                if (@all_mods) {
 -                      print STDERR "Only binary files changed.\n";
 +                      print STDERR __("Only binary files changed.\n");
                } else {
 -                      print STDERR "No changes.\n";
 +                      print STDERR __("No changes.\n");
                }
                return 0;
        }
                @them = @mods;
        }
        else {
 -              @them = list_and_choose({ PROMPT => 'Patch update',
 +              @them = list_and_choose({ PROMPT => __('Patch update'),
                                          HEADER => $status_head, },
                                        @mods);
        }
@@@ -1356,44 -1294,6 +1353,44 @@@ sub display_hunks 
        return $i;
  }
  
 +my %patch_update_prompt_modes = (
 +      stage => {
 +              mode => N__("Stage mode change [y,n,q,a,d,/%s,?]? "),
 +              deletion => N__("Stage deletion [y,n,q,a,d,/%s,?]? "),
 +              hunk => N__("Stage this hunk [y,n,q,a,d,/%s,?]? "),
 +      },
 +      stash => {
 +              mode => N__("Stash mode change [y,n,q,a,d,/%s,?]? "),
 +              deletion => N__("Stash deletion [y,n,q,a,d,/%s,?]? "),
 +              hunk => N__("Stash this hunk [y,n,q,a,d,/%s,?]? "),
 +      },
 +      reset_head => {
 +              mode => N__("Unstage mode change [y,n,q,a,d,/%s,?]? "),
 +              deletion => N__("Unstage deletion [y,n,q,a,d,/%s,?]? "),
 +              hunk => N__("Unstage this hunk [y,n,q,a,d,/%s,?]? "),
 +      },
 +      reset_nothead => {
 +              mode => N__("Apply mode change to index [y,n,q,a,d,/%s,?]? "),
 +              deletion => N__("Apply deletion to index [y,n,q,a,d,/%s,?]? "),
 +              hunk => N__("Apply this hunk to index [y,n,q,a,d,/%s,?]? "),
 +      },
 +      checkout_index => {
 +              mode => N__("Discard mode change from worktree [y,n,q,a,d,/%s,?]? "),
 +              deletion => N__("Discard deletion from worktree [y,n,q,a,d,/%s,?]? "),
 +              hunk => N__("Discard this hunk from worktree [y,n,q,a,d,/%s,?]? "),
 +      },
 +      checkout_head => {
 +              mode => N__("Discard mode change from index and worktree [y,n,q,a,d,/%s,?]? "),
 +              deletion => N__("Discard deletion from index and worktree [y,n,q,a,d,/%s,?]? "),
 +              hunk => N__("Discard this hunk from index and worktree [y,n,q,a,d,/%s,?]? "),
 +      },
 +      checkout_nothead => {
 +              mode => N__("Apply mode change to index and worktree [y,n,q,a,d,/%s,?]? "),
 +              deletion => N__("Apply deletion to index and worktree [y,n,q,a,d,/%s,?]? "),
 +              hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d,/%s,?]? "),
 +      },
 +);
 +
  sub patch_update_file {
        my $quit = 0;
        my ($ix, $num);
                for (@{$hunk[$ix]{DISPLAY}}) {
                        print;
                }
 -              print colored $prompt_color, $patch_mode_flavour{VERB},
 -                ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' :
 -                 $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' :
 -                 ' this hunk'),
 -                $patch_mode_flavour{TARGET},
 -                " [y,n,q,a,d,/$other,?]? ";
 +              print colored $prompt_color,
 +                      sprintf(__($patch_update_prompt_modes{$patch_mode}{$hunk[$ix]{TYPE}}), $other);
 +
                my $line = prompt_single_character;
                last unless defined $line;
                if ($line) {
                                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 (<ret> to see more)? ");
 +                                      } else {
 +                                              print __("go to which hunk? ");
                                        }
 -                                      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";
 +                                      error_msg sprintf(__("Invalid number: '%s'\n"),
 +                                                           $response);
                                } elsif (0 < $response && $response <= $num) {
                                        $ix = $response - 1;
                                } else {
 -                                      error_msg "Sorry, only $num hunks available.\n";
 +                                      error_msg sprintf(__n("Sorry, only %d hunk available.\n",
 +                                                            "Sorry, only %d hunks available.\n", $num), $num);
                                }
                                next;
                        }
                        elsif ($line =~ m|^/(.*)|) {
                                my $regex = $1;
                                if ($1 eq "") {
 -                                      print colored $prompt_color, "search for regex? ";
 +                                      print colored $prompt_color, __("search for regex? ");
                                        $regex = <STDIN>;
                                        if (defined $regex) {
                                                chomp $regex;
                                if ($@) {
                                        my ($err,$exp) = ($@, $1);
                                        $err =~ s/ at .*git-add--interactive line \d+, <STDIN> line \d+.*$//;
 -                                      error_msg "Malformed search regexp $exp: $err\n";
 +                                      error_msg sprintf(__("Malformed search regexp %s: %s\n"), $exp, $err);
                                        next;
                                }
                                my $iy = $ix;
                                        $iy++;
                                        $iy = 0 if ($iy >= $num);
                                        if ($ix == $iy) {
 -                                              error_msg "No hunk matches the given pattern\n";
 +                                              error_msg __("No hunk matches the given pattern\n");
                                                last;
                                        }
                                }
                                        $ix--;
                                }
                                else {
 -                                      error_msg "No previous hunk\n";
 +                                      error_msg __("No previous hunk\n");
                                }
                                next;
                        }
                                        $ix++;
                                }
                                else {
 -                                      error_msg "No next hunk\n";
 +                                      error_msg __("No next hunk\n");
                                }
                                next;
                        }
                                        }
                                }
                                else {
 -                                      error_msg "No previous hunk\n";
 +                                      error_msg __("No previous hunk\n");
                                }
                                next;
                        }
                        elsif ($line =~ /^j/) {
                                if ($other !~ /j/) {
 -                                      error_msg "No next hunk\n";
 +                                      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) {
 -                                      print colored $header_color, "Split into ",
 -                                      scalar(@split), " hunks.\n";
 +                                      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;
@@@ -1658,25 -1557,23 +1655,25 @@@ sub diff_cmd 
        my @mods = list_modified('index-only');
        @mods = grep { !($_->{BINARY}) } @mods;
        return if (!@mods);
 -      my (@them) = list_and_choose({ PROMPT => 'Review diff',
 +      my (@them) = list_and_choose({ PROMPT => __('Review diff'),
                                     IMMEDIATE => 1,
                                     HEADER => $status_head, },
                                   @mods);
        return if (!@them);
 -      my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
 +      my $reference = (is_initial_commit()) ? get_empty_tree() : 'HEAD';
        system(qw(git diff -p --cached), $reference, '--',
                map { $_->{VALUE} } @them);
  }
  
  sub quit_cmd {
 -      print "Bye.\n";
 +      print __("Bye.\n");
        exit(0);
  }
  
  sub help_cmd {
 -      print colored $help_color, <<\EOF ;
 +# TRANSLATORS: please do not translate the command names
 +# 'status', 'update', 'revert', etc.
 +      print colored $help_color, __ <<'EOF' ;
  status        - show paths with changes
  update        - add working tree state to the staged set of changes
  revert        - revert staged set of changes back to the HEAD version
@@@ -1694,40 -1591,39 +1691,40 @@@ sub process_args 
                        if ($1 eq 'reset') {
                                $patch_mode = 'reset_head';
                                $patch_mode_revision = 'HEAD';
 -                              $arg = shift @ARGV or die "missing --";
 +                              $arg = shift @ARGV or die __("missing --");
                                if ($arg ne '--') {
                                        $patch_mode_revision = $arg;
                                        $patch_mode = ($arg eq 'HEAD' ?
                                                       'reset_head' : 'reset_nothead');
 -                                      $arg = shift @ARGV or die "missing --";
 +                                      $arg = shift @ARGV or die __("missing --");
                                }
                        } elsif ($1 eq 'checkout') {
 -                              $arg = shift @ARGV or die "missing --";
 +                              $arg = shift @ARGV or die __("missing --");
                                if ($arg eq '--') {
                                        $patch_mode = 'checkout_index';
                                } else {
                                        $patch_mode_revision = $arg;
                                        $patch_mode = ($arg eq 'HEAD' ?
                                                       'checkout_head' : 'checkout_nothead');
 -                                      $arg = shift @ARGV or die "missing --";
 +                                      $arg = shift @ARGV or die __("missing --");
                                }
                        } elsif ($1 eq 'stage' or $1 eq 'stash') {
                                $patch_mode = $1;
 -                              $arg = shift @ARGV or die "missing --";
 +                              $arg = shift @ARGV or die __("missing --");
                        } else {
 -                              die "unknown --patch mode: $1";
 +                              die sprintf(__("unknown --patch mode: %s"), $1);
                        }
                } else {
                        $patch_mode = 'stage';
 -                      $arg = shift @ARGV or die "missing --";
 +                      $arg = shift @ARGV or die __("missing --");
                }
 -              die "invalid argument $arg, expecting --"
 -                  unless $arg eq "--";
 +              die sprintf(__("invalid argument %s, expecting --"),
 +                             $arg) unless $arg eq "--";
                %patch_mode_flavour = %{$patch_modes{$patch_mode}};
 +              $cmd = 1;
        }
        elsif ($arg ne "--") {
 -              die "invalid argument $arg, expecting --";
 +              die sprintf(__("invalid argument %s, expecting --"), $arg);
        }
  }
  
@@@ -1742,10 -1638,10 +1739,10 @@@ sub main_loop 
                   [ 'help', \&help_cmd, ],
        );
        while (1) {
 -              my ($it) = list_and_choose({ PROMPT => 'What now',
 +              my ($it) = list_and_choose({ PROMPT => __('What now'),
                                             SINGLETON => 1,
                                             LIST_FLAT => 4,
 -                                           HEADER => '*** Commands ***',
 +                                           HEADER => __('*** Commands ***'),
                                             ON_EOF => \&quit_cmd,
                                             IMMEDIATE => 1 }, @cmd);
                if ($it) {
  
  process_args();
  refresh();
 -if ($patch_mode) {
 +if ($cmd) {
        patch_update_cmd();
  }
  else {