]> git.ipfire.org Git - thirdparty/git.git/commitdiff
built-in add -p: implement the 'g' ("goto") command
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Fri, 13 Dec 2019 08:08:02 +0000 (08:08 +0000)
committerJunio C Hamano <gitster@pobox.com>
Fri, 13 Dec 2019 20:37:14 +0000 (12:37 -0800)
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 <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
add-patch.c
t/t3701-add-interactive.sh

index ea863ca09d6fb4cc25cb74d0ad14bf70871e97a7..fdbb1e3e2276f827ba187b7e86f7e2f394ec0c7d 100644 (file)
@@ -955,6 +955,54 @@ static int edit_hunk_loop(struct add_p_state *s,
        }
 }
 
+#define SUMMARY_HEADER_WIDTH 20
+#define SUMMARY_LINE_WIDTH 80
+static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
+                          struct strbuf *out)
+{
+       struct hunk_header *header = &hunk->header;
+       struct strbuf *plain = &s->plain;
+       size_t len = out->len, i;
+
+       strbuf_addf(out, " -%lu,%lu +%lu,%lu ",
+                   header->old_offset, header->old_count,
+                   header->new_offset, header->new_count);
+       if (out->len - len < SUMMARY_HEADER_WIDTH)
+               strbuf_addchars(out, ' ',
+                               SUMMARY_HEADER_WIDTH + len - out->len);
+       for (i = hunk->start; i < hunk->end; i = find_next_line(plain, i))
+               if (plain->buf[i] != ' ')
+                       break;
+       if (i < hunk->end)
+               strbuf_add(out, plain->buf + i, find_next_line(plain, i) - i);
+       if (out->len - len > SUMMARY_LINE_WIDTH)
+               strbuf_setlen(out, len + SUMMARY_LINE_WIDTH);
+       strbuf_complete_line(out);
+}
+
+#define DISPLAY_HUNKS_LINES 20
+static size_t display_hunks(struct add_p_state *s,
+                           struct file_diff *file_diff, size_t start_index)
+{
+       size_t end_index = start_index + DISPLAY_HUNKS_LINES;
+
+       if (end_index > file_diff->hunk_nr)
+               end_index = file_diff->hunk_nr;
+
+       while (start_index < end_index) {
+               struct hunk *hunk = file_diff->hunk + start_index++;
+
+               strbuf_reset(&s->buf);
+               strbuf_addf(&s->buf, "%c%2d: ", hunk->use == USE_HUNK ? '+'
+                           : hunk->use == SKIP_HUNK ? '-' : ' ',
+                           (int)start_index);
+               summarize_hunk(s, hunk, &s->buf);
+               fputs(s->buf.buf, stdout);
+       }
+
+       return end_index;
+}
+
 static const char help_patch_text[] =
 N_("y - stage this hunk\n"
    "n - do not stage this hunk\n"
@@ -964,6 +1012,7 @@ N_("y - stage this 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"
+   "g - select a hunk to go to\n"
    "s - split the current hunk into smaller hunks\n"
    "e - manually edit the current hunk\n"
    "? - print help\n");
@@ -1022,6 +1071,8 @@ static int patch_update_file(struct add_p_state *s,
                        strbuf_addstr(&s->buf, ",j");
                if (hunk_index + 1 < file_diff->hunk_nr)
                        strbuf_addstr(&s->buf, ",J");
+               if (file_diff->hunk_nr > 1)
+                       strbuf_addstr(&s->buf, ",g");
                if (hunk->splittable_into > 1)
                        strbuf_addstr(&s->buf, ",s");
                if (hunk_index + 1 > file_diff->mode_change &&
@@ -1089,6 +1140,43 @@ soft_increment:
                                hunk_index = undecided_next;
                        else
                                err(s, _("No next hunk"));
+               } else if (s->answer.buf[0] == 'g') {
+                       char *pend;
+                       unsigned long response;
+
+                       if (file_diff->hunk_nr < 2) {
+                               err(s, _("No other hunks to goto"));
+                               continue;
+                       }
+                       strbuf_remove(&s->answer, 0, 1);
+                       strbuf_trim(&s->answer);
+                       i = hunk_index - DISPLAY_HUNKS_LINES / 2;
+                       if (i < file_diff->mode_change)
+                               i = file_diff->mode_change;
+                       while (s->answer.len == 0) {
+                               i = display_hunks(s, file_diff, i);
+                               printf("%s", i < file_diff->hunk_nr ?
+                                      _("go to which hunk (<ret> to see "
+                                        "more)? ") : _("go to which hunk? "));
+                               fflush(stdout);
+                               if (strbuf_getline(&s->answer,
+                                                  stdin) == EOF)
+                                       break;
+                               strbuf_trim_trailing_newline(&s->answer);
+                       }
+
+                       strbuf_trim(&s->answer);
+                       response = strtoul(s->answer.buf, &pend, 10);
+                       if (*pend || pend == s->answer.buf)
+                               err(s, _("Invalid number: '%s'"),
+                                   s->answer.buf);
+                       else if (0 < response && response <= file_diff->hunk_nr)
+                               hunk_index = response - 1;
+                       else
+                               err(s, Q_("Sorry, only %d hunk available.",
+                                         "Sorry, only %d hunks available.",
+                                         file_diff->hunk_nr),
+                                   (int)file_diff->hunk_nr);
                } else if (s->answer.buf[0] == 's') {
                        size_t splittable_into = hunk->splittable_into;
                        if (splittable_into < 2)
index fe383be50e04ffc1ba094cb74fcaecafa7c25e7e..57c656a20c3c246f725764db9452e788511aa462 100755 (executable)
@@ -413,6 +413,22 @@ test_expect_success 'split hunk setup' '
        test_write_lines 10 15 20 21 22 23 24 30 40 50 60 >test
 '
 
+test_expect_success 'goto hunk' '
+       test_when_finished "git reset" &&
+       tr _ " " >expect <<-EOF &&
+       (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? + 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,?]?_
+       EOF
+       test_write_lines s y g 1 | git add -p >actual &&
+       tail -n 7 <actual >actual.trimmed &&
+       test_cmp expect actual.trimmed
+'
+
 test_expect_success 'split hunk "add -p (edit)"' '
        # Split, say Edit and do nothing.  Then:
        #