]> git.ipfire.org Git - thirdparty/git.git/blobdiff - sequencer.c
reftable: handle null refnames in reftable_ref_record_equal
[thirdparty/git.git] / sequencer.c
index 5c705f2ee0306747a21e3b01870df5f5690e117a..b4135a78c9133da2661384d291d9cd64523e0db1 100644 (file)
@@ -8,6 +8,7 @@
 #include "sequencer.h"
 #include "tag.h"
 #include "run-command.h"
+#include "hook.h"
 #include "exec-cmd.h"
 #include "utf8.h"
 #include "cache-tree.h"
@@ -403,7 +404,7 @@ static void print_advice(struct repository *r, int show_hint,
        char *msg = getenv("GIT_CHERRY_PICK_HELP");
 
        if (msg) {
-               fprintf(stderr, "%s\n", msg);
+               advise("%s\n", msg);
                /*
                 * A conflict has occurred but the porcelain
                 * (typically rebase --interactive) wants to take care
@@ -418,10 +419,22 @@ static void print_advice(struct repository *r, int show_hint,
                if (opts->no_commit)
                        advise(_("after resolving the conflicts, mark the corrected paths\n"
                                 "with 'git add <paths>' or 'git rm <paths>'"));
+               else if (opts->action == REPLAY_PICK)
+                       advise(_("After resolving the conflicts, mark them with\n"
+                                "\"git add/rm <pathspec>\", then run\n"
+                                "\"git cherry-pick --continue\".\n"
+                                "You can instead skip this commit with \"git cherry-pick --skip\".\n"
+                                "To abort and get back to the state before \"git cherry-pick\",\n"
+                                "run \"git cherry-pick --abort\"."));
+               else if (opts->action == REPLAY_REVERT)
+                       advise(_("After resolving the conflicts, mark them with\n"
+                                "\"git add/rm <pathspec>\", then run\n"
+                                "\"git revert --continue\".\n"
+                                "You can instead skip this commit with \"git revert --skip\".\n"
+                                "To abort and get back to the state before \"git revert\",\n"
+                                "run \"git revert --abort\"."));
                else
-                       advise(_("after resolving the conflicts, mark the corrected paths\n"
-                                "with 'git add <paths>' or 'git rm <paths>'\n"
-                                "and commit the result with 'git commit'"));
+                       BUG("unexpected pick action in print_advice()");
        }
 }
 
@@ -486,7 +499,7 @@ static int error_dirty_index(struct repository *repo, struct replay_opts *opts)
        error(_("your local changes would be overwritten by %s."),
                _(action_name(opts)));
 
-       if (advice_commit_before_merge)
+       if (advice_enabled(ADVICE_COMMIT_BEFORE_MERGE))
                advise(_("commit your changes or stash them to proceed."));
        return -1;
 }
@@ -636,7 +649,7 @@ static int do_recursive_merge(struct repository *r,
        for (i = 0; i < opts->xopts_nr; i++)
                parse_merge_opt(&o, opts->xopts[i]);
 
-       if (opts->strategy && !strcmp(opts->strategy, "ort")) {
+       if (!opts->strategy || !strcmp(opts->strategy, "ort")) {
                memset(&result, 0, sizeof(result));
                merge_incore_nonrecursive(&o, base_tree, head_tree, next_tree,
                                            &result);
@@ -652,6 +665,7 @@ static int do_recursive_merge(struct repository *r,
                merge_switch_to_result(&o, head_tree, &result, 1, show_output);
                clean = result.clean;
        } else {
+               ensure_full_index(r->index);
                clean = merge_trees(&o, head_tree, next_tree, base_tree);
                if (is_rebase_i(opts) && clean <= 0)
                        fputs(o.obuf.buf, stdout);
@@ -1244,7 +1258,7 @@ N_("Your name and email address were configured automatically based\n"
 
 static const char *implicit_ident_advice(void)
 {
-       char *user_config = expand_user_path("~/.gitconfig", 0);
+       char *user_config = interpolate_path("~/.gitconfig", 0);
        char *xdg_config = xdg_config_home("config");
        int config_exists = file_exists(user_config) || file_exists(xdg_config);
 
@@ -1270,6 +1284,8 @@ void print_commit_summary(struct repository *r,
        struct pretty_print_context pctx = {0};
        struct strbuf author_ident = STRBUF_INIT;
        struct strbuf committer_ident = STRBUF_INIT;
+       struct ref_store *refs;
+       int resolve_errno;
 
        commit = lookup_commit(r, oid);
        if (!commit)
@@ -1296,7 +1312,7 @@ void print_commit_summary(struct repository *r,
        if (!committer_ident_sufficiently_given()) {
                strbuf_addstr(&format, "\n Committer: ");
                strbuf_addbuf_percentquote(&format, &committer_ident);
-               if (advice_implicit_identity) {
+               if (advice_enabled(ADVICE_IMPLICIT_IDENTITY)) {
                        strbuf_addch(&format, '\n');
                        strbuf_addstr(&format, implicit_ident_advice());
                }
@@ -1319,9 +1335,13 @@ void print_commit_summary(struct repository *r,
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
 
-       head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-       if (!head)
+       refs = get_main_ref_store(the_repository);
+       head = refs_resolve_ref_unsafe(refs, "HEAD", 0, NULL, NULL,
+                                      &resolve_errno);
+       if (!head) {
+               errno = resolve_errno;
                die_errno(_("unable to resolve HEAD after creating commit"));
+       }
        if (!strcmp(head, "HEAD"))
                head = _("detached HEAD");
        else
@@ -1448,7 +1468,7 @@ static int try_to_commit(struct repository *r,
                }
        }
 
-       if (find_hook("prepare-commit-msg")) {
+       if (hook_exists("prepare-commit-msg")) {
                res = run_prepare_commit_msg_hook(r, msg, hook_commit);
                if (res)
                        goto out;
@@ -2349,6 +2369,7 @@ static int read_and_refresh_cache(struct repository *r,
                        _(action_name(opts)));
        }
        refresh_index(r->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
+
        if (index_fd >= 0) {
                if (write_locked_index(r->index, &index_lock,
                                       COMMIT_LOCK | SKIP_IF_UNCHANGED)) {
@@ -2356,6 +2377,13 @@ static int read_and_refresh_cache(struct repository *r,
                                _(action_name(opts)));
                }
        }
+
+       /*
+        * If we are resolving merges in any way other than "ort", then
+        * expand the sparse index.
+        */
+       if (opts->strategy && strcmp(opts->strategy, "ort"))
+               ensure_full_index(r->index);
        return 0;
 }
 
@@ -2674,7 +2702,6 @@ static int read_populate_todo(struct repository *r,
                              struct todo_list *todo_list,
                              struct replay_opts *opts)
 {
-       struct stat st;
        const char *todo_file = get_todo_path(opts);
        int res;
 
@@ -2682,11 +2709,6 @@ static int read_populate_todo(struct repository *r,
        if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0)
                return -1;
 
-       res = stat(todo_file, &st);
-       if (res)
-               return error(_("could not stat '%s'"), todo_file);
-       fill_stat_data(&todo_list->stat, &st);
-
        res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
        if (res) {
                if (is_rebase_i(opts))
@@ -3044,7 +3066,7 @@ static int create_seq_dir(struct repository *r)
        }
        if (in_progress_error) {
                error("%s", in_progress_error);
-               if (advice_sequencer_in_use)
+               if (advice_enabled(ADVICE_SEQUENCER_IN_USE))
                        advise(in_progress_advice,
                                advise_skip ? "--skip | " : "");
                return -1;
@@ -3248,7 +3270,7 @@ int sequencer_skip(struct repository *r, struct replay_opts *opts)
 give_advice:
        error(_("there is nothing to skip"));
 
-       if (advice_resolve_conflict) {
+       if (advice_enabled(ADVICE_RESOLVE_CONFLICT)) {
                advise(_("have you committed already?\n"
                         "try \"git %s --continue\""),
                         action == REPLAY_REVERT ? "revert" : "cherry-pick");
@@ -3631,9 +3653,9 @@ static int do_reset(struct repository *r,
        struct strbuf ref_name = STRBUF_INIT;
        struct object_id oid;
        struct lock_file lock = LOCK_INIT;
-       struct tree_desc desc;
+       struct tree_desc desc = { 0 };
        struct tree *tree;
-       struct unpack_trees_options unpack_tree_opts;
+       struct unpack_trees_options unpack_tree_opts = { 0 };
        int ret = 0;
 
        if (repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0)
@@ -3665,14 +3687,11 @@ static int do_reset(struct repository *r,
                strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
                if (get_oid(ref_name.buf, &oid) &&
                    get_oid(ref_name.buf + strlen("refs/rewritten/"), &oid)) {
-                       error(_("could not read '%s'"), ref_name.buf);
-                       rollback_lock_file(&lock);
-                       strbuf_release(&ref_name);
-                       return -1;
+                       ret = error(_("could not read '%s'"), ref_name.buf);
+                       goto cleanup;
                }
        }
 
-       memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
        setup_unpack_trees_porcelain(&unpack_tree_opts, "reset");
        unpack_tree_opts.head_idx = 1;
        unpack_tree_opts.src_index = r->index;
@@ -3680,27 +3699,22 @@ static int do_reset(struct repository *r,
        unpack_tree_opts.fn = oneway_merge;
        unpack_tree_opts.merge = 1;
        unpack_tree_opts.update = 1;
+       unpack_tree_opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
        init_checkout_metadata(&unpack_tree_opts.meta, name, &oid, NULL);
 
        if (repo_read_index_unmerged(r)) {
-               rollback_lock_file(&lock);
-               strbuf_release(&ref_name);
-               return error_resolve_conflict(_(action_name(opts)));
+               ret = error_resolve_conflict(_(action_name(opts)));
+               goto cleanup;
        }
 
        if (!fill_tree_descriptor(r, &desc, &oid)) {
-               error(_("failed to find tree of %s"), oid_to_hex(&oid));
-               rollback_lock_file(&lock);
-               free((void *)desc.buffer);
-               strbuf_release(&ref_name);
-               return -1;
+               ret = error(_("failed to find tree of %s"), oid_to_hex(&oid));
+               goto cleanup;
        }
 
        if (unpack_trees(1, &desc, &unpack_tree_opts)) {
-               rollback_lock_file(&lock);
-               free((void *)desc.buffer);
-               strbuf_release(&ref_name);
-               return -1;
+               ret = -1;
+               goto cleanup;
        }
 
        tree = parse_tree_indirect(&oid);
@@ -3708,14 +3722,17 @@ static int do_reset(struct repository *r,
 
        if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0)
                ret = error(_("could not write index"));
-       free((void *)desc.buffer);
 
        if (!ret)
                ret = update_ref(reflog_message(opts, "reset", "'%.*s'",
                                                len, name), "HEAD", &oid,
                                 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
-
+cleanup:
+       free((void *)desc.buffer);
+       if (ret < 0)
+               rollback_lock_file(&lock);
        strbuf_release(&ref_name);
+       clear_unpack_trees_porcelain(&unpack_tree_opts);
        return ret;
 }
 
@@ -3992,7 +4009,7 @@ static int do_merge(struct repository *r,
        o.branch2 = ref_name.buf;
        o.buffer_output = 2;
 
-       if (opts->strategy && !strcmp(opts->strategy, "ort")) {
+       if (!opts->strategy || !strcmp(opts->strategy, "ort")) {
                /*
                 * TODO: Should use merge_incore_recursive() and
                 * merge_switch_to_result(), skipping the call to
@@ -4265,6 +4282,30 @@ static int stopped_at_head(struct repository *r)
 
 }
 
+static int reread_todo_if_changed(struct repository *r,
+                                 struct todo_list *todo_list,
+                                 struct replay_opts *opts)
+{
+       int offset;
+       struct strbuf buf = STRBUF_INIT;
+
+       if (strbuf_read_file_or_whine(&buf, get_todo_path(opts)) < 0)
+               return -1;
+       offset = get_item_line_offset(todo_list, todo_list->current + 1);
+       if (buf.len != todo_list->buf.len - offset ||
+           memcmp(buf.buf, todo_list->buf.buf + offset, buf.len)) {
+               /* Reread the todo file if it has changed. */
+               todo_list_release(todo_list);
+               if (read_populate_todo(r, todo_list, opts))
+                       return -1; /* message was printed */
+               /* `current` will be incremented on return */
+               todo_list->current = -1;
+       }
+       strbuf_release(&buf);
+
+       return 0;
+}
+
 static const char rescheduled_advice[] =
 N_("Could not execute the todo command\n"
 "\n"
@@ -4443,20 +4484,9 @@ static int pick_commits(struct repository *r,
                                                        item->commit,
                                                        arg, item->arg_len,
                                                        opts, res, 0);
-               } else if (is_rebase_i(opts) && check_todo && !res) {
-                       struct stat st;
-
-                       if (stat(get_todo_path(opts), &st)) {
-                               res = error_errno(_("could not stat '%s'"),
-                                                 get_todo_path(opts));
-                       } else if (match_stat_data(&todo_list->stat, &st)) {
-                               /* Reread the todo file if it has changed. */
-                               todo_list_release(todo_list);
-                               if (read_populate_todo(r, todo_list, opts))
-                                       res = -1; /* message was printed */
-                               /* `current` will be incremented below */
-                               todo_list->current = -1;
-                       }
+               } else if (is_rebase_i(opts) && check_todo && !res &&
+                          reread_todo_if_changed(r, todo_list, opts)) {
+                       return -1;
                }
 
                todo_list->current++;
@@ -5112,6 +5142,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
        int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
        int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
        int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
+       int skipped_commit = 0;
        struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
        struct strbuf label = STRBUF_INIT;
        struct commit_list *commits = NULL, **tail = &commits, *iter;
@@ -5162,8 +5193,13 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                oidset_insert(&interesting, &commit->object.oid);
 
                is_empty = is_original_commit_empty(commit);
-               if (!is_empty && (commit->object.flags & PATCHSAME))
+               if (!is_empty && (commit->object.flags & PATCHSAME)) {
+                       if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS)
+                               warning(_("skipped previously applied commit %s"),
+                                       short_commit_name(commit));
+                       skipped_commit = 1;
                        continue;
+               }
                if (is_empty && !keep_empty)
                        continue;
 
@@ -5227,6 +5263,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                oidcpy(&entry->entry.oid, &commit->object.oid);
                oidmap_put(&commit2todo, entry);
        }
+       if (skipped_commit)
+               advise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,
+                                 _("use --reapply-cherry-picks to include skipped commits"));
 
        /*
         * Second phase:
@@ -5347,6 +5386,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
        const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
        int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
        int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;
+       int skipped_commit = 0;
 
        repo_init_revisions(r, &revs, NULL);
        revs.verbose_header = 1;
@@ -5382,8 +5422,13 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
        while ((commit = get_revision(&revs))) {
                int is_empty = is_original_commit_empty(commit);
 
-               if (!is_empty && (commit->object.flags & PATCHSAME))
+               if (!is_empty && (commit->object.flags & PATCHSAME)) {
+                       if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS)
+                               warning(_("skipped previously applied commit %s"),
+                                       short_commit_name(commit));
+                       skipped_commit = 1;
                        continue;
+               }
                if (is_empty && !keep_empty)
                        continue;
                strbuf_addf(out, "%s %s ", insn,
@@ -5393,6 +5438,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
                        strbuf_addf(out, " %c empty", comment_line_char);
                strbuf_addch(out, '\n');
        }
+       if (skipped_commit)
+               advise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,
+                                 _("use --reapply-cherry-picks to include skipped commits"));
        return 0;
 }
 
@@ -5400,8 +5448,8 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
  * Add commands after pick and (series of) squash/fixup commands
  * in the todo list.
  */
-void todo_list_add_exec_commands(struct todo_list *todo_list,
-                                struct string_list *commands)
+static void todo_list_add_exec_commands(struct todo_list *todo_list,
+                                       struct string_list *commands)
 {
        struct strbuf *buf = &todo_list->buf;
        size_t base_offset = buf->len;