diff --git a/add-patch.c b/add-patch.c index 4f089c82d0..b4dc7d2293 100644 --- a/add-patch.c +++ b/add-patch.c @@ -4,11 +4,13 @@ #include "git-compat-util.h" #include "add-patch.h" #include "advice.h" +#include "commit.h" #include "config.h" #include "diff.h" #include "editor.h" #include "environment.h" #include "gettext.h" +#include "hex.h" #include "object-name.h" #include "pager.h" #include "read-cache-ll.h" @@ -263,6 +265,8 @@ struct hunk { struct add_p_state { struct repository *r; + struct index_state *index; + const char *index_file; struct interactive_config cfg; struct strbuf answer, buf; @@ -438,7 +442,7 @@ static void setup_child_process(struct add_p_state *s, cp->git_cmd = 1; strvec_pushf(&cp->env, - INDEX_ENVIRONMENT "=%s", s->r->index_file); + INDEX_ENVIRONMENT "=%s", s->index_file); } static int parse_range(const char **p, @@ -1559,7 +1563,7 @@ static void apply_patch(struct add_p_state *s, struct file_diff *file_diff) strbuf_reset(&s->buf); reassemble_patch(s, file_diff, 0, &s->buf); - discard_index(s->r->index); + discard_index(s->index); if (s->mode->apply_for_checkout) apply_for_checkout(s, &s->buf, s->mode->is_reverse); @@ -1570,9 +1574,11 @@ static void apply_patch(struct add_p_state *s, struct file_diff *file_diff) NULL, 0, NULL, 0)) error(_("'git apply' failed")); } - if (repo_read_index(s->r) >= 0) + if (read_index_from(s->index, s->index_file, s->r->gitdir) >= 0 && + s->index == s->r->index) { repo_refresh_and_write_index(s->r, REFRESH_QUIET, 0, 1, NULL, NULL, NULL); + } } } @@ -1996,18 +2002,51 @@ soft_increment: return patch_update_resp; } +static int run_add_p_common(struct add_p_state *state, + const struct pathspec *ps) +{ + size_t binary_count = 0; + size_t i; + + if (parse_diff(state, ps) < 0) + return -1; + + for (i = 0; i < state->file_diff_nr;) { + if (state->file_diff[i].binary && !state->file_diff[i].hunk_nr) { + binary_count++; + i++; + continue; + } + if ((i = patch_update_file(state, i)) == state->file_diff_nr) + break; + } + + if (!state->cfg.auto_advance) + for (i = 0; i < state->file_diff_nr; i++) + apply_patch(state, state->file_diff + i); + + if (state->file_diff_nr == 0) + err(state, _("No changes.")); + else if (binary_count == state->file_diff_nr) + err(state, _("Only binary files changed.")); + + return 0; +} + int run_add_p(struct repository *r, enum add_p_mode mode, struct interactive_options *opts, const char *revision, const struct pathspec *ps) { struct add_p_state s = { .r = r, + .index = r->index, + .index_file = r->index_file, .answer = STRBUF_INIT, .buf = STRBUF_INIT, .plain = STRBUF_INIT, .colored = STRBUF_INIT, }; - size_t i, binary_count = 0; + int ret; interactive_config_init(&s.cfg, r, opts); @@ -2040,30 +2079,90 @@ int run_add_p(struct repository *r, enum add_p_mode mode, if (repo_read_index(r) < 0 || (!s.mode->index_only && repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1, - NULL, NULL, NULL) < 0) || - parse_diff(&s, ps) < 0) { - add_p_state_clear(&s); - return -1; + NULL, NULL, NULL) < 0)) { + ret = -1; + goto out; } - for (i = 0; i < s.file_diff_nr;) { - if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) { - binary_count++; - i++; - continue; - } - if ((i = patch_update_file(&s, i)) == s.file_diff_nr) - break; - } - if (!s.cfg.auto_advance) - for (i = 0; i < s.file_diff_nr; i++) - apply_patch(&s, s.file_diff + i); + ret = run_add_p_common(&s, ps); + if (ret < 0) + goto out; - if (s.file_diff_nr == 0) - err(&s, _("No changes.")); - else if (binary_count == s.file_diff_nr) - err(&s, _("Only binary files changed.")); + ret = 0; +out: add_p_state_clear(&s); - return 0; + return ret; +} + +int run_add_p_index(struct repository *r, + struct index_state *index, + const char *index_file, + struct interactive_options *opts, + const char *revision, + const struct pathspec *ps) +{ + struct patch_mode mode = { + .apply_args = { "--cached", NULL }, + .apply_check_args = { "--cached", NULL }, + .prompt_mode = { + N_("Stage mode change [y,n,q,a,d%s,?]? "), + N_("Stage deletion [y,n,q,a,d%s,?]? "), + N_("Stage addition [y,n,q,a,d%s,?]? "), + N_("Stage this hunk [y,n,q,a,d%s,?]? ") + }, + .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " + "will immediately be marked for staging."), + .help_patch_text = + N_("y - stage this hunk\n" + "n - do not stage this hunk\n" + "q - quit; do not stage this hunk or any of the remaining " + "ones\n" + "a - stage this hunk and all later hunks in the file\n" + "d - do not stage this hunk or any of the later hunks in " + "the file\n"), + .index_only = 1, + }; + struct add_p_state s = { + .r = r, + .index = index, + .index_file = index_file, + .answer = STRBUF_INIT, + .buf = STRBUF_INIT, + .plain = STRBUF_INIT, + .colored = STRBUF_INIT, + .mode = &mode, + .revision = revision, + }; + char parent_tree_oid[GIT_MAX_HEXSZ + 1]; + struct commit *commit; + int ret; + + interactive_config_init(&s.cfg, r, opts); + + commit = lookup_commit_reference_by_name(revision); + if (!commit) { + err(&s, _("Revision does not refer to a commit")); + ret = -1; + goto out; + } + + if (commit->parents) + oid_to_hex_r(parent_tree_oid, get_commit_tree_oid(commit->parents->item)); + else + oid_to_hex_r(parent_tree_oid, r->hash_algo->empty_tree); + + mode.diff_cmd[0] = "diff-tree"; + mode.diff_cmd[1] = "-r"; + mode.diff_cmd[2] = parent_tree_oid; + + ret = run_add_p_common(&s, ps); + if (ret < 0) + goto out; + + ret = 0; + +out: + add_p_state_clear(&s); + return ret; } diff --git a/add-patch.h b/add-patch.h index e6868c60a2..cf2a31a40f 100644 --- a/add-patch.h +++ b/add-patch.h @@ -3,6 +3,7 @@ #include "color.h" +struct index_state; struct pathspec; struct repository; @@ -56,4 +57,11 @@ int run_add_p(struct repository *r, enum add_p_mode mode, struct interactive_options *opts, const char *revision, const struct pathspec *ps); +int run_add_p_index(struct repository *r, + struct index_state *index, + const char *index_file, + struct interactive_options *opts, + const char *revision, + const struct pathspec *ps); + #endif