]> git.ipfire.org Git - thirdparty/git.git/commitdiff
add-patch: add support for in-memory index patching
authorPatrick Steinhardt <ps@pks.im>
Wed, 3 Dec 2025 10:48:36 +0000 (11:48 +0100)
committerJunio C Hamano <gitster@pobox.com>
Wed, 3 Dec 2025 21:49:46 +0000 (13:49 -0800)
With `run_add_p()` callers have the ability to apply changes from a
specific revision to a repository's index. This infra supports several
different modes, like for example applying changes to the index,
working tree or both.

One feature that is missing though is the ability to apply changes to an
in-memory index different from the repository's index. Add a new
function `run_add_p_index()` to plug this gap.

This new function will be used in a subsequent commit.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
add-patch.c
add-patch.h

index 27807381532ddc660ab919bc89ec9373f224bbc0..31d82a3e2209c45fded66f1366f4ef8fbadc21e1 100644 (file)
@@ -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"
@@ -47,7 +49,7 @@ static struct patch_mode patch_mode_add = {
                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,?]? ")
+               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."),
@@ -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;
 
@@ -437,7 +441,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,
@@ -1905,7 +1909,7 @@ soft_increment:
                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);
@@ -1916,27 +1920,54 @@ soft_increment:
                                         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);
+               }
        }
 
        putchar('\n');
        return quit;
 }
 
+static int run_add_p_common(struct add_p_state *state,
+                           const struct pathspec *ps)
+{
+       size_t binary_count = 0;
+
+       if (parse_diff(state, ps) < 0)
+               return -1;
+
+       for (size_t i = 0; i < state->file_diff_nr; i++) {
+               if (state->file_diff[i].binary && !state->file_diff[i].hunk_nr)
+                       binary_count++;
+               else if (patch_update_file(state, state->file_diff + i))
+                       break;
+       }
+
+       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);
 
@@ -1969,23 +2000,93 @@ 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; i++)
-               if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)
-                       binary_count++;
-               else if (patch_update_file(&s, s.file_diff + i))
-                       break;
+       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,
+       };
+       struct strbuf parent_revision = STRBUF_INIT;
+       char parent_tree_oid[GIT_MAX_HEXSZ + 1];
+       struct commit *commit;
+       int ret;
+
+       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);
+
+       strbuf_addf(&parent_revision, "%s~", revision);
+       mode.diff_cmd[0] = "diff-tree";
+       mode.diff_cmd[1] = "-r";
+       mode.diff_cmd[2] = parent_tree_oid;
+
+       interactive_config_init(&s.cfg, r, opts);
+
+       ret = run_add_p_common(&s, ps);
+       if (ret < 0)
+               goto out;
+
+       ret = 0;
+
+out:
+       strbuf_release(&parent_revision);
+       add_p_state_clear(&s);
+       return ret;
 }
index a4a05d9d145c5a6a82ec029969b75269330e527f..901c42fd7b61afece574154dbac31cd4386d5ed5 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "color.h"
 
+struct index_state;
 struct pathspec;
 struct repository;
 
@@ -53,4 +54,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