aboutsummaryrefslogtreecommitdiff
path: root/add-patch.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2026-03-03 11:08:13 -0800
committerJunio C Hamano <gitster@pobox.com>2026-03-03 11:08:13 -0800
commita751e79acaf5a02cc24fa6b0b2656e13c57ad803 (patch)
tree3bf8a7611463cb61ce1723b569119a22a2ea3039 /add-patch.c
parentf19f1b6cf37d22cf317b5c3b52a11eede1abe267 (diff)
parent417b181f99ce53f50dea6541430cfe1f1f359a6a (diff)
downloadgit-a751e79acaf5a02cc24fa6b0b2656e13c57ad803.tar.xz
Merge branch 'aa/add-p-no-auto-advance'
"git add -p" learned a new mode that allows the user to revisit a file that was already dealt with. * aa/add-p-no-auto-advance: add-patch: allow interfile navigation when selecting hunks add-patch: allow all-or-none application of patches add-patch: modify patch_update_file() signature interactive -p: add new `--auto-advance` flag
Diffstat (limited to 'add-patch.c')
-rw-r--r--add-patch.c154
1 files changed, 112 insertions, 42 deletions
diff --git a/add-patch.c b/add-patch.c
index 49d6911a32..8c03f710d3 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -1418,7 +1418,44 @@ N_("j - go to the next undecided hunk, roll over at the bottom\n"
"e - manually edit the current hunk\n"
"p - print the current hunk\n"
"P - print the current hunk using the pager\n"
- "? - print help\n");
+ "> - go to the next file, roll over at the bottom\n"
+ "< - go to the previous file, roll over at the top\n"
+ "? - print help\n"
+ "HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n");
+
+static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ size_t j;
+
+ /* Any hunk to be used? */
+ for (j = 0; j < file_diff->hunk_nr; j++)
+ if (file_diff->hunk[j].use == USE_HUNK)
+ break;
+
+ if (j < file_diff->hunk_nr ||
+ (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
+ /* At least one hunk selected: apply */
+ strbuf_reset(&s->buf);
+ reassemble_patch(s, file_diff, 0, &s->buf);
+
+ discard_index(s->s.r->index);
+ if (s->mode->apply_for_checkout)
+ apply_for_checkout(s, &s->buf,
+ s->mode->is_reverse);
+ else {
+ setup_child_process(s, &cp, "apply", NULL);
+ strvec_pushv(&cp.args, s->mode->apply_args);
+ if (pipe_command(&cp, s->buf.buf, s->buf.len,
+ NULL, 0, NULL, 0))
+ error(_("'git apply' failed"));
+ }
+ if (repo_read_index(s->s.r) >= 0)
+ repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
+ 1, NULL, NULL, NULL);
+ }
+
+}
static size_t dec_mod(size_t a, size_t m)
{
@@ -1441,20 +1478,21 @@ static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)
return false;
}
-static int patch_update_file(struct add_p_state *s,
- struct file_diff *file_diff)
+static size_t patch_update_file(struct add_p_state *s, size_t idx)
{
size_t hunk_index = 0;
ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
struct hunk *hunk;
char ch;
- struct child_process cp = CHILD_PROCESS_INIT;
- int colored = !!s->colored.len, quit = 0, use_pager = 0;
+ int colored = !!s->colored.len, use_pager = 0;
enum prompt_mode_type prompt_mode_type;
+ int all_decided = 0;
+ struct file_diff *file_diff = s->file_diff + idx;
+ size_t patch_update_resp = idx;
/* Empty added files have no hunks */
if (!file_diff->hunk_nr && !file_diff->added)
- return 0;
+ return patch_update_resp + 1;
strbuf_reset(&s->buf);
render_diff_header(s, file_diff, colored, &s->buf);
@@ -1468,7 +1506,9 @@ static int patch_update_file(struct add_p_state *s,
ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
ALLOW_SEARCH_AND_GOTO = 1 << 4,
ALLOW_SPLIT = 1 << 5,
- ALLOW_EDIT = 1 << 6
+ ALLOW_EDIT = 1 << 6,
+ ALLOW_GOTO_PREVIOUS_FILE = 1 << 7,
+ ALLOW_GOTO_NEXT_FILE = 1 << 8
} permitted = 0;
if (hunk_index >= file_diff->hunk_nr)
@@ -1499,9 +1539,14 @@ static int patch_update_file(struct add_p_state *s,
/* Everything decided? */
if (undecided_previous < 0 && undecided_next < 0 &&
- hunk->use != UNDECIDED_HUNK)
- break;
-
+ hunk->use != UNDECIDED_HUNK) {
+ if (!s->s.auto_advance)
+ all_decided = 1;
+ else {
+ patch_update_resp++;
+ break;
+ }
+ }
strbuf_reset(&s->buf);
if (file_diff->hunk_nr) {
if (rendered_hunk_index != hunk_index) {
@@ -1549,6 +1594,14 @@ static int patch_update_file(struct add_p_state *s,
permitted |= ALLOW_EDIT;
strbuf_addstr(&s->buf, ",e");
}
+ if (!s->s.auto_advance && s->file_diff_nr > 1) {
+ permitted |= ALLOW_GOTO_NEXT_FILE;
+ strbuf_addstr(&s->buf, ",>");
+ }
+ if (!s->s.auto_advance && s->file_diff_nr > 1) {
+ permitted |= ALLOW_GOTO_PREVIOUS_FILE;
+ strbuf_addstr(&s->buf, ",<");
+ }
strbuf_addstr(&s->buf, ",p,P");
}
if (file_diff->deleted)
@@ -1577,7 +1630,7 @@ static int patch_update_file(struct add_p_state *s,
fputs(s->s.reset_color_interactive, stdout);
fflush(stdout);
if (read_single_character(s) == EOF) {
- quit = 1;
+ patch_update_resp = s->file_diff_nr;
break;
}
@@ -1623,8 +1676,30 @@ soft_increment:
hunk->use = SKIP_HUNK;
}
} else if (ch == 'q') {
- quit = 1;
+ patch_update_resp = s->file_diff_nr;
break;
+ } else if (!s->s.auto_advance && s->answer.buf[0] == '>') {
+ if (permitted & ALLOW_GOTO_NEXT_FILE) {
+ if (patch_update_resp == s->file_diff_nr - 1)
+ patch_update_resp = 0;
+ else
+ patch_update_resp++;
+ break;
+ } else {
+ err(s, _("No next file"));
+ continue;
+ }
+ } else if (!s->s.auto_advance && s->answer.buf[0] == '<') {
+ if (permitted & ALLOW_GOTO_PREVIOUS_FILE) {
+ if (patch_update_resp == 0)
+ patch_update_resp = s->file_diff_nr - 1;
+ else
+ patch_update_resp--;
+ break;
+ } else {
+ err(s, _("No previous file"));
+ continue;
+ }
} else if (s->answer.buf[0] == 'K') {
if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
hunk_index = dec_mod(hunk_index,
@@ -1770,6 +1845,18 @@ soft_increment:
* commands shown in the prompt that are not
* always available.
*/
+ if (all_decided && !strncmp(p, "HUNKS SUMMARY", 13)) {
+ int total = file_diff->hunk_nr, used = 0, skipped = 0;
+
+ for (i = 0; i < file_diff->hunk_nr; i++) {
+ if (file_diff->hunk[i].use == USE_HUNK)
+ used += 1;
+ if (file_diff->hunk[i].use == SKIP_HUNK)
+ skipped += 1;
+ }
+ color_fprintf_ln(stdout, s->s.help_color, _(p),
+ total, used, skipped);
+ }
if (*p != '?' && !strchr(s->buf.buf, *p))
continue;
@@ -1782,35 +1869,11 @@ soft_increment:
}
}
- /* Any hunk to be used? */
- for (i = 0; i < file_diff->hunk_nr; i++)
- if (file_diff->hunk[i].use == USE_HUNK)
- break;
-
- if (i < file_diff->hunk_nr ||
- (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
- /* At least one hunk selected: apply */
- strbuf_reset(&s->buf);
- reassemble_patch(s, file_diff, 0, &s->buf);
-
- discard_index(s->s.r->index);
- if (s->mode->apply_for_checkout)
- apply_for_checkout(s, &s->buf,
- s->mode->is_reverse);
- else {
- setup_child_process(s, &cp, "apply", NULL);
- strvec_pushv(&cp.args, s->mode->apply_args);
- if (pipe_command(&cp, s->buf.buf, s->buf.len,
- NULL, 0, NULL, 0))
- error(_("'git apply' failed"));
- }
- if (repo_read_index(s->s.r) >= 0)
- repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
- 1, NULL, NULL, NULL);
- }
+ if (s->s.auto_advance)
+ apply_patch(s, file_diff);
putchar('\n');
- return quit;
+ return patch_update_resp;
}
int run_add_p(struct repository *r, enum add_p_mode mode,
@@ -1859,11 +1922,18 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
return -1;
}
- for (i = 0; i < s.file_diff_nr; i++)
- if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)
+ for (i = 0; i < s.file_diff_nr;) {
+ if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {
binary_count++;
- else if (patch_update_file(&s, s.file_diff + i))
+ i++;
+ continue;
+ }
+ if ((i = patch_update_file(&s, i)) == s.file_diff_nr)
break;
+ }
+ if (!s.s.auto_advance)
+ for (i = 0; i < s.file_diff_nr; i++)
+ apply_patch(&s, s.file_diff + i);
if (s.file_diff_nr == 0)
err(&s, _("No changes."));