From: "Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Johannes Schindelin <johannes.schindelin@gmx.de>,
Junio C Hamano <gitster@pobox.com>,
Johannes Schindelin <johannes.schindelin@gmx.de>
Subject: [PATCH 15/19] built-in add -p: implement the 'g' ("goto") command
Date: Fri, 13 Dec 2019 08:08:02 +0000 [thread overview]
Message-ID: <9a2cf7ed71fe012de93a5d23b72c9c3aa0bdcdf9.1576224486.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.173.git.1576224486.gitgitgadget@gmail.com>
From: Johannes Schindelin <johannes.schindelin@gmx.de>
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>
---
add-patch.c | 88 ++++++++++++++++++++++++++++++++++++++
t/t3701-add-interactive.sh | 16 +++++++
2 files changed, 104 insertions(+)
diff --git a/add-patch.c b/add-patch.c
index ea863ca09d..fdbb1e3e22 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -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 @@ static int patch_update_file(struct add_p_state *s,
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)
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index fe383be50e..57c656a20c 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -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:
#
--
gitgitgadget
next prev parent reply other threads:[~2019-12-13 8:08 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-12-13 8:07 [PATCH 00/19] Implement the git add --patch backend in C Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 01/19] built-in add -i: start implementing the `patch` functionality " Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 02/19] built-in add -i: wire up the new C code for the `patch` command Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 03/19] built-in add -p: show colored hunks by default Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 04/19] built-in add -p: adjust hunk headers as needed Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 05/19] built-in add -p: color the prompt and the help text Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 06/19] built-in add -p: offer a helpful error message when hunk navigation failed Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 07/19] built-in add -p: support multi-file diffs Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 08/19] built-in add -p: handle deleted empty files Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 09/19] built-in app -p: allow selecting a mode change as a "hunk" Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 10/19] built-in add -p: show different prompts for mode changes and deletions Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 11/19] built-in add -p: implement the hunk splitting feature Johannes Schindelin via GitGitGadget
2019-12-13 8:07 ` [PATCH 12/19] built-in add -p: coalesce hunks after splitting them Johannes Schindelin via GitGitGadget
2019-12-13 8:08 ` [PATCH 13/19] strbuf: add a helper function to call the editor "on an strbuf" Johannes Schindelin via GitGitGadget
2019-12-13 8:08 ` [PATCH 14/19] built-in add -p: implement hunk editing Johannes Schindelin via GitGitGadget
2019-12-13 8:08 ` Johannes Schindelin via GitGitGadget [this message]
2019-12-13 8:08 ` [PATCH 16/19] built-in add -p: implement the '/' ("search regex") command Johannes Schindelin via GitGitGadget
2019-12-13 8:08 ` [PATCH 17/19] built-in add -p: implement the 'q' ("quit") command Johannes Schindelin via GitGitGadget
2019-12-13 8:08 ` [PATCH 18/19] built-in add -p: only show the applicable parts of the help text Johannes Schindelin via GitGitGadget
2019-12-13 8:08 ` [PATCH 19/19] built-in add -p: show helpful hint when nothing can be staged Johannes Schindelin via GitGitGadget
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=9a2cf7ed71fe012de93a5d23b72c9c3aa0bdcdf9.1576224486.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=johannes.schindelin@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).