built-in add -p: implement the '/' ("search regex") command

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>
This commit is contained in:
Johannes Schindelin
2019-03-21 23:50:53 +01:00
parent e2b3a89d53
commit 6534ec4e77
2 changed files with 63 additions and 1 deletions

View File

@@ -967,6 +967,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");
@@ -1026,7 +1027,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 &&
@@ -1127,6 +1128,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)

View File

@@ -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 &&
Stage this hunk [y,n,q,a,d,K,g,/,e,?]? @@ -1,2 +1,3 @@
_10
+15
_20
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:
#