]> git.ipfire.org Git - thirdparty/git.git/blobdiff - sequencer.c
Merge branch 'jt/t5500-unflake'
[thirdparty/git.git] / sequencer.c
index f30bb73c703a2b872dec85de7c68f6aa6c0b075d..fd7701c88a8643c424d4f61fd0cc86fde3097d7d 100644 (file)
@@ -32,6 +32,7 @@
 #include "alias.h"
 #include "commit-reach.h"
 #include "rebase-interactive.h"
+#include "reset.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -419,25 +420,15 @@ static int write_message(const void *buf, size_t len, const char *filename,
        return 0;
 }
 
-/*
- * Reads a file that was presumably written by a shell script, i.e. with an
- * end-of-line marker that needs to be stripped.
- *
- * Note that only the last end-of-line marker is stripped, consistent with the
- * behavior of "$(cat path)" in a shell script.
- *
- * Returns 1 if the file was read, 0 if it could not be read or does not exist.
- */
-static int read_oneliner(struct strbuf *buf,
-       const char *path, int skip_if_empty)
+int read_oneliner(struct strbuf *buf,
+       const char *path, unsigned flags)
 {
        int orig_len = buf->len;
 
-       if (!file_exists(path))
-               return 0;
-
        if (strbuf_read_file(buf, path, 0) < 0) {
-               warning_errno(_("could not read '%s'"), path);
+               if ((flags & READ_ONELINER_WARN_MISSING) ||
+                   (errno != ENOENT && errno != ENOTDIR))
+                       warning_errno(_("could not read '%s'"), path);
                return 0;
        }
 
@@ -447,7 +438,7 @@ static int read_oneliner(struct strbuf *buf,
                buf->buf[buf->len] = '\0';
        }
 
-       if (skip_if_empty && buf->len == orig_len)
+       if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && buf->len == orig_len)
                return 0;
 
        return 1;
@@ -2504,8 +2495,10 @@ static int read_populate_opts(struct replay_opts *opts)
 {
        if (is_rebase_i(opts)) {
                struct strbuf buf = STRBUF_INIT;
+               int ret = 0;
 
-               if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
+               if (read_oneliner(&buf, rebase_path_gpg_sign_opt(),
+                                 READ_ONELINER_SKIP_IF_EMPTY)) {
                        if (!starts_with(buf.buf, "-S"))
                                strbuf_reset(&buf);
                        else {
@@ -2515,7 +2508,8 @@ static int read_populate_opts(struct replay_opts *opts)
                        strbuf_reset(&buf);
                }
 
-               if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
+               if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(),
+                                 READ_ONELINER_SKIP_IF_EMPTY)) {
                        if (!strcmp(buf.buf, "--rerere-autoupdate"))
                                opts->allow_rerere_auto = RERERE_AUTOUPDATE;
                        else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
@@ -2544,10 +2538,11 @@ static int read_populate_opts(struct replay_opts *opts)
                        opts->keep_redundant_commits = 1;
 
                read_strategy_opts(opts, &buf);
-               strbuf_release(&buf);
+               strbuf_reset(&buf);
 
                if (read_oneliner(&opts->current_fixups,
-                                 rebase_path_current_fixups(), 1)) {
+                                 rebase_path_current_fixups(),
+                                 READ_ONELINER_SKIP_IF_EMPTY)) {
                        const char *p = opts->current_fixups.buf;
                        opts->current_fixup_count = 1;
                        while ((p = strchr(p, '\n'))) {
@@ -2557,12 +2552,16 @@ static int read_populate_opts(struct replay_opts *opts)
                }
 
                if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
-                       if (get_oid_hex(buf.buf, &opts->squash_onto) < 0)
-                               return error(_("unusable squash-onto"));
+                       if (get_oid_hex(buf.buf, &opts->squash_onto) < 0) {
+                               ret = error(_("unusable squash-onto"));
+                               goto done_rebase_i;
+                       }
                        opts->have_squash_onto = 1;
                }
 
-               return 0;
+done_rebase_i:
+               strbuf_release(&buf);
+               return ret;
        }
 
        if (!file_exists(git_path_opts_file()))
@@ -3677,25 +3676,71 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
        return -1;
 }
 
-static int apply_autostash(struct replay_opts *opts)
+void create_autostash(struct repository *r, const char *path,
+                     const char *default_reflog_action)
+{
+       struct strbuf buf = STRBUF_INIT;
+       struct lock_file lock_file = LOCK_INIT;
+       int fd;
+
+       fd = repo_hold_locked_index(r, &lock_file, 0);
+       refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
+       if (0 <= fd)
+               repo_update_index_if_able(r, &lock_file);
+       rollback_lock_file(&lock_file);
+
+       if (has_unstaged_changes(r, 1) ||
+           has_uncommitted_changes(r, 1)) {
+               struct child_process stash = CHILD_PROCESS_INIT;
+               struct object_id oid;
+
+               argv_array_pushl(&stash.args,
+                                "stash", "create", "autostash", NULL);
+               stash.git_cmd = 1;
+               stash.no_stdin = 1;
+               strbuf_reset(&buf);
+               if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+                       die(_("Cannot autostash"));
+               strbuf_trim_trailing_newline(&buf);
+               if (get_oid(buf.buf, &oid))
+                       die(_("Unexpected stash response: '%s'"),
+                           buf.buf);
+               strbuf_reset(&buf);
+               strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+               if (safe_create_leading_directories_const(path))
+                       die(_("Could not create directory for '%s'"),
+                           path);
+               write_file(path, "%s", oid_to_hex(&oid));
+               printf(_("Created autostash: %s\n"), buf.buf);
+               if (reset_head(r, NULL, "reset --hard",
+                              NULL, RESET_HEAD_HARD, NULL, NULL,
+                              default_reflog_action) < 0)
+                       die(_("could not reset --hard"));
+
+               if (discard_index(r->index) < 0 ||
+                       repo_read_index(r) < 0)
+                       die(_("could not read index"));
+       }
+       strbuf_release(&buf);
+}
+
+static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
 {
-       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;
+       if (attempt_apply) {
+               child.git_cmd = 1;
+               child.no_stdout = 1;
+               child.no_stderr = 1;
+               argv_array_push(&child.args, "stash");
+               argv_array_push(&child.args, "apply");
+               argv_array_push(&child.args, stash_oid);
+               ret = run_command(&child);
        }
-       strbuf_trim(&stash_sha1);
 
-       child.git_cmd = 1;
-       child.no_stdout = 1;
-       child.no_stderr = 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))
+       if (attempt_apply && !ret)
                fprintf(stderr, _("Applied autostash.\n"));
        else {
                struct child_process store = CHILD_PROCESS_INIT;
@@ -3706,21 +3751,57 @@ static int apply_autostash(struct replay_opts *opts)
                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);
+               argv_array_push(&store.args, stash_oid);
                if (run_command(&store))
-                       ret = error(_("cannot store %s"), stash_sha1.buf);
+                       ret = error(_("cannot store %s"), stash_oid);
                else
                        fprintf(stderr,
-                               _("Applying autostash resulted in conflicts.\n"
+                               _("%s\n"
                                  "Your changes are safe in the stash.\n"
                                  "You can run \"git stash pop\" or"
-                                 " \"git stash drop\" at any time.\n"));
+                                 " \"git stash drop\" at any time.\n"),
+                               attempt_apply ?
+                               _("Applying autostash resulted in conflicts.") :
+                               _("Autostash exists; creating a new stash entry."));
+       }
+
+       return ret;
+}
+
+static int apply_save_autostash(const char *path, int attempt_apply)
+{
+       struct strbuf stash_oid = STRBUF_INIT;
+       int ret = 0;
+
+       if (!read_oneliner(&stash_oid, path,
+                          READ_ONELINER_SKIP_IF_EMPTY)) {
+               strbuf_release(&stash_oid);
+               return 0;
        }
+       strbuf_trim(&stash_oid);
+
+       ret = apply_save_autostash_oid(stash_oid.buf, attempt_apply);
 
-       strbuf_release(&stash_sha1);
+       unlink(path);
+       strbuf_release(&stash_oid);
        return ret;
 }
 
+int save_autostash(const char *path)
+{
+       return apply_save_autostash(path, 0);
+}
+
+int apply_autostash(const char *path)
+{
+       return apply_save_autostash(path, 1);
+}
+
+int apply_autostash_oid(const char *stash_oid)
+{
+       return apply_save_autostash_oid(stash_oid, 1);
+}
+
 static const char *reflog_message(struct replay_opts *opts,
        const char *sub_action, const char *fmt, ...)
 {
@@ -3776,7 +3857,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,
                return error(_("%s: not a valid OID"), orig_head);
 
        if (run_git_checkout(r, opts, oid_to_hex(onto), action)) {
-               apply_autostash(opts);
+               apply_autostash(rebase_path_autostash());
                sequencer_remove_state(opts);
                return error(_("could not detach HEAD"));
        }
@@ -4095,7 +4176,7 @@ cleanup_head_ref:
                                run_command(&hook);
                        }
                }
-               apply_autostash(opts);
+               apply_autostash(rebase_path_autostash());
 
                if (!opts->quiet) {
                        if (!opts->verbose)
@@ -4313,7 +4394,8 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
                struct strbuf buf = STRBUF_INIT;
                struct object_id oid;
 
-               if (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&
+               if (read_oneliner(&buf, rebase_path_stopped_sha(),
+                                 READ_ONELINER_SKIP_IF_EMPTY) &&
                    !get_oid_committish(buf.buf, &oid))
                        record_in_rewritten(&oid, peek_command(&todo_list, 0));
                strbuf_release(&buf);
@@ -5118,7 +5200,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
                todo_list_add_exec_commands(todo_list, commands);
 
        if (count_commands(todo_list) == 0) {
-               apply_autostash(opts);
+               apply_autostash(rebase_path_autostash());
                sequencer_remove_state(opts);
 
                return error(_("nothing to do"));
@@ -5129,12 +5211,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
        if (res == -1)
                return -1;
        else if (res == -2) {
-               apply_autostash(opts);
+               apply_autostash(rebase_path_autostash());
                sequencer_remove_state(opts);
 
                return -1;
        } else if (res == -3) {
-               apply_autostash(opts);
+               apply_autostash(rebase_path_autostash());
                sequencer_remove_state(opts);
                todo_list_release(&new_todo);
 
@@ -5303,10 +5385,13 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
                        todo_list->items[i].command =
                                starts_with(subject, "fixup!") ?
                                TODO_FIXUP : TODO_SQUASH;
-                       if (next[i2] < 0)
+                       if (tail[i2] < 0) {
+                               next[i] = next[i2];
                                next[i2] = i;
-                       else
+                       } else {
+                               next[i] = next[tail[i2]];
                                next[tail[i2]] = i;
+                       }
                        tail[i2] = i;
                } else if (!hashmap_get_from_hash(&subject2item,
                                                strhash(subject), subject)) {