Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Documentation/RelNotes/2.54.0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ UI, Workflows & Features
against the tree of the (potentially quite different from the
current working tree) given commit.

* "git add -p" learned a new mode that allows the user to revisit a
file that was already dealt with.


Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
Expand Down Expand Up @@ -179,6 +182,9 @@ Fixes since v2.53
* Update build precedure for mergetool documentation in meson-based builds.
(merge 58e4eeeeb5 pw/meson-doc-mergetool later to maint).

* An earlier attempt to optimize "git subtree" discarded too much
relevant histories, which has been corrected.

* Other code cleanup, docfix, build fix, etc.
(merge d79fff4a11 jk/remote-tracking-ref-leakfix later to maint).
(merge 7a747f972d dd/t5403-modernise later to maint).
Expand Down Expand Up @@ -207,3 +213,10 @@ Fixes since v2.53
(merge 96286f14b0 ty/symlinks-use-unsigned-for-bitset later to maint).
(merge b10e0cb1f3 kh/doc-am-xref later to maint).
(merge ed84bc1c0d kh/doc-patch-id-4 later to maint).
(merge 7451864bfa sc/pack-redundant-leakfix later to maint).

* A prefetch call can be triggered to access a stale diff_queue entry
after diffcore-break breaks a filepair into two and freed the
original entry that is no longer used, leading to a segfault, which
has been corrected.
(merge 2d88ab078d hy/diff-lazy-fetch-with-break-fix later to maint).
2 changes: 2 additions & 0 deletions add-interactive.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,
s->r = r;
s->context = -1;
s->interhunkcontext = -1;
s->auto_advance = add_p_opt->auto_advance;

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

Expand Down Expand Up @@ -1017,6 +1018,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
struct add_p_opt add_p_opt = {
.context = s->context,
.interhunkcontext = s->interhunkcontext,
.auto_advance = s->auto_advance
};
struct strvec args = STRVEC_INIT;
struct pathspec ps_selected = { 0 };
Expand Down
4 changes: 3 additions & 1 deletion add-interactive.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
struct add_p_opt {
int context;
int interhunkcontext;
int auto_advance;
};

#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 }
#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1, .auto_advance = 1 }

struct add_i_state {
struct repository *r;
Expand All @@ -29,6 +30,7 @@ struct add_i_state {
int use_single_key;
char *interactive_diff_filter, *interactive_diff_algorithm;
int context, interhunkcontext;
int auto_advance;
};

void init_add_i_state(struct add_i_state *s, struct repository *r,
Expand Down
154 changes: 112 additions & 42 deletions add-patch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -1623,8 +1676,30 @@ static int patch_update_file(struct add_p_state *s,
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,
Expand Down Expand Up @@ -1770,6 +1845,18 @@ static int patch_update_file(struct add_p_state *s,
* 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;

Expand All @@ -1782,35 +1869,11 @@ static int patch_update_file(struct add_p_state *s,
}
}

/* 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,
Expand Down Expand Up @@ -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."));
Expand Down
4 changes: 4 additions & 0 deletions builtin/add.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ static struct option builtin_add_options[] = {
OPT_GROUP(""),
OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
OPT_BOOL(0, "auto-advance", &add_p_opt.auto_advance,
N_("auto advance to the next file when selecting hunks interactively")),
OPT_DIFF_UNIFIED(&add_p_opt.context),
OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
Expand Down Expand Up @@ -418,6 +420,8 @@ int cmd_add(int argc,
die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch");
if (add_p_opt.interhunkcontext != -1)
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch");
if (!add_p_opt.auto_advance)
die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--interactive/--patch");
}

if (edit_interactive) {
Expand Down
7 changes: 7 additions & 0 deletions builtin/checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct checkout_opts {
int patch_mode;
int patch_context;
int patch_interhunk_context;
int auto_advance;
int quiet;
int merge;
int force;
Expand Down Expand Up @@ -95,6 +96,7 @@ struct checkout_opts {
.merge = -1, \
.patch_context = -1, \
.patch_interhunk_context = -1, \
.auto_advance = 1, \
}

struct branch_info {
Expand Down Expand Up @@ -533,6 +535,7 @@ static int checkout_paths(const struct checkout_opts *opts,
struct add_p_opt add_p_opt = {
.context = opts->patch_context,
.interhunkcontext = opts->patch_interhunk_context,
.auto_advance = opts->auto_advance
};
const char *rev = new_branch_info->name;
char rev_oid[GIT_MAX_HEXSZ + 1];
Expand Down Expand Up @@ -1845,6 +1848,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
die(_("the option '%s' requires '%s'"), "--unified", "--patch");
if (opts->patch_interhunk_context != -1)
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
if (!opts->auto_advance)
die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--patch");
}

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

Expand Down
4 changes: 3 additions & 1 deletion builtin/pack-redundant.c
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,10 @@ static struct pack_list * add_pack(struct packed_git *p)
l.pack = p;
llist_init(&l.remaining_objects);

if (open_pack_index(p))
if (open_pack_index(p)) {
llist_free(l.remaining_objects);
return NULL;
}

base = p->index_data;
base += 256 * 4 + ((p->index_version < 2) ? 4 : 8);
Expand Down
4 changes: 4 additions & 0 deletions builtin/reset.c
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ int cmd_reset(int argc,
PARSE_OPT_OPTARG,
option_parse_recurse_submodules_worktree_updater),
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
OPT_BOOL(0, "auto-advance", &add_p_opt.auto_advance,
N_("auto advance to the next file when selecting hunks interactively")),
OPT_DIFF_UNIFIED(&add_p_opt.context),
OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
OPT_BOOL('N', "intent-to-add", &intent_to_add,
Expand Down Expand Up @@ -443,6 +445,8 @@ int cmd_reset(int argc,
die(_("the option '%s' requires '%s'"), "--unified", "--patch");
if (add_p_opt.interhunkcontext != -1)
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
if (!add_p_opt.auto_advance)
die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--patch");
}

/* git reset tree [--] paths... can be used to
Expand Down
Loading