]> git.ipfire.org Git - thirdparty/git.git/commitdiff
built-in add -p: implement the '/' ("search regex") command
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Fri, 13 Dec 2019 08:08:03 +0000 (08:08 +0000)
committerJunio C Hamano <gitster@pobox.com>
Fri, 13 Dec 2019 20:37:14 +0000 (12:37 -0800)
This patch implements the hunk searching feature in the C version of
`git add -p`.

A test is added to verify that this behavior matches the one of the Perl
version of `git add -p`.

Note that this involves a change of behavior: the Perl version uses (of
course) the Perl flavor of regular expressions, while this patch uses
the regcomp()/regexec(), i.e. POSIX extended regular expressions. In
practice, this behavior change is unlikely to matter.

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 fdbb1e3e2276f827ba187b7e86f7e2f394ec0c7d..fd72850c659b79adbabd7aa8e9105279aaa88e93 100644 (file)
@@ -1013,6 +1013,7 @@ N_("y - stage this 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"
+   "/ - search for a hunk matching the given regex\n"
    "s - split the current hunk into smaller hunks\n"
    "e - manually edit the current hunk\n"
    "? - print help\n");
@@ -1072,7 +1073,7 @@ static int patch_update_file(struct add_p_state *s,
                if (hunk_index + 1 < file_diff->hunk_nr)
                        strbuf_addstr(&s->buf, ",J");
                if (file_diff->hunk_nr > 1)
-                       strbuf_addstr(&s->buf, ",g");
+                       strbuf_addstr(&s->buf, ",g,/");
                if (hunk->splittable_into > 1)
                        strbuf_addstr(&s->buf, ",s");
                if (hunk_index + 1 > file_diff->mode_change &&
@@ -1177,6 +1178,53 @@ soft_increment:
                                          "Sorry, only %d hunks available.",
                                          file_diff->hunk_nr),
                                    (int)file_diff->hunk_nr);
+               } else if (s->answer.buf[0] == '/') {
+                       regex_t regex;
+                       int ret;
+
+                       if (file_diff->hunk_nr < 2) {
+                               err(s, _("No other hunks to search"));
+                               continue;
+                       }
+                       strbuf_remove(&s->answer, 0, 1);
+                       strbuf_trim_trailing_newline(&s->answer);
+                       if (s->answer.len == 0) {
+                               printf("%s", _("search for regex? "));
+                               fflush(stdout);
+                               if (strbuf_getline(&s->answer,
+                                                  stdin) == EOF)
+                                       break;
+                               strbuf_trim_trailing_newline(&s->answer);
+                               if (s->answer.len == 0)
+                                       continue;
+                       }
+                       ret = regcomp(&regex, s->answer.buf,
+                                     REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
+                       if (ret) {
+                               char errbuf[1024];
+
+                               regerror(ret, &regex, errbuf, sizeof(errbuf));
+                               err(s, _("Malformed search regexp %s: %s"),
+                                   s->answer.buf, errbuf);
+                               continue;
+                       }
+                       i = hunk_index;
+                       for (;;) {
+                               /* render the hunk into a scratch buffer */
+                               render_hunk(s, file_diff->hunk + i, 0, 0,
+                                           &s->buf);
+                               if (regexec(&regex, s->buf.buf, 0, NULL, 0)
+                                   != REG_NOMATCH)
+                                       break;
+                               i++;
+                               if (i == file_diff->hunk_nr)
+                                       i = 0;
+                               if (i != hunk_index)
+                                       continue;
+                               err(s, _("No hunk matches the given pattern"));
+                               break;
+                       }
+                       hunk_index = i;
                } else if (s->answer.buf[0] == 's') {
                        size_t splittable_into = hunk->splittable_into;
                        if (splittable_into < 2)
index 57c656a20c3c246f725764db9452e788511aa462..12ee321707a33bd8455fb30ea24c57d002c13b68 100755 (executable)
@@ -429,6 +429,20 @@ test_expect_success 'goto hunk' '
        test_cmp expect actual.trimmed
 '
 
+test_expect_success 'navigate to hunk via regex' '
+       test_when_finished "git reset" &&
+       tr _ " " >expect <<-EOF &&
+       (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? @@ -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 /1,2 | git add -p >actual &&
+       tail -n 5 <actual >actual.trimmed &&
+       test_cmp expect actual.trimmed
+'
+
 test_expect_success 'split hunk "add -p (edit)"' '
        # Split, say Edit and do nothing.  Then:
        #