Skip to content

Commit df71e7f

Browse files
committed
built-in add -p: implement the 'g' ("goto") command
With this patch, it is now possible to see a summary of the available hunks and to navigate between them (by number). A test is added to verify that this behavior matches the one of the Perl version of `git add -p`. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 086dee6 commit df71e7f

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

add-patch.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,54 @@ static int edit_hunk_loop(struct add_p_state *s,
903903
}
904904
}
905905

906+
#define SUMMARY_HEADER_WIDTH 20
907+
#define SUMMARY_LINE_WIDTH 80
908+
static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
909+
struct strbuf *out)
910+
{
911+
struct hunk_header *header = &hunk->header;
912+
struct strbuf *plain = &s->plain;
913+
size_t len = out->len, i;
914+
915+
strbuf_addf(out, " -%lu,%lu +%lu,%lu ",
916+
header->old_offset, header->old_count,
917+
header->new_offset, header->new_count);
918+
if (out->len - len < SUMMARY_HEADER_WIDTH)
919+
strbuf_addchars(out, ' ',
920+
SUMMARY_HEADER_WIDTH + len - out->len);
921+
for (i = hunk->start; i < hunk->end; i = find_next_line(plain, i))
922+
if (plain->buf[i] != ' ')
923+
break;
924+
if (i < hunk->end)
925+
strbuf_add(out, plain->buf + i, find_next_line(plain, i) - i);
926+
if (out->len - len > SUMMARY_LINE_WIDTH)
927+
strbuf_setlen(out, len + SUMMARY_LINE_WIDTH);
928+
strbuf_complete_line(out);
929+
}
930+
931+
#define DISPLAY_HUNKS_LINES 20
932+
static size_t display_hunks(struct add_p_state *s,
933+
struct file_diff *file_diff, size_t start_index)
934+
{
935+
size_t end_index = start_index + DISPLAY_HUNKS_LINES;
936+
937+
if (end_index > file_diff->hunk_nr)
938+
end_index = file_diff->hunk_nr;
939+
940+
while (start_index < end_index) {
941+
struct hunk *hunk = file_diff->hunk + start_index++;
942+
943+
strbuf_reset(&s->buf);
944+
strbuf_addf(&s->buf, "%c%2d: ", hunk->use == USE_HUNK ? '+'
945+
: hunk->use == SKIP_HUNK ? '-' : ' ',
946+
(int)start_index);
947+
summarize_hunk(s, hunk, &s->buf);
948+
fputs(s->buf.buf, stdout);
949+
}
950+
951+
return end_index;
952+
}
953+
906954
static const char help_patch_text[] =
907955
N_("y - stage this hunk\n"
908956
"n - do not stage this hunk\n"
@@ -912,6 +960,7 @@ N_("y - stage this hunk\n"
912960
"J - leave this hunk undecided, see next hunk\n"
913961
"k - leave this hunk undecided, see previous undecided hunk\n"
914962
"K - leave this hunk undecided, see previous hunk\n"
963+
"g - select a hunk to go to\n"
915964
"s - split the current hunk into smaller hunks\n"
916965
"e - manually edit the current hunk\n"
917966
"? - print help\n");
@@ -970,6 +1019,8 @@ static int patch_update_file(struct add_p_state *s,
9701019
strbuf_addstr(&s->buf, ",j");
9711020
if (hunk_index + 1 < file_diff->hunk_nr)
9721021
strbuf_addstr(&s->buf, ",J");
1022+
if (file_diff->hunk_nr > 1)
1023+
strbuf_addstr(&s->buf, ",g");
9731024
if (hunk->splittable_into > 1)
9741025
strbuf_addstr(&s->buf, ",s");
9751026
if (hunk_index + 1 > file_diff->mode_change &&
@@ -1035,6 +1086,41 @@ static int patch_update_file(struct add_p_state *s,
10351086
hunk_index = undecided_next;
10361087
else
10371088
err(s, _("No next hunk"));
1089+
} else if (s->answer.buf[0] == 'g') {
1090+
char *pend;
1091+
unsigned long response;
1092+
1093+
if (file_diff->hunk_nr < 2) {
1094+
err(s, _("No other hunks to goto"));
1095+
continue;
1096+
}
1097+
strbuf_remove(&s->answer, 0, 1);
1098+
strbuf_trim(&s->answer);
1099+
i = hunk_index > 10 ? hunk_index - 10 : 0;
1100+
while (s->answer.len == 0) {
1101+
i = display_hunks(s, file_diff, i);
1102+
printf("%s", i < file_diff->hunk_nr ?
1103+
_("go to which hunk (<ret> to see "
1104+
"more)? ") : _("go to which hunk? "));
1105+
fflush(stdout);
1106+
if (strbuf_getline(&s->answer,
1107+
stdin) == EOF)
1108+
break;
1109+
strbuf_trim_trailing_newline(&s->answer);
1110+
}
1111+
1112+
strbuf_trim(&s->answer);
1113+
response = strtoul(s->answer.buf, &pend, 10);
1114+
if (*pend || pend == s->answer.buf)
1115+
err(s, _("Invalid number: '%s'"),
1116+
s->answer.buf);
1117+
else if (0 < response && response <= file_diff->hunk_nr)
1118+
hunk_index = response - 1;
1119+
else
1120+
err(s, Q_("Sorry, only %d hunk available.",
1121+
"Sorry, only %d hunks available.",
1122+
file_diff->hunk_nr),
1123+
(int)file_diff->hunk_nr);
10381124
} else if (s->answer.buf[0] == 's') {
10391125
size_t splittable_into = hunk->splittable_into;
10401126
if (splittable_into < 2)

t/t3701-add-interactive.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,22 @@ test_expect_success 'split hunk setup' '
413413
test_write_lines 10 15 20 21 22 23 24 30 40 50 60 >test
414414
'
415415

416+
test_expect_success 'goto hunk' '
417+
test_when_finished "git reset" &&
418+
tr _ " " >expect <<-EOF &&
419+
Stage this hunk [y,n,q,a,d,K,g,/,e,?]? + 1: -1,2 +1,3 +15
420+
_ 2: -2,4 +3,8 +21
421+
go to which hunk? @@ -1,2 +1,3 @@
422+
_10
423+
+15
424+
_20
425+
Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
426+
EOF
427+
test_write_lines s y g 1 | git add -p >actual &&
428+
tail -n 7 <actual >actual.trimmed &&
429+
test_cmp expect actual.trimmed
430+
'
431+
416432
test_expect_success 'split hunk "add -p (edit)"' '
417433
# Split, say Edit and do nothing. Then:
418434
#

0 commit comments

Comments
 (0)