git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 16/19] built-in add -p: implement the '/' ("search regex") command

From
Johannes Schindelin via GitGitGadget <gitgitgadget@gmail.com>
Date
Dec 13, 2019, 08:08 UTC
Message-ID
<e7d218793b506d7e9116d9f3a3fabf220191bc72.1576224486.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.173.git.1576224486.gitgitgadget@gmail.com>
From: Johannes Schindelin <johannes.schindelin@gmx.de>

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>
---
 add-patch.c                | 50 +++++++++++++++++++++++++++++++++++++-
 t/t3701-add-interactive.sh | 14 +++++++++++
 2 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/add-patch.c b/add-patch.c
index fdbb1e3e22..fd72850c65 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -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 @@ static int patch_update_file(struct add_p_state *s,
 					  "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)
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 57c656a20c..12ee321707 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -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:
 	#
-- 
gitgitgadget
Previous: Johannes Schindelin via GitGitGadgetNext: Johannes Schindelin via GitGitGadget
Message 16 of 20 in “Implement the git add --patch backend in C”
  1. 00/19 Implement the git add --patch backend in CJohannes Schindelin via GitGitGadget, Dec 13, 2019
  2. 02/19 built-in add -i: wire up the new C code for the `patch` commandJohannes Schindelin via GitGitGadget, Dec 13, 2019
  3. 01/19 built-in add -i: start implementing the `patch` functionality in CJohannes Schindelin via GitGitGadget, Dec 13, 2019
  4. 03/19 built-in add -p: show colored hunks by defaultJohannes Schindelin via GitGitGadget, Dec 13, 2019
  5. 07/19 built-in add -p: support multi-file diffsJohannes Schindelin via GitGitGadget, Dec 13, 2019
  6. 06/19 built-in add -p: offer a helpful error message when hunk navigation failedJohannes Schindelin via GitGitGadget, Dec 13, 2019
  7. 10/19 built-in add -p: show different prompts for mode changes and deletionsJohannes Schindelin via GitGitGadget, Dec 13, 2019
  8. 09/19 built-in app -p: allow selecting a mode change as a "hunk"Johannes Schindelin via GitGitGadget, Dec 13, 2019
  9. 13/19 strbuf: add a helper function to call the editor "on an strbuf"Johannes Schindelin via GitGitGadget, Dec 13, 2019
  10. 12/19 built-in add -p: coalesce hunks after splitting themJohannes Schindelin via GitGitGadget, Dec 13, 2019
  11. 15/19 built-in add -p: implement the 'g' ("goto") commandJohannes Schindelin via GitGitGadget, Dec 13, 2019
  12. 14/19 built-in add -p: implement hunk editingJohannes Schindelin via GitGitGadget, Dec 13, 2019
  13. 11/19 built-in add -p: implement the hunk splitting featureJohannes Schindelin via GitGitGadget, Dec 13, 2019
  14. 19/19 built-in add -p: show helpful hint when nothing can be stagedJohannes Schindelin via GitGitGadget, Dec 13, 2019
  15. 18/19 built-in add -p: only show the applicable parts of the help textJohannes Schindelin via GitGitGadget, Dec 13, 2019
  16. 16/19 built-in add -p: implement the '/' ("search regex") commandJohannes Schindelin via GitGitGadget, Dec 13, 2019
  17. 17/19 built-in add -p: implement the 'q' ("quit") commandJohannes Schindelin via GitGitGadget, Dec 13, 2019
  18. 08/19 built-in add -p: handle deleted empty filesJohannes Schindelin via GitGitGadget, Dec 13, 2019
  19. 04/19 built-in add -p: adjust hunk headers as neededJohannes Schindelin via GitGitGadget, Dec 13, 2019
  20. 05/19 built-in add -p: color the prompt and the help textJohannes Schindelin via GitGitGadget, Dec 13, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.