dir: convert struct untracked_cache_dir to object_id
[gitweb.git] / git-add--interactive.perl
index 6f6a21dc11e23be13a14beb24ed670c232925f4a..c1f52e457f6c5b03947766555e10fba33e7e1b38 100755 (executable)
@@ -262,7 +262,7 @@ sub list_modified {
                }
        }
 
-       for (run_cmd_pipe(qw(git diff-files --numstat --summary --raw --), @ARGV)) {
+       for (run_cmd_pipe(qw(git diff-files --ignore-submodules=dirty --numstat --summary --raw --), @ARGV)) {
                if (($add, $del, $file) =
                    /^([-\d]+)  ([-\d]+)        (.*)/) {
                        $file = unquote_path($file);
@@ -677,7 +677,7 @@ sub add_untracked_cmd {
 sub run_git_apply {
        my $cmd = shift;
        my $fh;
-       open $fh, '| git ' . $cmd . " --recount --allow-overlap";
+       open $fh, '| git ' . $cmd . " --allow-overlap";
        print $fh @_;
        return close $fh;
 }
@@ -705,6 +705,14 @@ sub parse_diff {
        }
        my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
 
+       if (@colored && @colored != @diff) {
+               print STDERR
+                 "fatal: mismatched output from interactive.diffFilter\n",
+                 "hint: Your filter must maintain a one-to-one correspondence\n",
+                 "hint: between its input and output lines.\n";
+               exit 1;
+       }
+
        for (my $i = 0; $i < @diff; $i++) {
                if ($diff[$i] =~ /^@@ /) {
                        push @hunk, { TEXT => [], DISPLAY => [],
@@ -793,6 +801,11 @@ sub split_hunk {
                while (++$i < @$text) {
                        my $line = $text->[$i];
                        my $display = $display->[$i];
+                       if ($line =~ /^\\/) {
+                               push @{$this->{TEXT}}, $line;
+                               push @{$this->{DISPLAY}}, $display;
+                               next;
+                       }
                        if ($line =~ /^ /) {
                                if ($this->{ADDDEL} &&
                                    !defined $next_hunk_start) {
@@ -891,6 +904,9 @@ sub merge_hunk {
                        $n_cnt++;
                        push @line, $line;
                        next;
+               } elsif ($line =~ /^\\/) {
+                       push @line, $line;
+                       next;
                }
 
                last if ($o1_ofs <= $ofs);
@@ -909,6 +925,9 @@ sub merge_hunk {
                        $n_cnt++;
                        push @line, $line;
                        next;
+               } elsif ($line =~ /^\\/) {
+                       push @line, $line;
+                       next;
                }
                $ofs++;
                $o_cnt++;
@@ -938,6 +957,11 @@ sub coalesce_overlapping_hunks {
                                                parse_hunk_header($text->[0]);
                unless ($_->{USE}) {
                        $ofs_delta += $o_cnt - $n_cnt;
+                       # If this hunk has been edited then subtract
+                       # the delta that is due to the edit.
+                       if ($_->{OFS_DELTA}) {
+                               $ofs_delta -= $_->{OFS_DELTA};
+                       }
                        next;
                }
                if ($ofs_delta) {
@@ -945,6 +969,11 @@ sub coalesce_overlapping_hunks {
                        $_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
                                                             $n_ofs, $n_cnt);
                }
+               # If this hunk was edited then adjust the offset delta
+               # to reflect the edit.
+               if ($_->{OFS_DELTA}) {
+                       $ofs_delta += $_->{OFS_DELTA};
+               }
                if (defined $last_o_ctx &&
                    $o_ofs <= $last_o_ctx &&
                    !$_->{DIRTY} &&
@@ -1016,6 +1045,30 @@ sub color_diff {
 marked for applying."),
 );
 
+sub recount_edited_hunk {
+       local $_;
+       my ($oldtext, $newtext) = @_;
+       my ($o_cnt, $n_cnt) = (0, 0);
+       for (@{$newtext}[1..$#{$newtext}]) {
+               my $mode = substr($_, 0, 1);
+               if ($mode eq '-') {
+                       $o_cnt++;
+               } elsif ($mode eq '+') {
+                       $n_cnt++;
+               } elsif ($mode eq ' ') {
+                       $o_cnt++;
+                       $n_cnt++;
+               }
+       }
+       my ($o_ofs, undef, $n_ofs, undef) =
+                                       parse_hunk_header($newtext->[0]);
+       $newtext->[0] = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);
+       my (undef, $orig_o_cnt, undef, $orig_n_cnt) =
+                                       parse_hunk_header($oldtext->[0]);
+       # Return the change in the number of lines inserted by this hunk
+       return $orig_o_cnt - $orig_n_cnt - $o_cnt + $n_cnt;
+}
+
 sub edit_hunk_manually {
        my ($oldtext) = @_;
 
@@ -1114,25 +1167,32 @@ sub prompt_yesno {
 }
 
 sub edit_hunk_loop {
-       my ($head, $hunk, $ix) = @_;
-       my $text = $hunk->[$ix]->{TEXT};
+       my ($head, $hunks, $ix) = @_;
+       my $hunk = $hunks->[$ix];
+       my $text = $hunk->{TEXT};
 
        while (1) {
-               $text = edit_hunk_manually($text);
-               if (!defined $text) {
+               my $newtext = edit_hunk_manually($text);
+               if (!defined $newtext) {
                        return undef;
                }
                my $newhunk = {
-                       TEXT => $text,
-                       TYPE => $hunk->[$ix]->{TYPE},
+                       TEXT => $newtext,
+                       TYPE => $hunk->{TYPE},
                        USE => 1,
                        DIRTY => 1,
                };
+               $newhunk->{OFS_DELTA} = recount_edited_hunk($text, $newtext);
+               # If this hunk has already been edited then add the
+               # offset delta of the previous edit to get the real
+               # delta from the original unedited hunk.
+               $hunk->{OFS_DELTA} and
+                               $newhunk->{OFS_DELTA} += $hunk->{OFS_DELTA};
                if (diff_applies($head,
-                                @{$hunk}[0..$ix-1],
+                                @{$hunks}[0..$ix-1],
                                 $newhunk,
-                                @{$hunk}[$ix+1..$#{$hunk}])) {
-                       $newhunk->{DISPLAY} = [color_diff(@{$text})];
+                                @{$hunks}[$ix+1..$#{$hunks}])) {
+                       $newhunk->{DISPLAY} = [color_diff(@{$newtext})];
                        return $newhunk;
                }
                else {
@@ -1196,7 +1256,13 @@ sub edit_hunk_loop {
 );
 
 sub help_patch_cmd {
-       print colored $help_color, __($help_patch_modes{$patch_mode}), "\n", __ <<EOF ;
+       local $_;
+       my $other = $_[0] . ",?";
+       print colored $help_color, __($help_patch_modes{$patch_mode}), "\n",
+               map { "$_\n" } grep {
+                       my $c = quotemeta(substr($_, 0, 1));
+                       $other =~ /,$c/
+               } split "\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
@@ -1314,39 +1380,39 @@ sub display_hunks {
 
 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,?]? "),
+               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,?]? "),
+               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,?]? "),
+               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,?]? "),
+               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,?]? "),
+               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,?]? "),
+               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,?]? "),
+               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,?]? "),
        },
 );
 
@@ -1402,7 +1468,7 @@ sub patch_update_file {
                        $other .= ',J';
                }
                if ($num > 1) {
-                       $other .= ',g';
+                       $other .= ',g,/';
                }
                for ($i = 0; $i < $num; $i++) {
                        if (!defined $hunk[$i]{USE}) {
@@ -1443,8 +1509,12 @@ sub patch_update_file {
                                }
                                next;
                        }
-                       elsif ($other =~ /g/ && $line =~ /^g(.*)/) {
+                       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);
@@ -1490,7 +1560,11 @@ sub patch_update_file {
                        }
                        elsif ($line =~ m|^/(.*)|) {
                                my $regex = $1;
-                               if ($1 eq "") {
+                               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) {
@@ -1558,7 +1632,11 @@ sub patch_update_file {
                                        next;
                                }
                        }
-                       elsif ($other =~ /s/ && $line =~ /^s/) {
+                       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(
@@ -1570,7 +1648,11 @@ sub patch_update_file {
                                $num = scalar @hunk;
                                next;
                        }
-                       elsif ($other =~ /e/ && $line =~ /^e/) {
+                       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;