}
}
-sub get_empty_tree {
- return '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
+{
+ my $empty_tree;
+ sub get_empty_tree {
+ return $empty_tree if defined $empty_tree;
+
+ $empty_tree = run_cmd_pipe(qw(git hash-object -t tree /dev/null));
+ chomp $empty_tree;
+ return $empty_tree;
+ }
}
sub get_diff_reference {
}
}
- 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);
}
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 => [],
next;
}
if ($ofs_delta) {
- $n_ofs += $ofs_delta;
+ if ($patch_mode_flavour{IS_REVERSE}) {
+ $o_ofs -= $ofs_delta;
+ } else {
+ $n_ofs += $ofs_delta;
+ }
$_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
$n_ofs, $n_cnt);
}
);
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
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,?]? "),
},
);
$other .= ',J';
}
if ($num > 1) {
- $other .= ',g';
+ $other .= ',g,/';
}
for ($i = 0; $i < $num; $i++) {
if (!defined $hunk[$i]{USE}) {
}
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);
}
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) {
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(
$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;
'
test_expect_success 'diff works (initial)' '
- (echo d; echo 1) | git add -i >output &&
+ test_write_lines d 1 | git add -i >output &&
sed -ne "/new file/,/content/p" <output >diff &&
diff_cmp expected diff
'
test_expect_success 'revert works (initial)' '
git add file &&
- (echo r; echo 1) | git add -i &&
+ test_write_lines r 1 | git add -i &&
git ls-files >output &&
! grep . output
'
'
test_expect_success 'diff works (commit)' '
- (echo d; echo 1) | git add -i >output &&
+ test_write_lines d 1 | git add -i >output &&
sed -ne "/^index/,/content/p" <output >diff &&
diff_cmp expected diff
'
test_expect_success 'revert works (commit)' '
git add file &&
- (echo r; echo 1) | git add -i &&
+ test_write_lines r 1 | git add -i &&
git add -i </dev/null >output &&
grep "unchanged *+3/-0 file" output
'
test_expect_success 'dummy edit works' '
test_set_editor : &&
- (echo e; echo a) | git add -p &&
+ test_write_lines e a | git add -p &&
git diff > diff &&
diff_cmp expected diff
'
test_expect_success 'bad edit rejected' '
git reset &&
- (echo e; echo n; echo d) | git add -p >output &&
+ test_write_lines e n d | git add -p >output &&
grep "hunk does not apply" output
'
test_expect_success 'garbage edit rejected' '
git reset &&
- (echo e; echo n; echo d) | git add -p >output &&
+ test_write_lines e n d | git add -p >output &&
grep "hunk does not apply" output
'
'
test_expect_success 'real edit works' '
- (echo e; echo n; echo d) | git add -p &&
+ test_write_lines e n d | git add -p &&
git diff >output &&
diff_cmp expected output
'
grep "$(printf "\\033")" output
'
+test_expect_success TTY 'diffFilter filters diff' '
+ git reset --hard &&
+
+ echo content >test &&
+ test_config interactive.diffFilter "sed s/^/foo:/" &&
+ printf y | test_terminal git add -p >output 2>&1 &&
+
+ # avoid depending on the exact coloring or content of the prompts,
+ # and just make sure we saw our diff prefixed
+ grep foo:.*content output
+'
+
+test_expect_success TTY 'detect bogus diffFilter output' '
+ git reset --hard &&
+
+ echo content >test &&
+ test_config interactive.diffFilter "echo too-short" &&
+ printf y | test_must_fail test_terminal git add -p
+'
+
test_expect_success 'patch-mode via -i prompts for files' '
git reset --hard &&
# update it, but we want to be sure that our "." pathspec
# was not expanded into the argument list of any command.
# So look only for "not-changed".
- ! grep not-changed trace.out
+ ! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out
'
test_expect_success 'hunk-editing handles custom comment char' '
test_cmp expect actual
'
+test_expect_success 'setup different kinds of dirty submodules' '
+ test_create_repo for-submodules &&
+ (
+ cd for-submodules &&
+ test_commit initial &&
+ test_create_repo dirty-head &&
+ (
+ cd dirty-head &&
+ test_commit initial
+ ) &&
+ cp -R dirty-head dirty-otherwise &&
+ cp -R dirty-head dirty-both-ways &&
+ git add dirty-head &&
+ git add dirty-otherwise dirty-both-ways &&
+ git commit -m initial &&
+
+ cd dirty-head &&
+ test_commit updated &&
+ cd ../dirty-both-ways &&
+ test_commit updated &&
+ echo dirty >>initial &&
+ : >untracked &&
+ cd ../dirty-otherwise &&
+ echo dirty >>initial &&
+ : >untracked
+ ) &&
+ git -C for-submodules diff-files --name-only >actual &&
+ cat >expected <<-\EOF &&
+ dirty-both-ways
+ dirty-head
+ dirty-otherwise
+ EOF
+ test_cmp expected actual &&
+ git -C for-submodules diff-files --name-only --ignore-submodules=dirty >actual &&
+ cat >expected <<-\EOF &&
+ dirty-both-ways
+ dirty-head
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'status ignores dirty submodules (except HEAD)' '
+ git -C for-submodules add -i </dev/null >output &&
+ grep dirty-head output &&
+ grep dirty-both-ways output &&
+ ! grep dirty-otherwise output
+'
+
test_expect_success 'set up pathological context' '
git reset --hard &&
test_write_lines a a a a a a a a a a a >a &&
test_cmp expected-2 actual
'
+ test_expect_success 'checkout -p works with pathological context lines' '
+ test_write_lines a a a a a a >a &&
+ git add a &&
+ test_write_lines a b a b a b a b a b a > a&&
+ test_write_lines s n n y q | git checkout -p &&
+ test_write_lines a b a b a a b a b a >expect &&
+ test_cmp expect a
+ '
test_done