Merge branch 'pw/add-p-edit-ita-path'
"add -p" now allows editing paths that were only added in intent. * pw/add-p-edit-ita-path: add -p: fix editing of intent-to-add paths
This commit is contained in:
commit
458205ff0f
42
add-patch.c
42
add-patch.c
@ -465,7 +465,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, *added = NULL, *mode_change = NULL;
|
const char *deleted = NULL, *mode_change = NULL;
|
||||||
|
|
||||||
if (!eol)
|
if (!eol)
|
||||||
eol = pend;
|
eol = pend;
|
||||||
@ -482,12 +482,11 @@ 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 || file_diff->added)
|
else if (file_diff->deleted)
|
||||||
; /* 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
|
||||||
@ -505,8 +504,6 @@ 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;
|
||||||
|
|
||||||
@ -515,6 +512,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
|||||||
* split
|
* split
|
||||||
*/
|
*/
|
||||||
marker = *p;
|
marker = *p;
|
||||||
|
} else if (hunk == &file_diff->head &&
|
||||||
|
starts_with(p, "new file")) {
|
||||||
|
file_diff->added = 1;
|
||||||
} else if (hunk == &file_diff->head &&
|
} else if (hunk == &file_diff->head &&
|
||||||
skip_prefix(p, "old mode ", &mode_change) &&
|
skip_prefix(p, "old mode ", &mode_change) &&
|
||||||
is_octal(mode_change, eol - mode_change)) {
|
is_octal(mode_change, eol - mode_change)) {
|
||||||
@ -1376,7 +1376,8 @@ static int patch_update_file(struct add_p_state *s,
|
|||||||
ALLOW_EDIT = 1 << 6
|
ALLOW_EDIT = 1 << 6
|
||||||
} permitted = 0;
|
} permitted = 0;
|
||||||
|
|
||||||
if (!file_diff->hunk_nr)
|
/* Empty added files have no hunks */
|
||||||
|
if (!file_diff->hunk_nr && !file_diff->added)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
strbuf_reset(&s->buf);
|
strbuf_reset(&s->buf);
|
||||||
@ -1385,21 +1386,25 @@ static int patch_update_file(struct add_p_state *s,
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
if (hunk_index >= file_diff->hunk_nr)
|
if (hunk_index >= file_diff->hunk_nr)
|
||||||
hunk_index = 0;
|
hunk_index = 0;
|
||||||
hunk = file_diff->hunk + hunk_index;
|
hunk = file_diff->hunk_nr
|
||||||
|
? file_diff->hunk + hunk_index
|
||||||
|
: &file_diff->head;
|
||||||
undecided_previous = -1;
|
undecided_previous = -1;
|
||||||
|
undecided_next = -1;
|
||||||
|
|
||||||
|
if (file_diff->hunk_nr) {
|
||||||
for (i = hunk_index - 1; i >= 0; i--)
|
for (i = hunk_index - 1; i >= 0; i--)
|
||||||
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
|
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
|
||||||
undecided_previous = i;
|
undecided_previous = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
undecided_next = -1;
|
|
||||||
for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
|
for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
|
||||||
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
|
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
|
||||||
undecided_next = i;
|
undecided_next = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Everything decided? */
|
/* Everything decided? */
|
||||||
if (undecided_previous < 0 && undecided_next < 0 &&
|
if (undecided_previous < 0 && undecided_next < 0 &&
|
||||||
@ -1407,6 +1412,7 @@ static int patch_update_file(struct add_p_state *s,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
strbuf_reset(&s->buf);
|
strbuf_reset(&s->buf);
|
||||||
|
if (file_diff->hunk_nr) {
|
||||||
render_hunk(s, hunk, 0, colored, &s->buf);
|
render_hunk(s, hunk, 0, colored, &s->buf);
|
||||||
fputs(s->buf.buf, stdout);
|
fputs(s->buf.buf, stdout);
|
||||||
|
|
||||||
@ -1440,6 +1446,7 @@ static int patch_update_file(struct add_p_state *s,
|
|||||||
permitted |= ALLOW_EDIT;
|
permitted |= ALLOW_EDIT;
|
||||||
strbuf_addstr(&s->buf, ",e");
|
strbuf_addstr(&s->buf, ",e");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (file_diff->deleted)
|
if (file_diff->deleted)
|
||||||
prompt_mode_type = PROMPT_DELETION;
|
prompt_mode_type = PROMPT_DELETION;
|
||||||
else if (file_diff->added)
|
else if (file_diff->added)
|
||||||
@ -1452,7 +1459,9 @@ static int patch_update_file(struct add_p_state *s,
|
|||||||
color_fprintf(stdout, s->s.prompt_color,
|
color_fprintf(stdout, s->s.prompt_color,
|
||||||
"(%"PRIuMAX"/%"PRIuMAX") ",
|
"(%"PRIuMAX"/%"PRIuMAX") ",
|
||||||
(uintmax_t)hunk_index + 1,
|
(uintmax_t)hunk_index + 1,
|
||||||
(uintmax_t)file_diff->hunk_nr);
|
(uintmax_t)(file_diff->hunk_nr
|
||||||
|
? file_diff->hunk_nr
|
||||||
|
: 1));
|
||||||
color_fprintf(stdout, s->s.prompt_color,
|
color_fprintf(stdout, s->s.prompt_color,
|
||||||
_(s->mode->prompt_mode[prompt_mode_type]),
|
_(s->mode->prompt_mode[prompt_mode_type]),
|
||||||
s->buf.buf);
|
s->buf.buf);
|
||||||
@ -1472,17 +1481,25 @@ soft_increment:
|
|||||||
hunk->use = SKIP_HUNK;
|
hunk->use = SKIP_HUNK;
|
||||||
goto soft_increment;
|
goto soft_increment;
|
||||||
} else if (ch == 'a') {
|
} else if (ch == 'a') {
|
||||||
|
if (file_diff->hunk_nr) {
|
||||||
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
|
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
|
||||||
hunk = file_diff->hunk + hunk_index;
|
hunk = file_diff->hunk + hunk_index;
|
||||||
if (hunk->use == UNDECIDED_HUNK)
|
if (hunk->use == UNDECIDED_HUNK)
|
||||||
hunk->use = USE_HUNK;
|
hunk->use = USE_HUNK;
|
||||||
}
|
}
|
||||||
|
} else if (hunk->use == UNDECIDED_HUNK) {
|
||||||
|
hunk->use = USE_HUNK;
|
||||||
|
}
|
||||||
} else if (ch == 'd' || ch == 'q') {
|
} else if (ch == 'd' || ch == 'q') {
|
||||||
|
if (file_diff->hunk_nr) {
|
||||||
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
|
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
|
||||||
hunk = file_diff->hunk + hunk_index;
|
hunk = file_diff->hunk + hunk_index;
|
||||||
if (hunk->use == UNDECIDED_HUNK)
|
if (hunk->use == UNDECIDED_HUNK)
|
||||||
hunk->use = SKIP_HUNK;
|
hunk->use = SKIP_HUNK;
|
||||||
}
|
}
|
||||||
|
} else if (hunk->use == UNDECIDED_HUNK) {
|
||||||
|
hunk->use = SKIP_HUNK;
|
||||||
|
}
|
||||||
if (ch == 'q') {
|
if (ch == 'q') {
|
||||||
quit = 1;
|
quit = 1;
|
||||||
break;
|
break;
|
||||||
@ -1639,7 +1656,8 @@ soft_increment:
|
|||||||
if (file_diff->hunk[i].use == USE_HUNK)
|
if (file_diff->hunk[i].use == USE_HUNK)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (i < file_diff->hunk_nr) {
|
if (i < file_diff->hunk_nr ||
|
||||||
|
(!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
|
||||||
/* At least one hunk selected: apply */
|
/* At least one hunk selected: apply */
|
||||||
strbuf_reset(&s->buf);
|
strbuf_reset(&s->buf);
|
||||||
reassemble_patch(s, file_diff, 0, &s->buf);
|
reassemble_patch(s, file_diff, 0, &s->buf);
|
||||||
|
@ -754,13 +754,16 @@ 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' };
|
my $addition;
|
||||||
|
|
||||||
for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
|
for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
|
||||||
|
if ($src->{TEXT}->[$i] =~ /^new file/) {
|
||||||
|
$addition = 1;
|
||||||
|
$head->{TYPE} = 'addition';
|
||||||
|
}
|
||||||
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];
|
||||||
@ -1501,12 +1504,6 @@ 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;
|
||||||
@ -1516,6 +1513,7 @@ sub patch_update_file {
|
|||||||
my ($prev, $next, $other, $undecided, $i);
|
my ($prev, $next, $other, $undecided, $i);
|
||||||
$other = '';
|
$other = '';
|
||||||
|
|
||||||
|
last if ($ix and !$num);
|
||||||
if ($num <= $ix) {
|
if ($num <= $ix) {
|
||||||
$ix = 0;
|
$ix = 0;
|
||||||
}
|
}
|
||||||
@ -1548,8 +1546,9 @@ sub patch_update_file {
|
|||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
last if (!$undecided);
|
last if (!$undecided && ($num || !$addition));
|
||||||
|
|
||||||
|
if ($num) {
|
||||||
if ($hunk[$ix]{TYPE} eq 'hunk' &&
|
if ($hunk[$ix]{TYPE} eq 'hunk' &&
|
||||||
hunk_splittable($hunk[$ix]{TEXT})) {
|
hunk_splittable($hunk[$ix]{TEXT})) {
|
||||||
$other .= ',s';
|
$other .= ',s';
|
||||||
@ -1560,25 +1559,40 @@ sub patch_update_file {
|
|||||||
for (@{$hunk[$ix]{DISPLAY}}) {
|
for (@{$hunk[$ix]{DISPLAY}}) {
|
||||||
print;
|
print;
|
||||||
}
|
}
|
||||||
print colored $prompt_color, "(", ($ix+1), "/$num) ",
|
}
|
||||||
sprintf(__($patch_update_prompt_modes{$patch_mode}{$hunk[$ix]{TYPE}}), $other);
|
my $type = $num ? $hunk[$ix]{TYPE} : $head->{TYPE};
|
||||||
|
print colored $prompt_color, "(", ($ix+1), "/", ($num ? $num : 1), ") ",
|
||||||
|
sprintf(__($patch_update_prompt_modes{$patch_mode}{$type}), $other);
|
||||||
|
|
||||||
my $line = prompt_single_character;
|
my $line = prompt_single_character;
|
||||||
last unless defined $line;
|
last unless defined $line;
|
||||||
if ($line) {
|
if ($line) {
|
||||||
if ($line =~ /^y/i) {
|
if ($line =~ /^y/i) {
|
||||||
|
if ($num) {
|
||||||
$hunk[$ix]{USE} = 1;
|
$hunk[$ix]{USE} = 1;
|
||||||
|
} else {
|
||||||
|
$head->{USE} = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elsif ($line =~ /^n/i) {
|
elsif ($line =~ /^n/i) {
|
||||||
|
if ($num) {
|
||||||
$hunk[$ix]{USE} = 0;
|
$hunk[$ix]{USE} = 0;
|
||||||
|
} else {
|
||||||
|
$head->{USE} = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elsif ($line =~ /^a/i) {
|
elsif ($line =~ /^a/i) {
|
||||||
|
if ($num) {
|
||||||
while ($ix < $num) {
|
while ($ix < $num) {
|
||||||
if (!defined $hunk[$ix]{USE}) {
|
if (!defined $hunk[$ix]{USE}) {
|
||||||
$hunk[$ix]{USE} = 1;
|
$hunk[$ix]{USE} = 1;
|
||||||
}
|
}
|
||||||
$ix++;
|
$ix++;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$head->{USE} = 1;
|
||||||
|
$ix++;
|
||||||
|
}
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
elsif ($line =~ /^g(.*)/) {
|
elsif ($line =~ /^g(.*)/) {
|
||||||
@ -1613,20 +1627,29 @@ sub patch_update_file {
|
|||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
elsif ($line =~ /^d/i) {
|
elsif ($line =~ /^d/i) {
|
||||||
|
if ($num) {
|
||||||
while ($ix < $num) {
|
while ($ix < $num) {
|
||||||
if (!defined $hunk[$ix]{USE}) {
|
if (!defined $hunk[$ix]{USE}) {
|
||||||
$hunk[$ix]{USE} = 0;
|
$hunk[$ix]{USE} = 0;
|
||||||
}
|
}
|
||||||
$ix++;
|
$ix++;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$head->{USE} = 0;
|
||||||
|
$ix++;
|
||||||
|
}
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
elsif ($line =~ /^q/i) {
|
elsif ($line =~ /^q/i) {
|
||||||
|
if ($num) {
|
||||||
for ($i = 0; $i < $num; $i++) {
|
for ($i = 0; $i < $num; $i++) {
|
||||||
if (!defined $hunk[$i]{USE}) {
|
if (!defined $hunk[$i]{USE}) {
|
||||||
$hunk[$i]{USE} = 0;
|
$hunk[$i]{USE} = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} elsif (!defined $head->{USE}) {
|
||||||
|
$head->{USE} = 0;
|
||||||
|
}
|
||||||
$quit = 1;
|
$quit = 1;
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
@ -1743,7 +1766,7 @@ sub patch_update_file {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@hunk = coalesce_overlapping_hunks(@hunk);
|
@hunk = coalesce_overlapping_hunks(@hunk) if ($num);
|
||||||
|
|
||||||
my $n_lofs = 0;
|
my $n_lofs = 0;
|
||||||
my @result = ();
|
my @result = ();
|
||||||
@ -1753,7 +1776,7 @@ sub patch_update_file {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (@result) {
|
if (@result or $head->{USE}) {
|
||||||
my @patch = reassemble_patch($head->{TEXT}, @result);
|
my @patch = reassemble_patch($head->{TEXT}, @result);
|
||||||
my $apply_routine = $patch_mode_flavour{APPLY};
|
my $apply_routine = $patch_mode_flavour{APPLY};
|
||||||
&$apply_routine(@patch);
|
&$apply_routine(@patch);
|
||||||
|
@ -822,6 +822,44 @@ test_expect_success 'checkout -p works with pathological context lines' '
|
|||||||
test_cmp expect a
|
test_cmp expect a
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# This should be called from a subshell as it sets a temporary editor
|
||||||
|
setup_new_file() {
|
||||||
|
write_script new-file-editor.sh <<-\EOF &&
|
||||||
|
sed /^#/d "$1" >patch &&
|
||||||
|
sed /^+c/d patch >"$1"
|
||||||
|
EOF
|
||||||
|
test_set_editor "$(pwd)/new-file-editor.sh" &&
|
||||||
|
test_write_lines a b c d e f >new-file &&
|
||||||
|
test_write_lines a b d e f >new-file-expect &&
|
||||||
|
test_write_lines "@@ -0,0 +1,6 @@" +a +b +c +d +e +f >patch-expect
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'add -N followed by add -p patch editing' '
|
||||||
|
git reset --hard &&
|
||||||
|
(
|
||||||
|
setup_new_file &&
|
||||||
|
git add -N new-file &&
|
||||||
|
test_write_lines e n q | git add -p &&
|
||||||
|
git cat-file blob :new-file >actual &&
|
||||||
|
test_cmp new-file-expect actual &&
|
||||||
|
test_cmp patch-expect patch
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'checkout -p patch editing of added file' '
|
||||||
|
git reset --hard &&
|
||||||
|
(
|
||||||
|
setup_new_file &&
|
||||||
|
git add new-file &&
|
||||||
|
git commit -m "add new file" &&
|
||||||
|
git rm new-file &&
|
||||||
|
git commit -m "remove new file" &&
|
||||||
|
test_write_lines e n q | git checkout -p HEAD^ &&
|
||||||
|
test_cmp new-file-expect new-file &&
|
||||||
|
test_cmp patch-expect patch
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'show help from add--helper' '
|
test_expect_success 'show help from add--helper' '
|
||||||
git reset --hard &&
|
git reset --hard &&
|
||||||
cat >expect <<-EOF &&
|
cat >expect <<-EOF &&
|
||||||
|
Loading…
x
Reference in New Issue
Block a user