Merge branch 'wc/find-commit-with-pattern-on-detached-head'
[gitweb.git] / sequencer.c
index 4034c0461b5022dad01b25d824cdc4f47ee09d13..b89d5f2e34f960fc8d171ecb4862a65e841221de 100644 (file)
@@ -2,6 +2,7 @@
 #include "config.h"
 #include "lockfile.h"
 #include "dir.h"
+#include "object-store.h"
 #include "object.h"
 #include "commit.h"
 #include "sequencer.h"
@@ -27,6 +28,7 @@
 #include "worktree.h"
 #include "oidmap.h"
 #include "oidset.h"
+#include "commit-slab.h"
 #include "alias.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
@@ -61,12 +63,12 @@ static GIT_PATH_FUNC(rebase_path_done, "rebase-merge/done")
  * The file to keep track of how many commands were already processed (e.g.
  * for the prompt).
  */
-static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum");
+static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum")
 /*
  * The file to keep track of how many commands are to be processed in total
  * (e.g. for the prompt).
  */
-static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end");
+static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end")
 /*
  * The commit message that is planned to be used for any changes that
  * need to be committed following a user interaction.
@@ -175,6 +177,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)
                        warning(_("invalid commit message cleanup mode '%s'"),
                                  s);
 
+               free((char *)s);
                return status;
        }
 
@@ -355,7 +358,7 @@ static void print_advice(int show_hint, struct replay_opts *opts)
                 * (typically rebase --interactive) wants to take care
                 * of the commit itself so remove CHERRY_PICK_HEAD
                 */
-               unlink(git_path_cherry_pick_head());
+               unlink(git_path_cherry_pick_head(the_repository));
                return;
        }
 
@@ -1322,8 +1325,8 @@ static int do_commit(const char *msg_file, const char *author,
                                    &oid);
                strbuf_release(&sb);
                if (!res) {
-                       unlink(git_path_cherry_pick_head());
-                       unlink(git_path_merge_msg());
+                       unlink(git_path_cherry_pick_head(the_repository));
+                       unlink(git_path_merge_msg(the_repository));
                        if (!is_rebase_i(opts))
                                print_commit_summary(NULL, &oid,
                                                SUMMARY_SHOW_AUTHOR_DATE);
@@ -1611,7 +1614,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                struct replay_opts *opts, int final_fixup)
 {
        unsigned int flags = opts->edit ? EDIT_MSG : 0;
-       const char *msg_file = opts->edit ? NULL : git_path_merge_msg();
+       const char *msg_file = opts->edit ? NULL : git_path_merge_msg(the_repository);
        struct object_id head;
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
@@ -1753,12 +1756,12 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                        flags |= CLEANUP_MSG;
                        msg_file = rebase_path_fixup_msg();
                } else {
-                       const char *dest = git_path_squash_msg();
+                       const char *dest = git_path_squash_msg(the_repository);
                        unlink(dest);
                        if (copy_file(dest, rebase_path_squash_msg(), 0666))
                                return error(_("could not rename '%s' to '%s'"),
                                             rebase_path_squash_msg(), dest);
-                       unlink(git_path_merge_msg());
+                       unlink(git_path_merge_msg(the_repository));
                        msg_file = dest;
                        flags |= EDIT_MSG;
                }
@@ -1773,15 +1776,16 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                res = do_recursive_merge(base, next, base_label, next_label,
                                         &head, &msgbuf, opts);
                if (res < 0)
-                       return res;
+                       goto leave;
+
                res |= write_message(msgbuf.buf, msgbuf.len,
-                                    git_path_merge_msg(), 0);
+                                    git_path_merge_msg(the_repository), 0);
        } else {
                struct commit_list *common = NULL;
                struct commit_list *remotes = NULL;
 
                res = write_message(msgbuf.buf, msgbuf.len,
-                                   git_path_merge_msg(), 0);
+                                   git_path_merge_msg(the_repository), 0);
 
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
@@ -1860,8 +1864,6 @@ static int prepare_revs(struct replay_opts *opts)
        if (prepare_revision_walk(opts->revs))
                return error(_("revision walk setup failed"));
 
-       if (!opts->revs->commits)
-               return error(_("empty commit set passed"));
        return 0;
 }
 
@@ -2203,6 +2205,7 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
 static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
 {
        int i;
+       char *strategy_opts_string;
 
        strbuf_reset(buf);
        if (!read_oneliner(buf, rebase_path_strategy(), 0))
@@ -2211,7 +2214,11 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
        if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
                return;
 
-       opts->xopts_nr = split_cmdline(buf->buf, (const char ***)&opts->xopts);
+       strategy_opts_string = buf->buf;
+       if (*strategy_opts_string == ' ')
+               strategy_opts_string++;
+       opts->xopts_nr = split_cmdline(strategy_opts_string,
+                                      (const char ***)&opts->xopts);
        for (i = 0; i < opts->xopts_nr; i++) {
                const char *arg = opts->xopts[i];
 
@@ -2314,6 +2321,10 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
                        short_commit_name(commit), subject_len, subject);
                unuse_commit_buffer(commit, commit_buffer);
        }
+
+       if (!todo_list->nr)
+               return error(_("empty commit set passed"));
+
        return 0;
 }
 
@@ -2391,8 +2402,8 @@ static int rollback_single_pick(void)
 {
        struct object_id head_oid;
 
-       if (!file_exists(git_path_cherry_pick_head()) &&
-           !file_exists(git_path_revert_head()))
+       if (!file_exists(git_path_cherry_pick_head(the_repository)) &&
+           !file_exists(git_path_revert_head(the_repository)))
                return error(_("no cherry-pick or revert in progress"));
        if (read_ref_full("HEAD", 0, &head_oid, NULL))
                return error(_("cannot resolve HEAD"));
@@ -2617,10 +2628,11 @@ static int error_failed_squash(struct commit *commit,
        if (copy_file(rebase_path_message(), rebase_path_squash_msg(), 0666))
                return error(_("could not copy '%s' to '%s'"),
                        rebase_path_squash_msg(), rebase_path_message());
-       unlink(git_path_merge_msg());
-       if (copy_file(git_path_merge_msg(), rebase_path_message(), 0666))
+       unlink(git_path_merge_msg(the_repository));
+       if (copy_file(git_path_merge_msg(the_repository), rebase_path_message(), 0666))
                return error(_("could not copy '%s' to '%s'"),
-                            rebase_path_message(), git_path_merge_msg());
+                            rebase_path_message(),
+                            git_path_merge_msg(the_repository));
        return error_with_patch(commit, subject, subject_len, opts, 1, 0);
 }
 
@@ -2910,11 +2922,11 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                write_author_script(message);
                find_commit_subject(message, &body);
                len = strlen(body);
-               ret = write_message(body, len, git_path_merge_msg(), 0);
+               ret = write_message(body, len, git_path_merge_msg(the_repository), 0);
                unuse_commit_buffer(commit, message);
                if (ret) {
                        error_errno(_("could not write '%s'"),
-                                   git_path_merge_msg());
+                                   git_path_merge_msg(the_repository));
                        goto leave_merge;
                }
        } else {
@@ -2935,11 +2947,11 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                        len = buf.len;
                }
 
-               ret = write_message(p, len, git_path_merge_msg(), 0);
+               ret = write_message(p, len, git_path_merge_msg(the_repository), 0);
                strbuf_release(&buf);
                if (ret) {
                        error_errno(_("could not write '%s'"),
-                                   git_path_merge_msg());
+                                   git_path_merge_msg(the_repository));
                        goto leave_merge;
                }
        }
@@ -2976,8 +2988,8 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
        }
 
        write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ,
-                     git_path_merge_head(), 0);
-       write_message("no-ff", 5, git_path_merge_mode(), 0);
+                     git_path_merge_head(the_repository), 0);
+       write_message("no-ff", 5, git_path_merge_mode(the_repository), 0);
 
        bases = get_merge_bases(head_commit, merge_commit);
        if (bases && !oidcmp(&merge_commit->object.oid,
@@ -3031,7 +3043,7 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                 * value (a negative one would indicate that the `merge`
                 * command needs to be rescheduled).
                 */
-               ret = !!run_git_commit(git_path_merge_msg(), opts,
+               ret = !!run_git_commit(git_path_merge_msg(the_repository), opts,
                                     run_commit_flags);
 
 leave_merge:
@@ -3214,10 +3226,27 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                        intend_to_amend();
                                return error_failed_squash(item->commit, opts,
                                        item->arg_len, item->arg);
-                       } else if (res && is_rebase_i(opts) && item->commit)
+                       } else if (res && is_rebase_i(opts) && item->commit) {
+                               int to_amend = 0;
+                               struct object_id oid;
+
+                               /*
+                                * If we are rewording and have either
+                                * fast-forwarded already, or are about to
+                                * create a new root commit, we want to amend,
+                                * otherwise we do not.
+                                */
+                               if (item->command == TODO_REWORD &&
+                                   !get_oid("HEAD", &oid) &&
+                                   (!oidcmp(&item->commit->object.oid, &oid) ||
+                                    (opts->have_squash_onto &&
+                                     !oidcmp(&opts->squash_onto, &oid))))
+                                       to_amend = 1;
+
                                return res | error_with_patch(item->commit,
-                                       item->arg, item->arg_len, opts, res,
-                                       item->command == TODO_REWORD);
+                                               item->arg, item->arg_len, opts,
+                                               res, to_amend);
+                       }
                } else if (item->command == TODO_EXEC) {
                        char *end_of_arg = (char *)(item->arg + item->arg_len);
                        int saved = *end_of_arg;
@@ -3398,8 +3427,8 @@ static int continue_single_pick(void)
 {
        const char *argv[] = { "commit", NULL };
 
-       if (!file_exists(git_path_cherry_pick_head()) &&
-           !file_exists(git_path_revert_head()))
+       if (!file_exists(git_path_cherry_pick_head(the_repository)) &&
+           !file_exists(git_path_revert_head(the_repository)))
                return error(_("no cherry-pick or revert in progress"));
        return run_command_v_opt(argv, RUN_GIT_CMD);
 }
@@ -3502,7 +3531,7 @@ static int commit_staged_changes(struct replay_opts *opts,
        }
 
        if (is_clean) {
-               const char *cherry_pick_head = git_path_cherry_pick_head();
+               const char *cherry_pick_head = git_path_cherry_pick_head(the_repository);
 
                if (file_exists(cherry_pick_head) && unlink(cherry_pick_head))
                        return error(_("could not remove CHERRY_PICK_HEAD"));
@@ -3552,8 +3581,8 @@ int sequencer_continue(struct replay_opts *opts)
 
        if (!is_rebase_i(opts)) {
                /* Verify that the conflict has been resolved */
-               if (file_exists(git_path_cherry_pick_head()) ||
-                   file_exists(git_path_revert_head())) {
+               if (file_exists(git_path_cherry_pick_head(the_repository)) ||
+                   file_exists(git_path_revert_head(the_repository))) {
                        res = continue_single_pick();
                        if (res)
                                goto release_todo_list;
@@ -3631,8 +3660,10 @@ int sequencer_pick_revisions(struct replay_opts *opts)
                if (prepare_revision_walk(opts->revs))
                        return error(_("revision walk setup failed"));
                cmit = get_revision(opts->revs);
-               if (!cmit || get_revision(opts->revs))
-                       return error("BUG: expected exactly one commit from walk");
+               if (!cmit)
+                       return error(_("empty commit set passed"));
+               if (get_revision(opts->revs))
+                       BUG("unexpected extra commit from walk");
                return single_pick(cmit, opts);
        }
 
@@ -4238,6 +4269,7 @@ static enum check_level get_missing_commit_check_level(void)
        return CHECK_IGNORE;
 }
 
+define_commit_slab(commit_seen, unsigned char);
 /*
  * Check if the user dropped some commits by mistake
  * Behaviour determined by rebase.missingCommitsCheck.
@@ -4251,6 +4283,9 @@ int check_todo_list(void)
        struct todo_list todo_list = TODO_LIST_INIT;
        struct strbuf missing = STRBUF_INIT;
        int advise_to_edit_todo = 0, res = 0, i;
+       struct commit_seen commit_seen;
+
+       init_commit_seen(&commit_seen);
 
        strbuf_addstr(&todo_file, rebase_path_todo());
        if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
@@ -4267,7 +4302,7 @@ int check_todo_list(void)
        for (i = 0; i < todo_list.nr; i++) {
                struct commit *commit = todo_list.items[i].commit;
                if (commit)
-                       commit->util = (void *)1;
+                       *commit_seen_at(&commit_seen, commit) = 1;
        }
 
        todo_list_release(&todo_list);
@@ -4283,11 +4318,11 @@ int check_todo_list(void)
        for (i = todo_list.nr - 1; i >= 0; i--) {
                struct todo_item *item = todo_list.items + i;
                struct commit *commit = item->commit;
-               if (commit && !commit->util) {
+               if (commit && !*commit_seen_at(&commit_seen, commit)) {
                        strbuf_addf(&missing, " - %s %.*s\n",
                                    short_commit_name(commit),
                                    item->arg_len, item->arg);
-                       commit->util = (void *)1;
+                       *commit_seen_at(&commit_seen, commit) = 1;
                }
        }
 
@@ -4313,6 +4348,7 @@ int check_todo_list(void)
                "The possible behaviours are: ignore, warn, error.\n\n"));
 
 leave_check:
+       clear_commit_seen(&commit_seen);
        strbuf_release(&todo_file);
        todo_list_release(&todo_list);
 
@@ -4433,6 +4469,8 @@ static int subject2item_cmp(const void *fndata,
        return key ? strcmp(a->subject, key) : strcmp(a->subject, b->subject);
 }
 
+define_commit_slab(commit_todo_item, struct todo_item *);
+
 /*
  * Rearrange the todo list that has both "pick commit-id msg" and "pick
  * commit-id fixup!/squash! msg" in it so that the latter is put immediately
@@ -4449,6 +4487,7 @@ int rearrange_squash(void)
        struct hashmap subject2item;
        int res = 0, rearranged = 0, *next, *tail, i;
        char **subjects;
+       struct commit_todo_item commit_todo;
 
        if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
                return -1;
@@ -4457,6 +4496,7 @@ int rearrange_squash(void)
                return -1;
        }
 
+       init_commit_todo_item(&commit_todo);
        /*
         * The hashmap maps onelines to the respective todo list index.
         *
@@ -4487,10 +4527,11 @@ int rearrange_squash(void)
 
                if (is_fixup(item->command)) {
                        todo_list_release(&todo_list);
+                       clear_commit_todo_item(&commit_todo);
                        return error(_("the script was already rearranged."));
                }
 
-               item->commit->util = item;
+               *commit_todo_item_at(&commit_todo, item->commit) = item;
 
                parse_commit(item->commit);
                commit_buffer = get_commit_buffer(item->commit, NULL);
@@ -4517,9 +4558,9 @@ int rearrange_squash(void)
                        else if (!strchr(p, ' ') &&
                                 (commit2 =
                                  lookup_commit_reference_by_name(p)) &&
-                                commit2->util)
+                                *commit_todo_item_at(&commit_todo, commit2))
                                /* found by commit name */
-                               i2 = (struct todo_item *)commit2->util
+                               i2 = *commit_todo_item_at(&commit_todo, commit2)
                                        - todo_list.items;
                        else {
                                /* copy can be a prefix of the commit subject */
@@ -4596,5 +4637,6 @@ int rearrange_squash(void)
        hashmap_free(&subject2item, 1);
        todo_list_release(&todo_list);
 
+       clear_commit_todo_item(&commit_todo);
        return res;
 }