add-patch: document that option J rolls over

The variable "permitted" is not reset after moving to a different hunk, so it only accumulates permission and doesn't necessarily reflect those of the current hunk. This may be a bug, but is actually useful with the option J, which can be used at the last hunk to roll over to the first hunk. Make this particular behavior official. Also adjust the error message, as it will only be shown if there's just a single hunk. Suggested-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Oct 6, 2025 at 19:20 UTC c309b65a7c8a0dc8a1566ac3587d37d935632e4d
3 files changed +18 -8
Documentation/git-add.adoc
+1 -1
@@ -343,7 +343,7 @@ patch::
343 g - select a hunk to go to
344 / - search for a hunk matching the given regex
345 j - go to the next undecided hunk
346 - J - go to the next hunk
346 + J - go to the next hunk, roll over at the bottom
347 k - go to the previous undecided hunk
348 K - go to the previous hunk
349 s - split the current hunk into smaller hunks
add-patch.c
+3 -3
@@ -1398,7 +1398,7 @@ static size_t display_hunks(struct add_p_state *s,
1398
1399 static const char help_patch_remainder[] =
1400 N_("j - go to the next undecided hunk\n"
1401 - "J - go to the next hunk\n"
1401 + "J - go to the next hunk, roll over at the bottom\n"
1402 "k - go to the previous undecided hunk\n"
1403 "K - go to the previous hunk\n"
1404 "g - select a hunk to go to\n"
@@ -1493,7 +1493,7 @@ static int patch_update_file(struct add_p_state *s,
1493 permitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;
1494 strbuf_addstr(&s->buf, ",j");
1495 }
1496 - if (hunk_index + 1 < file_diff->hunk_nr) {
1496 + if (file_diff->hunk_nr > 1) {
1497 permitted |= ALLOW_GOTO_NEXT_HUNK;
1498 strbuf_addstr(&s->buf, ",J");
1499 }
@@ -1584,7 +1584,7 @@ soft_increment:
1584 if (permitted & ALLOW_GOTO_NEXT_HUNK)
1585 hunk_index++;
1586 else
1587 - err(s, _("No next hunk"));
1587 + err(s, _("No other hunk"));
1588 } else if (s->answer.buf[0] == 'k') {
1589 if (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)
1590 hunk_index = undecided_previous;
t/t3701-add-interactive.sh
+14 -4
@@ -334,7 +334,7 @@ test_expect_success 'different prompts for mode change/deleted' '
334 cat >expect <<-\EOF &&
335 (1/1) Stage deletion [y,n,q,a,d,p,?]?
336 (1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,?]?
337 - (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]?
337 + (2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]?
338 EOF
339 test_cmp expect actual.filtered
340 '
@@ -521,7 +521,7 @@ test_expect_success 'split hunk setup' '
521 test_expect_success 'goto hunk 1 with "g 1"' '
522 test_when_finished "git reset" &&
523 tr _ " " >expect <<-EOF &&
524 - (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1: -1,2 +1,3 +15
524 + (2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]? + 1: -1,2 +1,3 +15
525 _ 2: -2,4 +3,8 +21
526 go to which hunk? @@ -1,2 +1,3 @@
527 _10
@@ -550,7 +550,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
550 test_expect_success 'navigate to hunk via regex /pattern' '
551 test_when_finished "git reset" &&
552 tr _ " " >expect <<-EOF &&
553 - (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@
553 + (2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
554 _10
555 +15
556 _20
@@ -805,7 +805,7 @@ test_expect_success 'colors can be overridden' '
805 <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
806 <CYAN> more-context<RESET>
807 <BLUE>+<RESET><BLUE>another-one<RESET>
808 - <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
808 + <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
809 <CYAN> context<RESET>
810 <BOLD>-old<RESET>
811 <BLUE>+new<RESET>
@@ -1354,4 +1354,14 @@ do
1354 '
1355 done
1356
1357 +test_expect_success 'option J rolls over' '
1358 + test_write_lines a b c d e f g h i >file &&
1359 + git add file &&
1360 + test_write_lines X b c d e f g h X >file &&
1361 + test_write_lines J J q | git add -p >out &&
1362 + test_write_lines 1 2 1 >expect &&
1363 + sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
1364 + test_cmp expect actual
1365 +'
1366 +
1367 test_done