add-patch: let options k and K roll over like j and J
Options j and J roll over at the bottom and go to the first undecided hunk and hunk 1, respectively. Let options k and K do the same when they reach the top of the hunk array, so let them go to the last undecided hunk and the last hunk, respectively, for consistency. Also use the same direction-neutral error messages. 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:22 UTC
1967b60681256ed452ed70dedf381b5380697901
3 files changed
+37
-29
Documentation/git-add.adoc
+2
-2
@@ -344,8 +344,8 @@ patch::
344
/ - search for a hunk matching the given regex
345
j - go to the next undecided hunk, roll over at the bottom
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
347
+ k - go to the previous undecided hunk, roll over at the top
348
+ K - go to the previous hunk, roll over at the top
349
s - split the current hunk into smaller hunks
350
e - manually edit the current hunk
351
p - print the current hunk
add-patch.c
+15
-7
@@ -1399,8 +1399,8 @@ static size_t display_hunks(struct add_p_state *s,
1399
static const char help_patch_remainder[] =
1400
N_("j - go to the next undecided hunk, roll over at the bottom\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"
1402
+ "k - go to the previous undecided hunk, roll over at the top\n"
1403
+ "K - go to the previous hunk, roll over at the top\n"
1404
"g - select a hunk to go to\n"
1405
"/ - search for a hunk matching the given regex\n"
1406
"s - split the current hunk into smaller hunks\n"
@@ -1408,6 +1408,11 @@ N_("j - go to the next undecided hunk, roll over at the bottom\n"
1408
"p - print the current hunk, 'P' to use the pager\n"
1409
"? - print help\n");
1410
1411
+static size_t dec_mod(size_t a, size_t m)
1412
+{
1413
+ return a > 0 ? a - 1 : m - 1;
1414
+}
1415
+
1416
static size_t inc_mod(size_t a, size_t m)
1417
{
1418
return a < m - 1 ? a + 1 : 0;
@@ -1450,7 +1455,9 @@ static int patch_update_file(struct add_p_state *s,
1455
undecided_next = -1;
1456
1457
if (file_diff->hunk_nr) {
1453
- for (i = hunk_index - 1; i >= 0; i--)
1458
+ for (i = dec_mod(hunk_index, file_diff->hunk_nr);
1459
+ i != hunk_index;
1460
+ i = dec_mod(i, file_diff->hunk_nr))
1461
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
1462
undecided_previous = i;
1463
break;
@@ -1492,7 +1499,7 @@ static int patch_update_file(struct add_p_state *s,
1499
permitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;
1500
strbuf_addstr(&s->buf, ",k");
1501
}
1495
- if (hunk_index) {
1502
+ if (file_diff->hunk_nr > 1) {
1503
permitted |= ALLOW_GOTO_PREVIOUS_HUNK;
1504
strbuf_addstr(&s->buf, ",K");
1505
}
@@ -1584,9 +1591,10 @@ soft_increment:
1591
}
1592
} else if (s->answer.buf[0] == 'K') {
1593
if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
1587
- hunk_index--;
1594
+ hunk_index = dec_mod(hunk_index,
1595
+ file_diff->hunk_nr);
1596
else
1589
- err(s, _("No previous hunk"));
1597
+ err(s, _("No other hunk"));
1598
} else if (s->answer.buf[0] == 'J') {
1599
if (permitted & ALLOW_GOTO_NEXT_HUNK)
1600
hunk_index++;
@@ -1596,7 +1604,7 @@ soft_increment:
1604
if (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)
1605
hunk_index = undecided_previous;
1606
else
1599
- err(s, _("No previous hunk"));
1607
+ err(s, _("No other undecided hunk"));
1608
} else if (s->answer.buf[0] == 'j') {
1609
if (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)
1610
hunk_index = undecided_next;
t/t3701-add-interactive.sh
+20
-20
@@ -333,7 +333,7 @@ test_expect_success 'different prompts for mode change/deleted' '
333
sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered &&
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,?]?
336
+ (1/2) Stage mode change [y,n,q,a,d,k,K,j,J,g,/,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
@@ -527,7 +527,7 @@ test_expect_success 'goto hunk 1 with "g 1"' '
527
_10
528
+15
529
_20
530
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
530
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
531
EOF
532
test_write_lines s y g 1 | git add -p >actual &&
533
tail -n 7 <actual >actual.trimmed &&
@@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
540
_10
541
+15
542
_20
543
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
543
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
544
EOF
545
test_write_lines s y g1 | git add -p >actual &&
546
tail -n 4 <actual >actual.trimmed &&
@@ -554,7 +554,7 @@ test_expect_success 'navigate to hunk via regex /pattern' '
554
_10
555
+15
556
_20
557
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
557
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
558
EOF
559
test_write_lines s y /1,2 | git add -p >actual &&
560
tail -n 5 <actual >actual.trimmed &&
@@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
567
_10
568
+15
569
_20
570
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
570
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
571
EOF
572
test_write_lines s y / 1,2 | git add -p >actual &&
573
tail -n 4 <actual >actual.trimmed &&
@@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
579
tr _ " " >expect <<-EOF &&
580
+15
581
20
582
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
582
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
583
10
584
+15
585
20
586
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
586
+ (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]?_
587
EOF
588
test_write_lines s y g 1 p | git add -p >actual &&
589
tail -n 7 <actual >actual.trimmed &&
@@ -595,11 +595,11 @@ test_expect_success TTY 'print again the hunk (PAGER)' '
595
cat >expect <<-EOF &&
596
<GREEN>+<RESET><GREEN>15<RESET>
597
20<RESET>
598
- <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>
598
+ <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>
599
PAGER 10<RESET>
600
PAGER <GREEN>+<RESET><GREEN>15<RESET>
601
PAGER 20<RESET>
602
- <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
602
+ <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? <RESET>
603
EOF
604
test_write_lines s y g 1 P |
605
(
@@ -802,7 +802,7 @@ test_expect_success 'colors can be overridden' '
802
<BOLD>-old<RESET>
803
<BLUE>+<RESET><BLUE>new<RESET>
804
<CYAN> more-context<RESET>
805
- <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
805
+ <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,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,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
@@ -810,7 +810,7 @@ test_expect_success 'colors can be overridden' '
810
<BOLD>-old<RESET>
811
<BLUE>+new<RESET>
812
<CYAN> more-context<RESET>
813
- <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
813
+ <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,?]? <RESET>
814
EOF
815
test_cmp expect actual
816
'
@@ -1354,34 +1354,34 @@ do
1354
'
1355
done
1356
1357
-test_expect_success 'option J rolls over' '
1357
+test_expect_success 'options J, K roll 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 &&
1361
+ test_write_lines J J K q | git add -p >out &&
1362
+ test_write_lines 1 2 1 2 >expect &&
1363
sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
1364
test_cmp expect actual
1365
'
1366
1367
-test_expect_success 'options y, n, j, e roll over to next undecided (1)' '
1367
+test_expect_success 'options y, n, j, k, e roll over to next undecided (1)' '
1368
test_write_lines a b c d e f g h i j k l m n o p q >file &&
1369
git add file &&
1370
test_write_lines X b c d e f g h X j k l m n o p X >file &&
1371
test_set_editor : &&
1372
- test_write_lines g3 y g3 n g3 j g3 e q | git add -p >out &&
1373
- test_write_lines 1 3 1 3 1 3 1 3 1 >expect &&
1372
+ test_write_lines g3 y g3 n g3 j g3 e k q | git add -p >out &&
1373
+ test_write_lines 1 3 1 3 1 3 1 3 1 2 >expect &&
1374
sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
1375
test_cmp expect actual
1376
'
1377
1378
-test_expect_success 'options y, n, j, e roll over to next undecided (2)' '
1378
+test_expect_success 'options y, n, j, k, e roll over to next undecided (2)' '
1379
test_write_lines a b c d e f g h i j k l m n o p q >file &&
1380
git add file &&
1381
test_write_lines X b c d e f g h X j k l m n o p X >file &&
1382
test_set_editor : &&
1383
- test_write_lines y g3 y g3 n g3 j g3 e q | git add -p >out &&
1384
- test_write_lines 1 2 3 2 3 2 3 2 3 2 >expect &&
1383
+ test_write_lines y g3 y g3 n g3 j g3 e g1 k q | git add -p >out &&
1384
+ test_write_lines 1 2 3 2 3 2 3 2 3 2 1 2 >expect &&
1385
sed -n -e "s-/.*--" -e "s/^(//p" <out >actual &&
1386
test_cmp expect actual
1387
'