files-backend: delete dead code in files_init_db()
[gitweb.git] / sequencer.c
index 43ced8db31adbce1b92b4f438319c1993ba4b43e..1f729b053bbc6f544fe0b3e8736113082894b24d 100644 (file)
@@ -46,6 +46,16 @@ static GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
  * actions.
  */
 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");
+/*
+ * 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");
 /*
  * The commit message that is planned to be used for any changes that
  * need to be committed following a user interaction.
@@ -113,6 +123,9 @@ static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
 static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose")
 static GIT_PATH_FUNC(rebase_path_head_name, "rebase-merge/head-name")
 static GIT_PATH_FUNC(rebase_path_onto, "rebase-merge/onto")
+static GIT_PATH_FUNC(rebase_path_autostash, "rebase-merge/autostash")
+static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
+static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
 
 static inline int is_rebase_i(const struct replay_opts *opts)
 {
@@ -430,6 +443,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        o.ancestor = base ? base_label : "(empty tree)";
        o.branch1 = "HEAD";
        o.branch2 = next ? next_label : "(empty tree)";
+       if (is_rebase_i(opts))
+               o.buffer_output = 2;
 
        head_tree = parse_tree_indirect(head);
        next_tree = next ? next->tree : empty_tree();
@@ -441,6 +456,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        clean = merge_trees(&o,
                            head_tree,
                            next_tree, base_tree, &result);
+       if (is_rebase_i(opts) && clean <= 0)
+               fputs(o.obuf.buf, stdout);
        strbuf_release(&o.obuf);
        if (clean < 0)
                return clean;
@@ -541,18 +558,17 @@ static int write_author_script(const char *message)
 }
 
 /*
- * Read the author-script file into an environment block, ready for use in
- * run_command(), that can be free()d afterwards.
+ * Read a list of environment variable assignments (such as the author-script
+ * file) into an environment block. Returns -1 on error, 0 otherwise.
  */
-static char **read_author_script(void)
+static int read_env_script(struct argv_array *env)
 {
        struct strbuf script = STRBUF_INIT;
        int i, count = 0;
-       char *p, *p2, **env;
-       size_t env_size;
+       char *p, *p2;
 
        if (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0)
-               return NULL;
+               return -1;
 
        for (p = script.buf; *p; p++)
                if (skip_prefix(p, "'\\\\''", (const char **)&p2))
@@ -564,19 +580,12 @@ static char **read_author_script(void)
                        count++;
                }
 
-       env_size = (count + 1) * sizeof(*env);
-       strbuf_grow(&script, env_size);
-       memmove(script.buf + env_size, script.buf, script.len);
-       p = script.buf + env_size;
-       env = (char **)strbuf_detach(&script, NULL);
-
-       for (i = 0; i < count; i++) {
-               env[i] = p;
+       for (i = 0, p = script.buf; i < count; i++) {
+               argv_array_push(env, p);
                p += strlen(p) + 1;
        }
-       env[count] = NULL;
 
-       return env;
+       return 0;
 }
 
 static const char staged_changes_advice[] =
@@ -609,14 +618,18 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
                          int allow_empty, int edit, int amend,
                          int cleanup_commit_message)
 {
-       char **env = NULL;
-       struct argv_array array;
-       int rc;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        const char *value;
 
+       cmd.git_cmd = 1;
+
        if (is_rebase_i(opts)) {
-               env = read_author_script();
-               if (!env) {
+               if (!edit) {
+                       cmd.stdout_to_stderr = 1;
+                       cmd.err = -1;
+               }
+
+               if (read_env_script(&cmd.env_array)) {
                        const char *gpg_opt = gpg_sign_opt_quoted(opts);
 
                        return error(_(staged_changes_advice),
@@ -624,39 +637,47 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
                }
        }
 
-       argv_array_init(&array);
-       argv_array_push(&array, "commit");
-       argv_array_push(&array, "-n");
+       argv_array_push(&cmd.args, "commit");
+       argv_array_push(&cmd.args, "-n");
 
        if (amend)
-               argv_array_push(&array, "--amend");
+               argv_array_push(&cmd.args, "--amend");
        if (opts->gpg_sign)
-               argv_array_pushf(&array, "-S%s", opts->gpg_sign);
+               argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
        if (opts->signoff)
-               argv_array_push(&array, "-s");
+               argv_array_push(&cmd.args, "-s");
        if (defmsg)
-               argv_array_pushl(&array, "-F", defmsg, NULL);
+               argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
        if (cleanup_commit_message)
-               argv_array_push(&array, "--cleanup=strip");
+               argv_array_push(&cmd.args, "--cleanup=strip");
        if (edit)
-               argv_array_push(&array, "-e");
+               argv_array_push(&cmd.args, "-e");
        else if (!cleanup_commit_message &&
                 !opts->signoff && !opts->record_origin &&
                 git_config_get_value("commit.cleanup", &value))
-               argv_array_push(&array, "--cleanup=verbatim");
+               argv_array_push(&cmd.args, "--cleanup=verbatim");
 
        if (allow_empty)
-               argv_array_push(&array, "--allow-empty");
+               argv_array_push(&cmd.args, "--allow-empty");
 
        if (opts->allow_empty_message)
-               argv_array_push(&array, "--allow-empty-message");
+               argv_array_push(&cmd.args, "--allow-empty-message");
 
-       rc = run_command_v_opt_cd_env(array.argv, RUN_GIT_CMD, NULL,
-                       (const char *const *)env);
-       argv_array_clear(&array);
-       free(env);
+       if (cmd.err == -1) {
+               /* hide stderr on success */
+               struct strbuf buf = STRBUF_INIT;
+               int rc = pipe_command(&cmd,
+                                     NULL, 0,
+                                     /* stdout is already redirected */
+                                     NULL, 0,
+                                     &buf, 0);
+               if (rc)
+                       fputs(buf.buf, stderr);
+               strbuf_release(&buf);
+               return rc;
+       }
 
-       return rc;
+       return run_command(&cmd);
 }
 
 static int is_original_commit_empty(struct commit *commit)
@@ -733,7 +754,10 @@ enum todo_command {
        /* commands that do something else than handling a single commit */
        TODO_EXEC,
        /* commands that do nothing but are counted for reporting progress */
-       TODO_NOOP
+       TODO_NOOP,
+       TODO_DROP,
+       /* comments (not counted for reporting progress) */
+       TODO_COMMENT
 };
 
 static struct {
@@ -747,19 +771,21 @@ static struct {
        { 'f', "fixup" },
        { 's', "squash" },
        { 'x', "exec" },
-       { 0,   "noop" }
+       { 0,   "noop" },
+       { 'd', "drop" },
+       { 0,   NULL }
 };
 
 static const char *command_to_string(const enum todo_command command)
 {
-       if ((size_t)command < ARRAY_SIZE(todo_command_info))
+       if (command < TODO_COMMENT)
                return todo_command_info[command].str;
        die("Unknown command: %d", command);
 }
 
 static int is_noop(const enum todo_command command)
 {
-       return TODO_NOOP <= (size_t)command;
+       return TODO_NOOP <= command;
 }
 
 static int is_fixup(enum todo_command command)
@@ -1165,6 +1191,7 @@ struct todo_list {
        struct strbuf buf;
        struct todo_item *items;
        int nr, alloc, current;
+       int done_nr, total_nr;
 };
 
 #define TODO_LIST_INIT { STRBUF_INIT }
@@ -1193,14 +1220,14 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
        bol += strspn(bol, " \t");
 
        if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
-               item->command = TODO_NOOP;
+               item->command = TODO_COMMENT;
                item->commit = NULL;
                item->arg = bol;
                item->arg_len = eol - bol;
                return 0;
        }
 
-       for (i = 0; i < ARRAY_SIZE(todo_command_info); i++)
+       for (i = 0; i < TODO_COMMENT; i++)
                if (skip_prefix(bol, todo_command_info[i].str, &bol)) {
                        item->command = i;
                        break;
@@ -1209,7 +1236,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
                        item->command = i;
                        break;
                }
-       if (i >= ARRAY_SIZE(todo_command_info))
+       if (i >= TODO_COMMENT)
                return -1;
 
        if (item->command == TODO_NOOP) {
@@ -1281,6 +1308,17 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
        return res;
 }
 
+static int count_commands(struct todo_list *todo_list)
+{
+       int count = 0, i;
+
+       for (i = 0; i < todo_list->nr; i++)
+               if (todo_list->items[i].command != TODO_COMMENT)
+                       count++;
+
+       return count;
+}
+
 static int read_populate_todo(struct todo_list *todo_list,
                        struct replay_opts *opts)
 {
@@ -1298,8 +1336,12 @@ static int read_populate_todo(struct todo_list *todo_list,
        close(fd);
 
        res = parse_insn_buffer(todo_list->buf.buf, todo_list);
-       if (res)
+       if (res) {
+               if (is_rebase_i(opts))
+                       return error(_("please fix this using "
+                                      "'git rebase --edit-todo'."));
                return error(_("unusable instruction sheet: '%s'"), todo_file);
+       }
 
        if (!todo_list->nr &&
            (!is_rebase_i(opts) || !file_exists(rebase_path_done())))
@@ -1319,6 +1361,26 @@ static int read_populate_todo(struct todo_list *todo_list,
                                return error(_("cannot revert during a cherry-pick."));
        }
 
+       if (is_rebase_i(opts)) {
+               struct todo_list done = TODO_LIST_INIT;
+               FILE *f = fopen(rebase_path_msgtotal(), "w");
+
+               if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
+                               !parse_insn_buffer(done.buf.buf, &done))
+                       todo_list->done_nr = count_commands(&done);
+               else
+                       todo_list->done_nr = 0;
+
+               todo_list->total_nr = todo_list->done_nr
+                       + count_commands(todo_list);
+               todo_list_release(&done);
+
+               if (f) {
+                       fprintf(f, "%d\n", todo_list->total_nr);
+                       fclose(f);
+               }
+       }
+
        return 0;
 }
 
@@ -1367,6 +1429,26 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
        return 0;
 }
 
+static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
+{
+       int i;
+
+       strbuf_reset(buf);
+       if (!read_oneliner(buf, rebase_path_strategy(), 0))
+               return;
+       opts->strategy = strbuf_detach(buf, NULL);
+       if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
+               return;
+
+       opts->xopts_nr = split_cmdline(buf->buf, (const char ***)&opts->xopts);
+       for (i = 0; i < opts->xopts_nr; i++) {
+               const char *arg = opts->xopts[i];
+
+               skip_prefix(arg, "--", &arg);
+               opts->xopts[i] = xstrdup(arg);
+       }
+}
+
 static int read_populate_opts(struct replay_opts *opts)
 {
        if (is_rebase_i(opts)) {
@@ -1380,11 +1462,13 @@ static int read_populate_opts(struct replay_opts *opts)
                                opts->gpg_sign = xstrdup(buf.buf + 2);
                        }
                }
-               strbuf_release(&buf);
 
                if (file_exists(rebase_path_verbose()))
                        opts->verbose = 1;
 
+               read_strategy_opts(opts, &buf);
+               strbuf_release(&buf);
+
                return 0;
        }
 
@@ -1801,6 +1885,47 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
        return -1;
 }
 
+static int apply_autostash(struct replay_opts *opts)
+{
+       struct strbuf stash_sha1 = STRBUF_INIT;
+       struct child_process child = CHILD_PROCESS_INIT;
+       int ret = 0;
+
+       if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {
+               strbuf_release(&stash_sha1);
+               return 0;
+       }
+       strbuf_trim(&stash_sha1);
+
+       child.git_cmd = 1;
+       argv_array_push(&child.args, "stash");
+       argv_array_push(&child.args, "apply");
+       argv_array_push(&child.args, stash_sha1.buf);
+       if (!run_command(&child))
+               printf(_("Applied autostash."));
+       else {
+               struct child_process store = CHILD_PROCESS_INIT;
+
+               store.git_cmd = 1;
+               argv_array_push(&store.args, "stash");
+               argv_array_push(&store.args, "store");
+               argv_array_push(&store.args, "-m");
+               argv_array_push(&store.args, "autostash");
+               argv_array_push(&store.args, "-q");
+               argv_array_push(&store.args, stash_sha1.buf);
+               if (run_command(&store))
+                       ret = error(_("cannot store %s"), stash_sha1.buf);
+               else
+                       printf(_("Applying autostash resulted in conflicts.\n"
+                               "Your changes are safe in the stash.\n"
+                               "You can run \"git stash pop\" or"
+                               " \"git stash drop\" at any time.\n"));
+       }
+
+       strbuf_release(&stash_sha1);
+       return ret;
+}
+
 static const char *reflog_message(struct replay_opts *opts,
        const char *sub_action, const char *fmt, ...)
 {
@@ -1837,6 +1962,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                if (save_todo(todo_list, opts))
                        return -1;
                if (is_rebase_i(opts)) {
+                       if (item->command != TODO_COMMENT) {
+                               FILE *f = fopen(rebase_path_msgnum(), "w");
+
+                               todo_list->done_nr++;
+
+                               if (f) {
+                                       fprintf(f, "%d\n", todo_list->done_nr);
+                                       fclose(f);
+                               }
+                               fprintf(stderr, "Rebasing (%d/%d)%s",
+                                       todo_list->done_nr,
+                                       todo_list->total_nr,
+                                       opts->verbose ? "\n" : "\r");
+                       }
                        unlink(rebase_path_message());
                        unlink(rebase_path_author_script());
                        unlink(rebase_path_stopped_sha());
@@ -1849,6 +1988,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                        1);
                        res = do_pick_commit(item->command, item->commit,
                                        opts, is_final_fixup(todo_list));
+                       if (is_rebase_i(opts) && res < 0) {
+                               /* Reschedule */
+                               todo_list->current--;
+                               if (save_todo(todo_list, opts))
+                                       return -1;
+                       }
                        if (item->command == TODO_EDIT) {
                                struct commit *commit = item->commit;
                                if (!res)
@@ -1980,6 +2125,10 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                run_command(&hook);
                        }
                }
+               apply_autostash(opts);
+
+               fprintf(stderr, "Successfully rebased and updated %s.\n",
+                       head_ref.buf);
 
                strbuf_release(&buf);
                strbuf_release(&head_ref);