Merge branch 'js/checkout-p-new-file'

"git checkout -p" did not handle a newly added path at all.

* js/checkout-p-new-file:
  checkout -p: handle new files correctly
This commit is contained in:
Junio C Hamano 2020-06-08 18:06:31 -07:00
Родитель b37fd14beb 2c8bd8471a
Коммит 2bdf00e66a
3 изменённых файлов: 61 добавлений и 9 удалений

Просмотреть файл

@ -10,7 +10,7 @@
#include "prompt.h" #include "prompt.h"
enum prompt_mode_type { enum prompt_mode_type {
PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK, PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_ADDITION, PROMPT_HUNK,
PROMPT_MODE_MAX, /* must be last */ PROMPT_MODE_MAX, /* must be last */
}; };
@ -33,6 +33,7 @@ static struct patch_mode patch_mode_add = {
.prompt_mode = { .prompt_mode = {
N_("Stage mode change [y,n,q,a,d%s,?]? "), N_("Stage mode change [y,n,q,a,d%s,?]? "),
N_("Stage deletion [y,n,q,a,d%s,?]? "), N_("Stage deletion [y,n,q,a,d%s,?]? "),
N_("Stage addition [y,n,q,a,d%s,?]? "),
N_("Stage this hunk [y,n,q,a,d%s,?]? ") N_("Stage this hunk [y,n,q,a,d%s,?]? ")
}, },
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@ -54,6 +55,7 @@ static struct patch_mode patch_mode_stash = {
.prompt_mode = { .prompt_mode = {
N_("Stash mode change [y,n,q,a,d%s,?]? "), N_("Stash mode change [y,n,q,a,d%s,?]? "),
N_("Stash deletion [y,n,q,a,d%s,?]? "), N_("Stash deletion [y,n,q,a,d%s,?]? "),
N_("Stash addition [y,n,q,a,d%s,?]? "),
N_("Stash this hunk [y,n,q,a,d%s,?]? "), N_("Stash this hunk [y,n,q,a,d%s,?]? "),
}, },
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@ -77,6 +79,7 @@ static struct patch_mode patch_mode_reset_head = {
.prompt_mode = { .prompt_mode = {
N_("Unstage mode change [y,n,q,a,d%s,?]? "), N_("Unstage mode change [y,n,q,a,d%s,?]? "),
N_("Unstage deletion [y,n,q,a,d%s,?]? "), N_("Unstage deletion [y,n,q,a,d%s,?]? "),
N_("Unstage addition [y,n,q,a,d%s,?]? "),
N_("Unstage this hunk [y,n,q,a,d%s,?]? "), N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
}, },
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@ -99,6 +102,7 @@ static struct patch_mode patch_mode_reset_nothead = {
.prompt_mode = { .prompt_mode = {
N_("Apply mode change to index [y,n,q,a,d%s,?]? "), N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index [y,n,q,a,d%s,?]? "), N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
N_("Apply addition to index [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index [y,n,q,a,d%s,?]? "), N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
}, },
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@ -121,6 +125,7 @@ static struct patch_mode patch_mode_checkout_index = {
.prompt_mode = { .prompt_mode = {
N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "), N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "), N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "), N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
}, },
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@ -143,6 +148,7 @@ static struct patch_mode patch_mode_checkout_head = {
.prompt_mode = { .prompt_mode = {
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "), N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "), N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "), N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
}, },
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@ -164,6 +170,7 @@ static struct patch_mode patch_mode_checkout_nothead = {
.prompt_mode = { .prompt_mode = {
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "), N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "), N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "), N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
}, },
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@ -186,6 +193,7 @@ static struct patch_mode patch_mode_worktree_head = {
.prompt_mode = { .prompt_mode = {
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "), N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "), N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "), N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
}, },
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@ -207,6 +215,7 @@ static struct patch_mode patch_mode_worktree_nothead = {
.prompt_mode = { .prompt_mode = {
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "), N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "), N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "), N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
}, },
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@ -248,7 +257,7 @@ struct add_p_state {
struct hunk head; struct hunk head;
struct hunk *hunk; struct hunk *hunk;
size_t hunk_nr, hunk_alloc; size_t hunk_nr, hunk_alloc;
unsigned deleted:1, mode_change:1,binary:1; unsigned deleted:1, added:1, mode_change:1,binary:1;
} *file_diff; } *file_diff;
size_t file_diff_nr; size_t file_diff_nr;
@ -442,7 +451,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
pend = p + plain->len; pend = p + plain->len;
while (p != pend) { while (p != pend) {
char *eol = memchr(p, '\n', pend - p); char *eol = memchr(p, '\n', pend - p);
const char *deleted = NULL, *mode_change = NULL; const char *deleted = NULL, *added = NULL, *mode_change = NULL;
if (!eol) if (!eol)
eol = pend; eol = pend;
@ -461,11 +470,12 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
} else if (p == plain->buf) } else if (p == plain->buf)
BUG("diff starts with unexpected line:\n" BUG("diff starts with unexpected line:\n"
"%.*s\n", (int)(eol - p), p); "%.*s\n", (int)(eol - p), p);
else if (file_diff->deleted) else if (file_diff->deleted || file_diff->added)
; /* keep the rest of the file in a single "hunk" */ ; /* keep the rest of the file in a single "hunk" */
else if (starts_with(p, "@@ ") || else if (starts_with(p, "@@ ") ||
(hunk == &file_diff->head && (hunk == &file_diff->head &&
skip_prefix(p, "deleted file", &deleted))) { (skip_prefix(p, "deleted file", &deleted) ||
skip_prefix(p, "new file", &added)))) {
if (marker == '-' || marker == '+') if (marker == '-' || marker == '+')
/* /*
* Should not happen; previous hunk did not end * Should not happen; previous hunk did not end
@ -485,6 +495,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
if (deleted) if (deleted)
file_diff->deleted = 1; file_diff->deleted = 1;
else if (added)
file_diff->added = 1;
else if (parse_hunk_header(s, hunk) < 0) else if (parse_hunk_header(s, hunk) < 0)
return -1; return -1;
@ -537,8 +549,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
starts_with(p, "Binary files ")) starts_with(p, "Binary files "))
file_diff->binary = 1; file_diff->binary = 1;
if (file_diff->deleted && file_diff->mode_change) if (!!file_diff->deleted + !!file_diff->added +
BUG("diff contains delete *and* a mode change?!?\n%.*s", !!file_diff->mode_change > 1)
BUG("diff can only contain delete *or* add *or* a "
"mode change?!?\n%.*s",
(int)(eol - (plain->buf + file_diff->head.start)), (int)(eol - (plain->buf + file_diff->head.start)),
plain->buf + file_diff->head.start); plain->buf + file_diff->head.start);
@ -1397,6 +1411,8 @@ static int patch_update_file(struct add_p_state *s,
if (file_diff->deleted) if (file_diff->deleted)
prompt_mode_type = PROMPT_DELETION; prompt_mode_type = PROMPT_DELETION;
else if (file_diff->added)
prompt_mode_type = PROMPT_ADDITION;
else if (file_diff->mode_change && !hunk_index) else if (file_diff->mode_change && !hunk_index)
prompt_mode_type = PROMPT_MODE_CHANGE; prompt_mode_type = PROMPT_MODE_CHANGE;
else else

Просмотреть файл

@ -754,16 +754,18 @@ sub parse_diff_header {
my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' }; my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };
my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' }; my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };
my $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' }; my $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };
my $addition = { TEXT => [], DISPLAY => [], TYPE => 'addition' };
for (my $i = 0; $i < @{$src->{TEXT}}; $i++) { for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
my $dest = my $dest =
$src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode : $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode :
$src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion : $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :
$src->{TEXT}->[$i] =~ /^new file/ ? $addition :
$head; $head;
push @{$dest->{TEXT}}, $src->{TEXT}->[$i]; push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i]; push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
} }
return ($head, $mode, $deletion); return ($head, $mode, $deletion, $addition);
} }
sub hunk_splittable { sub hunk_splittable {
@ -1427,46 +1429,55 @@ my %patch_update_prompt_modes = (
stage => { stage => {
mode => N__("Stage mode change [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,?]? "), deletion => N__("Stage deletion [y,n,q,a,d%s,?]? "),
addition => N__("Stage addition [y,n,q,a,d%s,?]? "),
hunk => N__("Stage this hunk [y,n,q,a,d%s,?]? "), hunk => N__("Stage this hunk [y,n,q,a,d%s,?]? "),
}, },
stash => { stash => {
mode => N__("Stash mode change [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,?]? "), deletion => N__("Stash deletion [y,n,q,a,d%s,?]? "),
addition => N__("Stash addition [y,n,q,a,d%s,?]? "),
hunk => N__("Stash this hunk [y,n,q,a,d%s,?]? "), hunk => N__("Stash this hunk [y,n,q,a,d%s,?]? "),
}, },
reset_head => { reset_head => {
mode => N__("Unstage mode change [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,?]? "), deletion => N__("Unstage deletion [y,n,q,a,d%s,?]? "),
addition => N__("Unstage addition [y,n,q,a,d%s,?]? "),
hunk => N__("Unstage this hunk [y,n,q,a,d%s,?]? "), hunk => N__("Unstage this hunk [y,n,q,a,d%s,?]? "),
}, },
reset_nothead => { reset_nothead => {
mode => N__("Apply mode change 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,?]? "), deletion => N__("Apply deletion to index [y,n,q,a,d%s,?]? "),
addition => N__("Apply addition to index [y,n,q,a,d%s,?]? "),
hunk => N__("Apply this hunk to index [y,n,q,a,d%s,?]? "), hunk => N__("Apply this hunk to index [y,n,q,a,d%s,?]? "),
}, },
checkout_index => { checkout_index => {
mode => N__("Discard mode change 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,?]? "), deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
addition => N__("Discard addition from worktree [y,n,q,a,d%s,?]? "),
hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "), hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
}, },
checkout_head => { checkout_head => {
mode => N__("Discard mode change 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,?]? "), deletion => N__("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
addition => N__("Discard addition 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,?]? "), hunk => N__("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
}, },
checkout_nothead => { checkout_nothead => {
mode => N__("Apply mode change 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,?]? "), deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
addition => N__("Apply addition 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,?]? "), hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
}, },
worktree_head => { worktree_head => {
mode => N__("Discard mode change 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,?]? "), deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
addition => N__("Discard addition from worktree [y,n,q,a,d%s,?]? "),
hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "), hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
}, },
worktree_nothead => { worktree_nothead => {
mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "), mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "), deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
addition => N__("Apply addition to worktree [y,n,q,a,d%s,?]? "),
hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "), hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
}, },
); );
@ -1476,7 +1487,7 @@ sub patch_update_file {
my ($ix, $num); my ($ix, $num);
my $path = shift; my $path = shift;
my ($head, @hunk) = parse_diff($path); my ($head, @hunk) = parse_diff($path);
($head, my $mode, my $deletion) = parse_diff_header($head); ($head, my $mode, my $deletion, my $addition) = parse_diff_header($head);
for (@{$head->{DISPLAY}}) { for (@{$head->{DISPLAY}}) {
print; print;
} }
@ -1490,6 +1501,12 @@ sub patch_update_file {
push @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}}; push @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};
} }
@hunk = ($deletion); @hunk = ($deletion);
} elsif (@{$addition->{TEXT}}) {
foreach my $hunk (@hunk) {
push @{$addition->{TEXT}}, @{$hunk->{TEXT}};
push @{$addition->{DISPLAY}}, @{$hunk->{DISPLAY}};
}
@hunk = ($addition);
} }
$num = scalar @hunk; $num = scalar @hunk;

Просмотреть файл

@ -412,6 +412,25 @@ test_expect_success 'deleting an empty file' '
diff_cmp expected diff diff_cmp expected diff
' '
test_expect_success 'adding an empty file' '
git init added &&
(
cd added &&
test_commit initial &&
>empty &&
git add empty &&
test_tick &&
git commit -m empty &&
git tag added-file &&
git reset --hard HEAD^ &&
test_path_is_missing empty &&
echo y | git checkout -p added-file -- >actual &&
test_path_is_file empty &&
test_i18ngrep "Apply addition to index and worktree" actual
)
'
test_expect_success 'split hunk setup' ' test_expect_success 'split hunk setup' '
git reset --hard && git reset --hard &&
test_write_lines 10 20 30 40 50 60 >test && test_write_lines 10 20 30 40 50 60 >test &&