mirror of
https://github.com/git/git.git
synced 2025-12-12 20:36:24 +01:00
Merge branch 'rs/add-patch-options-fix'
The code in "git add -p" and friends to iterate over hunks was riddled with bugs, which has been corrected. * rs/add-patch-options-fix: add-patch: reset "permitted" at loop start add-patch: let options a and d roll over like y and n add-patch: let options k and K roll over like j and J add-patch: let options y, n, j, and e roll over to next undecided add-patch: document that option J rolls over add-patch: improve help for options j, J, k, and K
This commit is contained in:
@@ -342,10 +342,10 @@ patch::
|
||||
d - do not stage this hunk or any of the later hunks in the file
|
||||
g - select a hunk to go to
|
||||
/ - search for a hunk matching the given regex
|
||||
j - leave this hunk undecided, see next undecided hunk
|
||||
J - leave this hunk undecided, see next hunk
|
||||
k - leave this hunk undecided, see previous undecided hunk
|
||||
K - leave this hunk undecided, see previous hunk
|
||||
j - go to the next undecided hunk, roll over at the bottom
|
||||
J - go to the next hunk, roll over at the bottom
|
||||
k - go to the previous undecided hunk, roll over at the top
|
||||
K - go to the previous hunk, roll over at the top
|
||||
s - split the current hunk into smaller hunks
|
||||
e - manually edit the current hunk
|
||||
p - print the current hunk
|
||||
|
||||
75
add-patch.c
75
add-patch.c
@@ -1408,10 +1408,10 @@ static size_t display_hunks(struct add_p_state *s,
|
||||
}
|
||||
|
||||
static const char help_patch_remainder[] =
|
||||
N_("j - leave this hunk undecided, see next undecided hunk\n"
|
||||
"J - leave this hunk undecided, see next hunk\n"
|
||||
"k - leave this hunk undecided, see previous undecided hunk\n"
|
||||
"K - leave this hunk undecided, see previous hunk\n"
|
||||
N_("j - go to the next undecided hunk, roll over at the bottom\n"
|
||||
"J - go to the next hunk, roll over at the bottom\n"
|
||||
"k - go to the previous undecided hunk, roll over at the top\n"
|
||||
"K - go to the previous hunk, roll over at the top\n"
|
||||
"g - select a hunk to go to\n"
|
||||
"/ - search for a hunk matching the given regex\n"
|
||||
"s - split the current hunk into smaller hunks\n"
|
||||
@@ -1419,6 +1419,27 @@ N_("j - leave this hunk undecided, see next undecided hunk\n"
|
||||
"p - print the current hunk, 'P' to use the pager\n"
|
||||
"? - print help\n");
|
||||
|
||||
static size_t dec_mod(size_t a, size_t m)
|
||||
{
|
||||
return a > 0 ? a - 1 : m - 1;
|
||||
}
|
||||
|
||||
static size_t inc_mod(size_t a, size_t m)
|
||||
{
|
||||
return a < m - 1 ? a + 1 : 0;
|
||||
}
|
||||
|
||||
static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)
|
||||
{
|
||||
for (size_t i = 0; i < file_diff->hunk_nr; i++) {
|
||||
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
|
||||
*idx = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int patch_update_file(struct add_p_state *s,
|
||||
struct file_diff *file_diff)
|
||||
{
|
||||
@@ -1429,15 +1450,6 @@ static int patch_update_file(struct add_p_state *s,
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
int colored = !!s->colored.len, quit = 0, use_pager = 0;
|
||||
enum prompt_mode_type prompt_mode_type;
|
||||
enum {
|
||||
ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
|
||||
ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
|
||||
ALLOW_GOTO_NEXT_HUNK = 1 << 2,
|
||||
ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
|
||||
ALLOW_SEARCH_AND_GOTO = 1 << 4,
|
||||
ALLOW_SPLIT = 1 << 5,
|
||||
ALLOW_EDIT = 1 << 6
|
||||
} permitted = 0;
|
||||
|
||||
/* Empty added files have no hunks */
|
||||
if (!file_diff->hunk_nr && !file_diff->added)
|
||||
@@ -1447,6 +1459,16 @@ static int patch_update_file(struct add_p_state *s,
|
||||
render_diff_header(s, file_diff, colored, &s->buf);
|
||||
fputs(s->buf.buf, stdout);
|
||||
for (;;) {
|
||||
enum {
|
||||
ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
|
||||
ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
|
||||
ALLOW_GOTO_NEXT_HUNK = 1 << 2,
|
||||
ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
|
||||
ALLOW_SEARCH_AND_GOTO = 1 << 4,
|
||||
ALLOW_SPLIT = 1 << 5,
|
||||
ALLOW_EDIT = 1 << 6
|
||||
} permitted = 0;
|
||||
|
||||
if (hunk_index >= file_diff->hunk_nr)
|
||||
hunk_index = 0;
|
||||
hunk = file_diff->hunk_nr
|
||||
@@ -1456,13 +1478,17 @@ static int patch_update_file(struct add_p_state *s,
|
||||
undecided_next = -1;
|
||||
|
||||
if (file_diff->hunk_nr) {
|
||||
for (i = hunk_index - 1; i >= 0; i--)
|
||||
for (i = dec_mod(hunk_index, file_diff->hunk_nr);
|
||||
i != hunk_index;
|
||||
i = dec_mod(i, file_diff->hunk_nr))
|
||||
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
|
||||
undecided_previous = i;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
|
||||
for (i = inc_mod(hunk_index, file_diff->hunk_nr);
|
||||
i != hunk_index;
|
||||
i = inc_mod(i, file_diff->hunk_nr))
|
||||
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
|
||||
undecided_next = i;
|
||||
break;
|
||||
@@ -1496,7 +1522,7 @@ static int patch_update_file(struct add_p_state *s,
|
||||
permitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;
|
||||
strbuf_addstr(&s->buf, ",k");
|
||||
}
|
||||
if (hunk_index) {
|
||||
if (file_diff->hunk_nr > 1) {
|
||||
permitted |= ALLOW_GOTO_PREVIOUS_HUNK;
|
||||
strbuf_addstr(&s->buf, ",K");
|
||||
}
|
||||
@@ -1504,7 +1530,7 @@ static int patch_update_file(struct add_p_state *s,
|
||||
permitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;
|
||||
strbuf_addstr(&s->buf, ",j");
|
||||
}
|
||||
if (hunk_index + 1 < file_diff->hunk_nr) {
|
||||
if (file_diff->hunk_nr > 1) {
|
||||
permitted |= ALLOW_GOTO_NEXT_HUNK;
|
||||
strbuf_addstr(&s->buf, ",J");
|
||||
}
|
||||
@@ -1569,6 +1595,8 @@ soft_increment:
|
||||
if (hunk->use == UNDECIDED_HUNK)
|
||||
hunk->use = USE_HUNK;
|
||||
}
|
||||
if (!get_first_undecided(file_diff, &hunk_index))
|
||||
hunk_index = 0;
|
||||
} else if (hunk->use == UNDECIDED_HUNK) {
|
||||
hunk->use = USE_HUNK;
|
||||
}
|
||||
@@ -1579,6 +1607,8 @@ soft_increment:
|
||||
if (hunk->use == UNDECIDED_HUNK)
|
||||
hunk->use = SKIP_HUNK;
|
||||
}
|
||||
if (!get_first_undecided(file_diff, &hunk_index))
|
||||
hunk_index = 0;
|
||||
} else if (hunk->use == UNDECIDED_HUNK) {
|
||||
hunk->use = SKIP_HUNK;
|
||||
}
|
||||
@@ -1588,24 +1618,25 @@ soft_increment:
|
||||
}
|
||||
} else if (s->answer.buf[0] == 'K') {
|
||||
if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
|
||||
hunk_index--;
|
||||
hunk_index = dec_mod(hunk_index,
|
||||
file_diff->hunk_nr);
|
||||
else
|
||||
err(s, _("No previous hunk"));
|
||||
err(s, _("No other hunk"));
|
||||
} else if (s->answer.buf[0] == 'J') {
|
||||
if (permitted & ALLOW_GOTO_NEXT_HUNK)
|
||||
hunk_index++;
|
||||
else
|
||||
err(s, _("No next hunk"));
|
||||
err(s, _("No other hunk"));
|
||||
} else if (s->answer.buf[0] == 'k') {
|
||||
if (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)
|
||||
hunk_index = undecided_previous;
|
||||
else
|
||||
err(s, _("No previous hunk"));
|
||||
err(s, _("No other undecided hunk"));
|
||||
} else if (s->answer.buf[0] == 'j') {
|
||||
if (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)
|
||||
hunk_index = undecided_next;
|
||||
else
|
||||
err(s, _("No next hunk"));
|
||||
err(s, _("No other undecided hunk"));
|
||||
} else if (s->answer.buf[0] == 'g') {
|
||||
char *pend;
|
||||
unsigned long response;
|
||||
|
||||
@@ -333,8 +333,8 @@ test_expect_success 'different prompts for mode change/deleted' '
|
||||
sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered &&
|
||||
cat >expect <<-\EOF &&
|
||||
(1/1) Stage deletion [y,n,q,a,d,p,?]?
|
||||
(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,?]?
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]?
|
||||
(1/2) Stage mode change [y,n,q,a,d,k,K,j,J,g,/,p,?]?
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]?
|
||||
EOF
|
||||
test_cmp expect actual.filtered
|
||||
'
|
||||
@@ -521,13 +521,13 @@ test_expect_success 'split hunk setup' '
|
||||
test_expect_success 'goto hunk 1 with "g 1"' '
|
||||
test_when_finished "git reset" &&
|
||||
tr _ " " >expect <<-EOF &&
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1: -1,2 +1,3 +15
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]? + 1: -1,2 +1,3 +15
|
||||
_ 2: -2,4 +3,8 +21
|
||||
go to which hunk? @@ -1,2 +1,3 @@
|
||||
_10
|
||||
+15
|
||||
_20
|
||||
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
|
||||
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
|
||||
EOF
|
||||
test_write_lines s y g 1 | git add -p >actual &&
|
||||
tail -n 7 <actual >actual.trimmed &&
|
||||
@@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
|
||||
_10
|
||||
+15
|
||||
_20
|
||||
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
|
||||
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
|
||||
EOF
|
||||
test_write_lines s y g1 | git add -p >actual &&
|
||||
tail -n 4 <actual >actual.trimmed &&
|
||||
@@ -550,11 +550,11 @@ test_expect_success 'goto hunk 1 with "g1"' '
|
||||
test_expect_success 'navigate to hunk via regex /pattern' '
|
||||
test_when_finished "git reset" &&
|
||||
tr _ " " >expect <<-EOF &&
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
|
||||
_10
|
||||
+15
|
||||
_20
|
||||
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
|
||||
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
|
||||
EOF
|
||||
test_write_lines s y /1,2 | git add -p >actual &&
|
||||
tail -n 5 <actual >actual.trimmed &&
|
||||
@@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
|
||||
_10
|
||||
+15
|
||||
_20
|
||||
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
|
||||
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
|
||||
EOF
|
||||
test_write_lines s y / 1,2 | git add -p >actual &&
|
||||
tail -n 4 <actual >actual.trimmed &&
|
||||
@@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
|
||||
tr _ " " >expect <<-EOF &&
|
||||
+15
|
||||
20
|
||||
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
|
||||
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
|
||||
10
|
||||
+15
|
||||
20
|
||||
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
|
||||
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
|
||||
EOF
|
||||
test_write_lines s y g 1 p | git add -p >actual &&
|
||||
tail -n 7 <actual >actual.trimmed &&
|
||||
@@ -595,11 +595,11 @@ test_expect_success TTY 'print again the hunk (PAGER)' '
|
||||
cat >expect <<-EOF &&
|
||||
<GREEN>+<RESET><GREEN>15<RESET>
|
||||
20<RESET>
|
||||
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
|
||||
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
|
||||
PAGER 10<RESET>
|
||||
PAGER <GREEN>+<RESET><GREEN>15<RESET>
|
||||
PAGER 20<RESET>
|
||||
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
|
||||
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? <RESET>
|
||||
EOF
|
||||
test_write_lines s y g 1 P |
|
||||
(
|
||||
@@ -802,15 +802,15 @@ test_expect_success 'colors can be overridden' '
|
||||
<BOLD>-old<RESET>
|
||||
<BLUE>+<RESET><BLUE>new<RESET>
|
||||
<CYAN> more-context<RESET>
|
||||
<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
|
||||
<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
|
||||
<CYAN> more-context<RESET>
|
||||
<BLUE>+<RESET><BLUE>another-one<RESET>
|
||||
<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
|
||||
<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
|
||||
<CYAN> context<RESET>
|
||||
<BOLD>-old<RESET>
|
||||
<BLUE>+new<RESET>
|
||||
<CYAN> more-context<RESET>
|
||||
<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
|
||||
<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? <RESET>
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
@@ -1385,4 +1385,50 @@ test_expect_success 'splitting edited hunk' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'options J, K roll over' '
|
||||
test_write_lines a b c d e f g h i >file &&
|
||||
git add file &&
|
||||
test_write_lines X b c d e f g h X >file &&
|
||||
test_write_lines J J K q | git add -p >out &&
|
||||
test_write_lines 1 2 1 2 >expect &&
|
||||
sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'options y, n, a, d, j, k, e roll over to next undecided (1)' '
|
||||
test_write_lines a b c d e f g h i j k l m n o p q >file &&
|
||||
git add file &&
|
||||
test_write_lines X b c d e f g h X j k l m n o p X >file &&
|
||||
test_set_editor : &&
|
||||
test_write_lines g3 y g3 n g3 a g3 d g3 j g3 e k q | git add -p >out &&
|
||||
test_write_lines 1 3 1 3 1 3 1 3 1 3 1 3 1 2 >expect &&
|
||||
sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'options y, n, a, d, j, k, e roll over to next undecided (2)' '
|
||||
test_write_lines a b c d e f g h i j k l m n o p q >file &&
|
||||
git add file &&
|
||||
test_write_lines X b c d e f g h X j k l m n o p X >file &&
|
||||
test_set_editor : &&
|
||||
test_write_lines y g3 y g3 n g3 a g3 d g3 j g3 e g1 k q | git add -p >out &&
|
||||
test_write_lines 1 2 3 2 3 2 3 2 3 2 3 2 3 2 1 2 >expect &&
|
||||
sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'invalid option s is rejected' '
|
||||
test_write_lines a b c d e f g h i j k >file &&
|
||||
git add file &&
|
||||
test_write_lines X b X d e f g h i j X >file &&
|
||||
test_write_lines j s q | git add -p >out &&
|
||||
sed -ne "s/ @@.*//" -e "s/ \$//" -e "/^(/p" <out >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,s,e,p,?]?
|
||||
(2/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? Sorry, cannot split this hunk
|
||||
(2/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
Reference in New Issue
Block a user