Skip to content

Commit a751e79

Browse files
committed
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
2 parents f19f1b6 + 417b181 commit a751e79

9 files changed

Lines changed: 241 additions & 43 deletions

File tree

add-interactive.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,
6464
s->r = r;
6565
s->context = -1;
6666
s->interhunkcontext = -1;
67+
s->auto_advance = add_p_opt->auto_advance;
6768

6869
s->use_color_interactive = check_color_config(r, "color.interactive");
6970

@@ -1017,6 +1018,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
10171018
struct add_p_opt add_p_opt = {
10181019
.context = s->context,
10191020
.interhunkcontext = s->interhunkcontext,
1021+
.auto_advance = s->auto_advance
10201022
};
10211023
struct strvec args = STRVEC_INIT;
10221024
struct pathspec ps_selected = { 0 };

add-interactive.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
struct add_p_opt {
77
int context;
88
int interhunkcontext;
9+
int auto_advance;
910
};
1011

11-
#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 }
12+
#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1, .auto_advance = 1 }
1213

1314
struct add_i_state {
1415
struct repository *r;
@@ -29,6 +30,7 @@ struct add_i_state {
2930
int use_single_key;
3031
char *interactive_diff_filter, *interactive_diff_algorithm;
3132
int context, interhunkcontext;
33+
int auto_advance;
3234
};
3335

3436
void init_add_i_state(struct add_i_state *s, struct repository *r,

add-patch.c

Lines changed: 112 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,44 @@ N_("j - go to the next undecided hunk, roll over at the bottom\n"
14181418
"e - manually edit the current hunk\n"
14191419
"p - print the current hunk\n"
14201420
"P - print the current hunk using the pager\n"
1421-
"? - print help\n");
1421+
"> - go to the next file, roll over at the bottom\n"
1422+
"< - go to the previous file, roll over at the top\n"
1423+
"? - print help\n"
1424+
"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n");
1425+
1426+
static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)
1427+
{
1428+
struct child_process cp = CHILD_PROCESS_INIT;
1429+
size_t j;
1430+
1431+
/* Any hunk to be used? */
1432+
for (j = 0; j < file_diff->hunk_nr; j++)
1433+
if (file_diff->hunk[j].use == USE_HUNK)
1434+
break;
1435+
1436+
if (j < file_diff->hunk_nr ||
1437+
(!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
1438+
/* At least one hunk selected: apply */
1439+
strbuf_reset(&s->buf);
1440+
reassemble_patch(s, file_diff, 0, &s->buf);
1441+
1442+
discard_index(s->s.r->index);
1443+
if (s->mode->apply_for_checkout)
1444+
apply_for_checkout(s, &s->buf,
1445+
s->mode->is_reverse);
1446+
else {
1447+
setup_child_process(s, &cp, "apply", NULL);
1448+
strvec_pushv(&cp.args, s->mode->apply_args);
1449+
if (pipe_command(&cp, s->buf.buf, s->buf.len,
1450+
NULL, 0, NULL, 0))
1451+
error(_("'git apply' failed"));
1452+
}
1453+
if (repo_read_index(s->s.r) >= 0)
1454+
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
1455+
1, NULL, NULL, NULL);
1456+
}
1457+
1458+
}
14221459

14231460
static size_t dec_mod(size_t a, size_t m)
14241461
{
@@ -1441,20 +1478,21 @@ static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)
14411478
return false;
14421479
}
14431480

1444-
static int patch_update_file(struct add_p_state *s,
1445-
struct file_diff *file_diff)
1481+
static size_t patch_update_file(struct add_p_state *s, size_t idx)
14461482
{
14471483
size_t hunk_index = 0;
14481484
ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
14491485
struct hunk *hunk;
14501486
char ch;
1451-
struct child_process cp = CHILD_PROCESS_INIT;
1452-
int colored = !!s->colored.len, quit = 0, use_pager = 0;
1487+
int colored = !!s->colored.len, use_pager = 0;
14531488
enum prompt_mode_type prompt_mode_type;
1489+
int all_decided = 0;
1490+
struct file_diff *file_diff = s->file_diff + idx;
1491+
size_t patch_update_resp = idx;
14541492

14551493
/* Empty added files have no hunks */
14561494
if (!file_diff->hunk_nr && !file_diff->added)
1457-
return 0;
1495+
return patch_update_resp + 1;
14581496

14591497
strbuf_reset(&s->buf);
14601498
render_diff_header(s, file_diff, colored, &s->buf);
@@ -1468,7 +1506,9 @@ static int patch_update_file(struct add_p_state *s,
14681506
ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
14691507
ALLOW_SEARCH_AND_GOTO = 1 << 4,
14701508
ALLOW_SPLIT = 1 << 5,
1471-
ALLOW_EDIT = 1 << 6
1509+
ALLOW_EDIT = 1 << 6,
1510+
ALLOW_GOTO_PREVIOUS_FILE = 1 << 7,
1511+
ALLOW_GOTO_NEXT_FILE = 1 << 8
14721512
} permitted = 0;
14731513

14741514
if (hunk_index >= file_diff->hunk_nr)
@@ -1499,9 +1539,14 @@ static int patch_update_file(struct add_p_state *s,
14991539

15001540
/* Everything decided? */
15011541
if (undecided_previous < 0 && undecided_next < 0 &&
1502-
hunk->use != UNDECIDED_HUNK)
1503-
break;
1504-
1542+
hunk->use != UNDECIDED_HUNK) {
1543+
if (!s->s.auto_advance)
1544+
all_decided = 1;
1545+
else {
1546+
patch_update_resp++;
1547+
break;
1548+
}
1549+
}
15051550
strbuf_reset(&s->buf);
15061551
if (file_diff->hunk_nr) {
15071552
if (rendered_hunk_index != hunk_index) {
@@ -1549,6 +1594,14 @@ static int patch_update_file(struct add_p_state *s,
15491594
permitted |= ALLOW_EDIT;
15501595
strbuf_addstr(&s->buf, ",e");
15511596
}
1597+
if (!s->s.auto_advance && s->file_diff_nr > 1) {
1598+
permitted |= ALLOW_GOTO_NEXT_FILE;
1599+
strbuf_addstr(&s->buf, ",>");
1600+
}
1601+
if (!s->s.auto_advance && s->file_diff_nr > 1) {
1602+
permitted |= ALLOW_GOTO_PREVIOUS_FILE;
1603+
strbuf_addstr(&s->buf, ",<");
1604+
}
15521605
strbuf_addstr(&s->buf, ",p,P");
15531606
}
15541607
if (file_diff->deleted)
@@ -1577,7 +1630,7 @@ static int patch_update_file(struct add_p_state *s,
15771630
fputs(s->s.reset_color_interactive, stdout);
15781631
fflush(stdout);
15791632
if (read_single_character(s) == EOF) {
1580-
quit = 1;
1633+
patch_update_resp = s->file_diff_nr;
15811634
break;
15821635
}
15831636

@@ -1623,8 +1676,30 @@ static int patch_update_file(struct add_p_state *s,
16231676
hunk->use = SKIP_HUNK;
16241677
}
16251678
} else if (ch == 'q') {
1626-
quit = 1;
1679+
patch_update_resp = s->file_diff_nr;
16271680
break;
1681+
} else if (!s->s.auto_advance && s->answer.buf[0] == '>') {
1682+
if (permitted & ALLOW_GOTO_NEXT_FILE) {
1683+
if (patch_update_resp == s->file_diff_nr - 1)
1684+
patch_update_resp = 0;
1685+
else
1686+
patch_update_resp++;
1687+
break;
1688+
} else {
1689+
err(s, _("No next file"));
1690+
continue;
1691+
}
1692+
} else if (!s->s.auto_advance && s->answer.buf[0] == '<') {
1693+
if (permitted & ALLOW_GOTO_PREVIOUS_FILE) {
1694+
if (patch_update_resp == 0)
1695+
patch_update_resp = s->file_diff_nr - 1;
1696+
else
1697+
patch_update_resp--;
1698+
break;
1699+
} else {
1700+
err(s, _("No previous file"));
1701+
continue;
1702+
}
16281703
} else if (s->answer.buf[0] == 'K') {
16291704
if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
16301705
hunk_index = dec_mod(hunk_index,
@@ -1770,6 +1845,18 @@ static int patch_update_file(struct add_p_state *s,
17701845
* commands shown in the prompt that are not
17711846
* always available.
17721847
*/
1848+
if (all_decided && !strncmp(p, "HUNKS SUMMARY", 13)) {
1849+
int total = file_diff->hunk_nr, used = 0, skipped = 0;
1850+
1851+
for (i = 0; i < file_diff->hunk_nr; i++) {
1852+
if (file_diff->hunk[i].use == USE_HUNK)
1853+
used += 1;
1854+
if (file_diff->hunk[i].use == SKIP_HUNK)
1855+
skipped += 1;
1856+
}
1857+
color_fprintf_ln(stdout, s->s.help_color, _(p),
1858+
total, used, skipped);
1859+
}
17731860
if (*p != '?' && !strchr(s->buf.buf, *p))
17741861
continue;
17751862

@@ -1782,35 +1869,11 @@ static int patch_update_file(struct add_p_state *s,
17821869
}
17831870
}
17841871

1785-
/* Any hunk to be used? */
1786-
for (i = 0; i < file_diff->hunk_nr; i++)
1787-
if (file_diff->hunk[i].use == USE_HUNK)
1788-
break;
1789-
1790-
if (i < file_diff->hunk_nr ||
1791-
(!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
1792-
/* At least one hunk selected: apply */
1793-
strbuf_reset(&s->buf);
1794-
reassemble_patch(s, file_diff, 0, &s->buf);
1795-
1796-
discard_index(s->s.r->index);
1797-
if (s->mode->apply_for_checkout)
1798-
apply_for_checkout(s, &s->buf,
1799-
s->mode->is_reverse);
1800-
else {
1801-
setup_child_process(s, &cp, "apply", NULL);
1802-
strvec_pushv(&cp.args, s->mode->apply_args);
1803-
if (pipe_command(&cp, s->buf.buf, s->buf.len,
1804-
NULL, 0, NULL, 0))
1805-
error(_("'git apply' failed"));
1806-
}
1807-
if (repo_read_index(s->s.r) >= 0)
1808-
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
1809-
1, NULL, NULL, NULL);
1810-
}
1872+
if (s->s.auto_advance)
1873+
apply_patch(s, file_diff);
18111874

18121875
putchar('\n');
1813-
return quit;
1876+
return patch_update_resp;
18141877
}
18151878

18161879
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,
18591922
return -1;
18601923
}
18611924

1862-
for (i = 0; i < s.file_diff_nr; i++)
1863-
if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)
1925+
for (i = 0; i < s.file_diff_nr;) {
1926+
if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {
18641927
binary_count++;
1865-
else if (patch_update_file(&s, s.file_diff + i))
1928+
i++;
1929+
continue;
1930+
}
1931+
if ((i = patch_update_file(&s, i)) == s.file_diff_nr)
18661932
break;
1933+
}
1934+
if (!s.s.auto_advance)
1935+
for (i = 0; i < s.file_diff_nr; i++)
1936+
apply_patch(&s, s.file_diff + i);
18671937

18681938
if (s.file_diff_nr == 0)
18691939
err(&s, _("No changes."));

builtin/add.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ static struct option builtin_add_options[] = {
256256
OPT_GROUP(""),
257257
OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
258258
OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
259+
OPT_BOOL(0, "auto-advance", &add_p_opt.auto_advance,
260+
N_("auto advance to the next file when selecting hunks interactively")),
259261
OPT_DIFF_UNIFIED(&add_p_opt.context),
260262
OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
261263
OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
@@ -418,6 +420,8 @@ int cmd_add(int argc,
418420
die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch");
419421
if (add_p_opt.interhunkcontext != -1)
420422
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch");
423+
if (!add_p_opt.auto_advance)
424+
die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--interactive/--patch");
421425
}
422426

423427
if (edit_interactive) {

builtin/checkout.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ struct checkout_opts {
4747
int patch_mode;
4848
int patch_context;
4949
int patch_interhunk_context;
50+
int auto_advance;
5051
int quiet;
5152
int merge;
5253
int force;
@@ -95,6 +96,7 @@ struct checkout_opts {
9596
.merge = -1, \
9697
.patch_context = -1, \
9798
.patch_interhunk_context = -1, \
99+
.auto_advance = 1, \
98100
}
99101

100102
struct branch_info {
@@ -533,6 +535,7 @@ static int checkout_paths(const struct checkout_opts *opts,
533535
struct add_p_opt add_p_opt = {
534536
.context = opts->patch_context,
535537
.interhunkcontext = opts->patch_interhunk_context,
538+
.auto_advance = opts->auto_advance
536539
};
537540
const char *rev = new_branch_info->name;
538541
char rev_oid[GIT_MAX_HEXSZ + 1];
@@ -1845,6 +1848,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
18451848
die(_("the option '%s' requires '%s'"), "--unified", "--patch");
18461849
if (opts->patch_interhunk_context != -1)
18471850
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
1851+
if (!opts->auto_advance)
1852+
die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--patch");
18481853
}
18491854

18501855
if (opts->show_progress < 0) {
@@ -2043,6 +2048,8 @@ int cmd_checkout(int argc,
20432048
OPT_BOOL(0, "guess", &opts.dwim_new_local_branch,
20442049
N_("second guess 'git checkout <no-such-branch>' (default)")),
20452050
OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
2051+
OPT_BOOL(0, "auto-advance", &opts.auto_advance,
2052+
N_("auto advance to the next file when selecting hunks interactively")),
20462053
OPT_END()
20472054
};
20482055

builtin/reset.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ int cmd_reset(int argc,
371371
PARSE_OPT_OPTARG,
372372
option_parse_recurse_submodules_worktree_updater),
373373
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
374+
OPT_BOOL(0, "auto-advance", &add_p_opt.auto_advance,
375+
N_("auto advance to the next file when selecting hunks interactively")),
374376
OPT_DIFF_UNIFIED(&add_p_opt.context),
375377
OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
376378
OPT_BOOL('N', "intent-to-add", &intent_to_add,
@@ -443,6 +445,8 @@ int cmd_reset(int argc,
443445
die(_("the option '%s' requires '%s'"), "--unified", "--patch");
444446
if (add_p_opt.interhunkcontext != -1)
445447
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
448+
if (!add_p_opt.auto_advance)
449+
die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--patch");
446450
}
447451

448452
/* git reset tree [--] paths... can be used to

builtin/stash.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1849,6 +1849,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
18491849
N_("stash staged changes only")),
18501850
OPT_BOOL('p', "patch", &patch_mode,
18511851
N_("stash in patch mode")),
1852+
OPT_BOOL(0, "auto-advance", &add_p_opt.auto_advance,
1853+
N_("auto advance to the next file when selecting hunks interactively")),
18521854
OPT_DIFF_UNIFIED(&add_p_opt.context),
18531855
OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
18541856
OPT__QUIET(&quiet, N_("quiet mode")),
@@ -1911,6 +1913,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
19111913
die(_("the option '%s' requires '%s'"), "--unified", "--patch");
19121914
if (add_p_opt.interhunkcontext != -1)
19131915
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
1916+
if (!add_p_opt.auto_advance)
1917+
die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--patch");
19141918
}
19151919

19161920
if (add_p_opt.context < -1)
@@ -1952,6 +1956,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,
19521956
N_("stash staged changes only")),
19531957
OPT_BOOL('p', "patch", &patch_mode,
19541958
N_("stash in patch mode")),
1959+
OPT_BOOL(0, "auto-advance", &add_p_opt.auto_advance,
1960+
N_("auto advance to the next file when selecting hunks interactively")),
19551961
OPT_DIFF_UNIFIED(&add_p_opt.context),
19561962
OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
19571963
OPT__QUIET(&quiet, N_("quiet mode")),
@@ -1983,6 +1989,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,
19831989
die(_("the option '%s' requires '%s'"), "--unified", "--patch");
19841990
if (add_p_opt.interhunkcontext != -1)
19851991
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
1992+
if (!add_p_opt.auto_advance)
1993+
die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--patch");
19861994
}
19871995

19881996
ret = do_push_stash(&ps, stash_msg, quiet, keep_index,

0 commit comments

Comments
 (0)