mingw: mark t9100's test cases with appropriate prereqs
[gitweb.git] / sequencer.c
index bad5077911667e11e522043ae09875489d4591c4..8c58fa2f4ddaaadb09a63ef5a1937b4282ed988a 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "sequencer.h"
 #include "dir.h"
 #include "object.h"
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
 const char sign_off_header[] = "Signed-off-by: ";
+static const char cherry_picked_prefix[] = "(cherry picked from commit ";
+
+static GIT_PATH_FUNC(git_path_todo_file, SEQ_TODO_FILE)
+static GIT_PATH_FUNC(git_path_opts_file, SEQ_OPTS_FILE)
+static GIT_PATH_FUNC(git_path_seq_dir, SEQ_DIR)
+static GIT_PATH_FUNC(git_path_head_file, SEQ_HEAD_FILE)
+
+static int is_rfc2822_line(const char *buf, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               int ch = buf[i];
+               if (ch == ':')
+                       return 1;
+               if (!isalnum(ch) && ch != '-')
+                       break;
+       }
+
+       return 0;
+}
+
+static int is_cherry_picked_from_line(const char *buf, int len)
+{
+       /*
+        * We only care that it looks roughly like (cherry picked from ...)
+        */
+       return len > strlen(cherry_picked_prefix) + 1 &&
+               starts_with(buf, cherry_picked_prefix) && buf[len - 1] == ')';
+}
+
+/*
+ * Returns 0 for non-conforming footer
+ * Returns 1 for conforming footer
+ * Returns 2 when sob exists within conforming footer
+ * Returns 3 when sob exists within conforming footer as last entry
+ */
+static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
+       int ignore_footer)
+{
+       char prev;
+       int i, k;
+       int len = sb->len - ignore_footer;
+       const char *buf = sb->buf;
+       int found_sob = 0;
+
+       /* footer must end with newline */
+       if (!len || buf[len - 1] != '\n')
+               return 0;
+
+       prev = '\0';
+       for (i = len - 1; i > 0; i--) {
+               char ch = buf[i];
+               if (prev == '\n' && ch == '\n') /* paragraph break */
+                       break;
+               prev = ch;
+       }
+
+       /* require at least one blank line */
+       if (prev != '\n' || buf[i] != '\n')
+               return 0;
+
+       /* advance to start of last paragraph */
+       while (i < len - 1 && buf[i] == '\n')
+               i++;
+
+       for (; i < len; i = k) {
+               int found_rfc2822;
+
+               for (k = i; k < len && buf[k] != '\n'; k++)
+                       ; /* do nothing */
+               k++;
+
+               found_rfc2822 = is_rfc2822_line(buf + i, k - i - 1);
+               if (found_rfc2822 && sob &&
+                   !strncmp(buf + i, sob->buf, sob->len))
+                       found_sob = k;
+
+               if (!(found_rfc2822 ||
+                     is_cherry_picked_from_line(buf + i, k - i - 1)))
+                       return 0;
+       }
+       if (found_sob == i)
+               return 3;
+       if (found_sob)
+               return 2;
+       return 1;
+}
 
 static void remove_sequencer_state(void)
 {
@@ -33,40 +122,24 @@ static const char *action_name(const struct replay_opts *opts)
        return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
 }
 
-static char *get_encoding(const char *message);
-
 struct commit_message {
        char *parent_label;
        const char *label;
        const char *subject;
-       char *reencoded_message;
        const char *message;
 };
 
 static int get_message(struct commit *commit, struct commit_message *out)
 {
-       const char *encoding;
        const char *abbrev, *subject;
        int abbrev_len, subject_len;
        char *q;
 
-       if (!commit->buffer)
-               return -1;
-       encoding = get_encoding(commit->buffer);
-       if (!encoding)
-               encoding = "UTF-8";
        if (!git_commit_encoding)
                git_commit_encoding = "UTF-8";
 
-       out->reencoded_message = NULL;
-       out->message = commit->buffer;
-       if (same_encoding(encoding, git_commit_encoding))
-               out->reencoded_message = reencode_string(commit->buffer,
-                                       git_commit_encoding, encoding);
-       if (out->reencoded_message)
-               out->message = out->reencoded_message;
-
-       abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
+       out->message = logmsg_reencode(commit, NULL, git_commit_encoding);
+       abbrev = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
        abbrev_len = strlen(abbrev);
 
        subject_len = find_commit_subject(out->message, &subject);
@@ -84,46 +157,10 @@ static int get_message(struct commit *commit, struct commit_message *out)
        return 0;
 }
 
-static void free_message(struct commit_message *msg)
+static void free_message(struct commit *commit, struct commit_message *msg)
 {
        free(msg->parent_label);
-       free(msg->reencoded_message);
-}
-
-static char *get_encoding(const char *message)
-{
-       const char *p = message, *eol;
-
-       while (*p && *p != '\n') {
-               for (eol = p + 1; *eol && *eol != '\n'; eol++)
-                       ; /* do nothing */
-               if (!prefixcmp(p, "encoding ")) {
-                       char *result = xmalloc(eol - 8 - p);
-                       strlcpy(result, p + 9, eol - 8 - p);
-                       return result;
-               }
-               p = eol;
-               if (*p == '\n')
-                       p++;
-       }
-       return NULL;
-}
-
-static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
-{
-       const char *filename;
-       int fd;
-       struct strbuf buf = STRBUF_INIT;
-
-       strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
-
-       filename = git_path("%s", pseudoref);
-       fd = open(filename, O_WRONLY | O_CREAT, 0666);
-       if (fd < 0)
-               die_errno(_("Could not open '%s' for writing"), filename);
-       if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
-               die_errno(_("Could not write to '%s'"), filename);
-       strbuf_release(&buf);
+       unuse_commit_buffer(commit, msg->message);
 }
 
 static void print_advice(int show_hint, struct replay_opts *opts)
@@ -137,7 +174,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());
                return;
        }
 
@@ -187,15 +224,52 @@ static int error_dirty_index(struct replay_opts *opts)
 }
 
 static int fast_forward_to(const unsigned char *to, const unsigned char *from,
-                          int unborn)
+                       int unborn, struct replay_opts *opts)
 {
-       struct ref_lock *ref_lock;
+       struct ref_transaction *transaction;
+       struct strbuf sb = STRBUF_INIT;
+       struct strbuf err = STRBUF_INIT;
 
        read_cache();
        if (checkout_fast_forward(from, to, 1))
-               exit(1); /* the callee should have complained already */
-       ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from, 0);
-       return write_ref_sha1(ref_lock, to, "cherry-pick");
+               exit(128); /* the callee should have complained already */
+
+       strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
+
+       transaction = ref_transaction_begin(&err);
+       if (!transaction ||
+           ref_transaction_update(transaction, "HEAD",
+                                  to, unborn ? null_sha1 : from,
+                                  0, sb.buf, &err) ||
+           ref_transaction_commit(transaction, &err)) {
+               ref_transaction_free(transaction);
+               error("%s", err.buf);
+               strbuf_release(&sb);
+               strbuf_release(&err);
+               return -1;
+       }
+
+       strbuf_release(&sb);
+       strbuf_release(&err);
+       ref_transaction_free(transaction);
+       return 0;
+}
+
+void append_conflicts_hint(struct strbuf *msgbuf)
+{
+       int i;
+
+       strbuf_addch(msgbuf, '\n');
+       strbuf_commented_addf(msgbuf, "Conflicts:\n");
+       for (i = 0; i < active_nr;) {
+               const struct cache_entry *ce = active_cache[i++];
+               if (ce_stage(ce)) {
+                       strbuf_commented_addf(msgbuf, "\t%s\n", ce->name);
+                       while (i < active_nr && !strcmp(ce->name,
+                                                       active_cache[i]->name))
+                               i++;
+               }
+       }
 }
 
 static int do_recursive_merge(struct commit *base, struct commit *next,
@@ -205,11 +279,11 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 {
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
-       int clean, index_fd;
+       int clean;
        const char **xopt;
        static struct lock_file index_lock;
 
-       index_fd = hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, 1);
 
        read_cache();
 
@@ -230,30 +304,16 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
                            next_tree, base_tree, &result);
 
        if (active_cache_changed &&
-           (write_cache(index_fd, active_cache, active_nr) ||
-            commit_locked_index(&index_lock)))
+           write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
                die(_("%s: Unable to write new index file"), action_name(opts));
        rollback_lock_file(&index_lock);
 
        if (opts->signoff)
-               append_signoff(msgbuf, 0);
+               append_signoff(msgbuf, 0, 0);
 
-       if (!clean) {
-               int i;
-               strbuf_addstr(msgbuf, "\nConflicts:\n");
-               for (i = 0; i < active_nr;) {
-                       struct cache_entry *ce = active_cache[i++];
-                       if (ce_stage(ce)) {
-                               strbuf_addch(msgbuf, '\t');
-                               strbuf_addstr(msgbuf, ce->name);
-                               strbuf_addch(msgbuf, '\n');
-                               while (i < active_nr && !strcmp(ce->name,
-                                               active_cache[i]->name))
-                                       i++;
-                       }
-               }
-       }
+       if (!clean)
+               append_conflicts_hint(msgbuf);
 
        return !clean;
 }
@@ -263,7 +323,7 @@ static int is_index_unchanged(void)
        unsigned char head_sha1[20];
        struct commit *head_commit;
 
-       if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
                return error(_("Could not resolve HEAD commit\n"));
 
        head_commit = lookup_commit(head_sha1);
@@ -283,11 +343,10 @@ static int is_index_unchanged(void)
                active_cache_tree = cache_tree();
 
        if (!cache_tree_fully_valid(active_cache_tree))
-               if (cache_tree_update(active_cache_tree, active_cache,
-                                 active_nr, 0))
+               if (cache_tree_update(&the_index, 0))
                        return error(_("Unable to update cache tree\n"));
 
-       return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
+       return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash);
 }
 
 /*
@@ -302,16 +361,23 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 {
        struct argv_array array;
        int rc;
+       const char *value;
 
        argv_array_init(&array);
        argv_array_push(&array, "commit");
        argv_array_push(&array, "-n");
 
+       if (opts->gpg_sign)
+               argv_array_pushf(&array, "-S%s", opts->gpg_sign);
        if (opts->signoff)
                argv_array_push(&array, "-s");
        if (!opts->edit) {
                argv_array_push(&array, "-F");
                argv_array_push(&array, defmsg);
+               if (!opts->signoff &&
+                   !opts->record_origin &&
+                   git_config_get_value("commit.cleanup", &value))
+                       argv_array_push(&array, "--cleanup=verbatim");
        }
 
        if (allow_empty)
@@ -331,18 +397,18 @@ static int is_original_commit_empty(struct commit *commit)
 
        if (parse_commit(commit))
                return error(_("Could not parse commit %s\n"),
-                            sha1_to_hex(commit->object.sha1));
+                            oid_to_hex(&commit->object.oid));
        if (commit->parents) {
                struct commit *parent = commit->parents->item;
                if (parse_commit(parent))
                        return error(_("Could not parse parent commit %s\n"),
-                               sha1_to_hex(parent->object.sha1));
-               ptree_sha1 = parent->tree->object.sha1;
+                               oid_to_hex(&parent->object.oid));
+               ptree_sha1 = parent->tree->object.oid.hash;
        } else {
                ptree_sha1 = EMPTY_TREE_SHA1_BIN; /* commit is root */
        }
 
-       return !hashcmp(ptree_sha1, commit->tree->object.sha1);
+       return !hashcmp(ptree_sha1, commit->tree->object.oid.hash);
 }
 
 /*
@@ -388,10 +454,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        unsigned char head[20];
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
-       struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
-       char *defmsg = NULL;
+       struct commit_message msg = { NULL, NULL, NULL, NULL };
        struct strbuf msgbuf = STRBUF_INIT;
-       int res, unborn = 0;
+       int res, unborn = 0, allow;
 
        if (opts->no_commit) {
                /*
@@ -421,7 +486,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
 
                if (!opts->mainline)
                        return error(_("Commit %s is a merge but no -m option was given."),
-                               sha1_to_hex(commit->object.sha1));
+                               oid_to_hex(&commit->object.oid));
 
                for (cnt = 1, p = commit->parents;
                     cnt != opts->mainline && p;
@@ -429,28 +494,28 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                        p = p->next;
                if (cnt != opts->mainline || !p)
                        return error(_("Commit %s does not have parent %d"),
-                               sha1_to_hex(commit->object.sha1), opts->mainline);
+                               oid_to_hex(&commit->object.oid), opts->mainline);
                parent = p->item;
        } else if (0 < opts->mainline)
                return error(_("Mainline was specified but commit %s is not a merge."),
-                       sha1_to_hex(commit->object.sha1));
+                       oid_to_hex(&commit->object.oid));
        else
                parent = commit->parents->item;
 
        if (opts->allow_ff &&
-           ((parent && !hashcmp(parent->object.sha1, head)) ||
+           ((parent && !hashcmp(parent->object.oid.hash, head)) ||
             (!parent && unborn)))
-            return fast_forward_to(commit->object.sha1, head, unborn);
+               return fast_forward_to(commit->object.oid.hash, head, unborn, opts);
 
        if (parent && parse_commit(parent) < 0)
                /* TRANSLATORS: The first %s will be "revert" or
                   "cherry-pick", the second %s a SHA1 */
                return error(_("%s: cannot parse parent commit %s"),
-                       action_name(opts), sha1_to_hex(parent->object.sha1));
+                       action_name(opts), oid_to_hex(&parent->object.oid));
 
        if (get_message(commit, &msg) != 0)
                return error(_("Cannot get commit message for %s"),
-                       sha1_to_hex(commit->object.sha1));
+                       oid_to_hex(&commit->object.oid));
 
        /*
         * "commit" is an existing commit.  We would want to apply
@@ -459,8 +524,6 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
         * reverse of it if we are revert.
         */
 
-       defmsg = git_pathdup("MERGE_MSG");
-
        if (opts->action == REPLAY_REVERT) {
                base = commit;
                base_label = msg.label;
@@ -469,11 +532,11 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                strbuf_addstr(&msgbuf, "Revert \"");
                strbuf_addstr(&msgbuf, msg.subject);
                strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
-               strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+               strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
 
                if (commit->parents && commit->parents->next) {
                        strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
-                       strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
+                       strbuf_addstr(&msgbuf, oid_to_hex(&parent->object.oid));
                }
                strbuf_addstr(&msgbuf, ".\n");
        } else {
@@ -496,8 +559,10 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                }
 
                if (opts->record_origin) {
-                       strbuf_addstr(&msgbuf, "(cherry picked from commit ");
-                       strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+                       if (!has_conforming_footer(&msgbuf, NULL, 0))
+                               strbuf_addch(&msgbuf, '\n');
+                       strbuf_addstr(&msgbuf, cherry_picked_prefix);
+                       strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
                        strbuf_addstr(&msgbuf, ")\n");
                }
        }
@@ -505,12 +570,12 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
                res = do_recursive_merge(base, next, base_label, next_label,
                                         head, &msgbuf, opts);
-               write_message(&msgbuf, defmsg);
+               write_message(&msgbuf, git_path_merge_msg());
        } else {
                struct commit_list *common = NULL;
                struct commit_list *remotes = NULL;
 
-               write_message(&msgbuf, defmsg);
+               write_message(&msgbuf, git_path_merge_msg());
 
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
@@ -527,28 +592,33 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
         * write it at all.
         */
        if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))
-               write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
+               update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.oid.hash, NULL,
+                          REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
        if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))
-               write_cherry_pick_head(commit, "REVERT_HEAD");
+               update_ref(NULL, "REVERT_HEAD", commit->object.oid.hash, NULL,
+                          REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
 
        if (res) {
                error(opts->action == REPLAY_REVERT
                      ? _("could not revert %s... %s")
                      : _("could not apply %s... %s"),
-                     find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
+                     find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV),
                      msg.subject);
                print_advice(res == 1, opts);
                rerere(opts->allow_rerere_auto);
-       } else {
-               int allow = allow_empty(opts, commit);
-               if (allow < 0)
-                       return allow;
-               if (!opts->no_commit)
-                       res = run_git_commit(defmsg, opts, allow);
+               goto leave;
+       }
+
+       allow = allow_empty(opts, commit);
+       if (allow < 0) {
+               res = allow;
+               goto leave;
        }
+       if (!opts->no_commit)
+               res = run_git_commit(git_path_merge_msg(), opts, allow);
 
-       free_message(&msg);
-       free(defmsg);
+leave:
+       free_message(commit, &msg);
 
        return res;
 }
@@ -576,9 +646,8 @@ static void read_and_refresh_cache(struct replay_opts *opts)
        if (read_index_preload(&the_index, NULL) < 0)
                die(_("git %s: failed to read the index"), action_name(opts));
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
-       if (the_index.cache_changed) {
-               if (write_index(&the_index, index_fd) ||
-                   commit_locked_index(&index_lock))
+       if (the_index.cache_changed && index_fd >= 0) {
+               if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
                        die(_("git %s: failed to refresh the index"), action_name(opts));
        }
        rollback_lock_file(&index_lock);
@@ -594,10 +663,12 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
        int subject_len;
 
        for (cur = todo_list; cur; cur = cur->next) {
-               sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
-               subject_len = find_commit_subject(cur->item->buffer, &subject);
+               const char *commit_buffer = get_commit_buffer(cur->item, NULL);
+               sha1_abbrev = find_unique_abbrev(cur->item->object.oid.hash, DEFAULT_ABBREV);
+               subject_len = find_commit_subject(commit_buffer, &subject);
                strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
                        subject_len, subject);
+               unuse_commit_buffer(cur->item, commit_buffer);
        }
        return 0;
 }
@@ -609,10 +680,10 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *
        char *end_of_object_name;
        int saved, status, padding;
 
-       if (!prefixcmp(bol, "pick")) {
+       if (starts_with(bol, "pick")) {
                action = REPLAY_PICK;
                bol += strlen("pick");
-       } else if (!prefixcmp(bol, "revert")) {
+       } else if (starts_with(bol, "revert")) {
                action = REPLAY_REVERT;
                bol += strlen("revert");
        } else
@@ -671,24 +742,23 @@ static int parse_insn_buffer(char *buf, struct commit_list **todo_list,
 static void read_populate_todo(struct commit_list **todo_list,
                        struct replay_opts *opts)
 {
-       const char *todo_file = git_path(SEQ_TODO_FILE);
        struct strbuf buf = STRBUF_INIT;
        int fd, res;
 
-       fd = open(todo_file, O_RDONLY);
+       fd = open(git_path_todo_file(), O_RDONLY);
        if (fd < 0)
-               die_errno(_("Could not open %s"), todo_file);
+               die_errno(_("Could not open %s"), git_path_todo_file());
        if (strbuf_read(&buf, fd, 0) < 0) {
                close(fd);
                strbuf_release(&buf);
-               die(_("Could not read %s."), todo_file);
+               die(_("Could not read %s."), git_path_todo_file());
        }
        close(fd);
 
        res = parse_insn_buffer(buf.buf, todo_list, opts);
        strbuf_release(&buf);
        if (res)
-               die(_("Unusable instruction sheet: %s"), todo_file);
+               die(_("Unusable instruction sheet: %s"), git_path_todo_file());
 }
 
 static int populate_opts_cb(const char *key, const char *value, void *data)
@@ -712,6 +782,8 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
                opts->mainline = git_config_int(key, value);
        else if (!strcmp(key, "options.strategy"))
                git_config_string(&opts->strategy, key, value);
+       else if (!strcmp(key, "options.gpg-sign"))
+               git_config_string(&opts->gpg_sign, key, value);
        else if (!strcmp(key, "options.strategy-option")) {
                ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
                opts->xopts[opts->xopts_nr++] = xstrdup(value);
@@ -726,12 +798,10 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
 
 static void read_populate_opts(struct replay_opts **opts_ptr)
 {
-       const char *opts_file = git_path(SEQ_OPTS_FILE);
-
-       if (!file_exists(opts_file))
+       if (!file_exists(git_path_opts_file()))
                return;
-       if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
-               die(_("Malformed options sheet: %s"), opts_file);
+       if (git_config_from_file(populate_opts_cb, git_path_opts_file(), *opts_ptr) < 0)
+               die(_("Malformed options sheet: %s"), git_path_opts_file());
 }
 
 static void walk_revs_populate_todo(struct commit_list **todo_list,
@@ -749,31 +819,29 @@ static void walk_revs_populate_todo(struct commit_list **todo_list,
 
 static int create_seq_dir(void)
 {
-       const char *seq_dir = git_path(SEQ_DIR);
-
-       if (file_exists(seq_dir)) {
+       if (file_exists(git_path_seq_dir())) {
                error(_("a cherry-pick or revert is already in progress"));
                advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
                return -1;
        }
-       else if (mkdir(seq_dir, 0777) < 0)
-               die_errno(_("Could not create sequencer directory %s"), seq_dir);
+       else if (mkdir(git_path_seq_dir(), 0777) < 0)
+               die_errno(_("Could not create sequencer directory %s"),
+                         git_path_seq_dir());
        return 0;
 }
 
 static void save_head(const char *head)
 {
-       const char *head_file = git_path(SEQ_HEAD_FILE);
        static struct lock_file head_lock;
        struct strbuf buf = STRBUF_INIT;
        int fd;
 
-       fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
+       fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), LOCK_DIE_ON_ERROR);
        strbuf_addf(&buf, "%s\n", head);
        if (write_in_full(fd, buf.buf, buf.len) < 0)
-               die_errno(_("Could not write to %s"), head_file);
+               die_errno(_("Could not write to %s"), git_path_head_file());
        if (commit_lock_file(&head_lock) < 0)
-               die(_("Error wrapping up %s."), head_file);
+               die(_("Error wrapping up %s."), git_path_head_file());
 }
 
 static int reset_for_rollback(const unsigned char *sha1)
@@ -790,10 +858,10 @@ static int rollback_single_pick(void)
 {
        unsigned char head_sha1[20];
 
-       if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
-           !file_exists(git_path("REVERT_HEAD")))
+       if (!file_exists(git_path_cherry_pick_head()) &&
+           !file_exists(git_path_revert_head()))
                return error(_("no cherry-pick or revert in progress"));
-       if (read_ref_full("HEAD", head_sha1, 0, NULL))
+       if (read_ref_full("HEAD", 0, head_sha1, NULL))
                return error(_("cannot resolve HEAD"));
        if (is_null_sha1(head_sha1))
                return error(_("cannot abort from a branch yet to be born"));
@@ -802,13 +870,11 @@ static int rollback_single_pick(void)
 
 static int sequencer_rollback(struct replay_opts *opts)
 {
-       const char *filename;
        FILE *f;
        unsigned char sha1[20];
        struct strbuf buf = STRBUF_INIT;
 
-       filename = git_path(SEQ_HEAD_FILE);
-       f = fopen(filename, "r");
+       f = fopen(git_path_head_file(), "r");
        if (!f && errno == ENOENT) {
                /*
                 * There is no multiple-cherry-pick in progress.
@@ -818,18 +884,18 @@ static int sequencer_rollback(struct replay_opts *opts)
                return rollback_single_pick();
        }
        if (!f)
-               return error(_("cannot open %s: %s"), filename,
+               return error(_("cannot open %s: %s"), git_path_head_file(),
                                                strerror(errno));
        if (strbuf_getline(&buf, f, '\n')) {
-               error(_("cannot read %s: %s"), filename, ferror(f) ?
-                       strerror(errno) : _("unexpected end of file"));
+               error(_("cannot read %s: %s"), git_path_head_file(),
+                     ferror(f) ?  strerror(errno) : _("unexpected end of file"));
                fclose(f);
                goto fail;
        }
        fclose(f);
        if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') {
                error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"),
-                       filename);
+                       git_path_head_file());
                goto fail;
        }
        if (reset_for_rollback(sha1))
@@ -844,28 +910,27 @@ static int sequencer_rollback(struct replay_opts *opts)
 
 static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
 {
-       const char *todo_file = git_path(SEQ_TODO_FILE);
        static struct lock_file todo_lock;
        struct strbuf buf = STRBUF_INIT;
        int fd;
 
-       fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
+       fd = hold_lock_file_for_update(&todo_lock, git_path_todo_file(), LOCK_DIE_ON_ERROR);
        if (format_todo(&buf, todo_list, opts) < 0)
-               die(_("Could not format %s."), todo_file);
+               die(_("Could not format %s."), git_path_todo_file());
        if (write_in_full(fd, buf.buf, buf.len) < 0) {
                strbuf_release(&buf);
-               die_errno(_("Could not write to %s"), todo_file);
+               die_errno(_("Could not write to %s"), git_path_todo_file());
        }
        if (commit_lock_file(&todo_lock) < 0) {
                strbuf_release(&buf);
-               die(_("Error wrapping up %s."), todo_file);
+               die(_("Error wrapping up %s."), git_path_todo_file());
        }
        strbuf_release(&buf);
 }
 
 static void save_opts(struct replay_opts *opts)
 {
-       const char *opts_file = git_path(SEQ_OPTS_FILE);
+       const char *opts_file = git_path_opts_file();
 
        if (opts->no_commit)
                git_config_set_in_file(opts_file, "options.no-commit", "true");
@@ -885,6 +950,8 @@ static void save_opts(struct replay_opts *opts)
        }
        if (opts->strategy)
                git_config_set_in_file(opts_file, "options.strategy", opts->strategy);
+       if (opts->gpg_sign)
+               git_config_set_in_file(opts_file, "options.gpg-sign", opts->gpg_sign);
        if (opts->xopts) {
                int i;
                for (i = 0; i < opts->xopts_nr; i++)
@@ -924,8 +991,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()) &&
+           !file_exists(git_path_revert_head()))
                return error(_("no cherry-pick or revert in progress"));
        return run_command_v_opt(argv, RUN_GIT_CMD);
 }
@@ -934,14 +1001,14 @@ static int sequencer_continue(struct replay_opts *opts)
 {
        struct commit_list *todo_list = NULL;
 
-       if (!file_exists(git_path(SEQ_TODO_FILE)))
+       if (!file_exists(git_path_todo_file()))
                return continue_single_pick();
        read_populate_opts(&opts);
        read_populate_todo(&todo_list, 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()) ||
+           file_exists(git_path_revert_head())) {
                int ret = continue_single_pick();
                if (ret)
                        return ret;
@@ -962,6 +1029,7 @@ int sequencer_pick_revisions(struct replay_opts *opts)
 {
        struct commit_list *todo_list = NULL;
        unsigned char sha1[20];
+       int i;
 
        if (opts->subcommand == REPLAY_NONE)
                assert(opts->revs);
@@ -982,6 +1050,23 @@ int sequencer_pick_revisions(struct replay_opts *opts)
        if (opts->subcommand == REPLAY_CONTINUE)
                return sequencer_continue(opts);
 
+       for (i = 0; i < opts->revs->pending.nr; i++) {
+               unsigned char sha1[20];
+               const char *name = opts->revs->pending.objects[i].name;
+
+               /* This happens when using --stdin. */
+               if (!strlen(name))
+                       continue;
+
+               if (!get_sha1(name, sha1)) {
+                       if (!lookup_commit_reference_gently(sha1, 1)) {
+                               enum object_type type = sha1_object_info(sha1, NULL);
+                               die(_("%s: can't cherry-pick a %s"), name, typename(type));
+                       }
+               } else
+                       die(_("%s: bad revision"), name);
+       }
+
        /*
         * If we were called as "git cherry-pick <commit>", just
         * cherry-pick/revert it, set CHERRY_PICK_HEAD /
@@ -1021,62 +1106,67 @@ int sequencer_pick_revisions(struct replay_opts *opts)
        return pick_commits(todo_list, opts);
 }
 
-static int ends_rfc2822_footer(struct strbuf *sb, int ignore_footer)
-{
-       int ch;
-       int hit = 0;
-       int i, j, k;
-       int len = sb->len - ignore_footer;
-       int first = 1;
-       const char *buf = sb->buf;
-
-       for (i = len - 1; i > 0; i--) {
-               if (hit && buf[i] == '\n')
-                       break;
-               hit = (buf[i] == '\n');
-       }
-
-       while (i < len - 1 && buf[i] == '\n')
-               i++;
-
-       for (; i < len; i = k) {
-               for (k = i; k < len && buf[k] != '\n'; k++)
-                       ; /* do nothing */
-               k++;
-
-               if ((buf[k] == ' ' || buf[k] == '\t') && !first)
-                       continue;
-
-               first = 0;
-
-               for (j = 0; i + j < len; j++) {
-                       ch = buf[i + j];
-                       if (ch == ':')
-                               break;
-                       if (isalnum(ch) ||
-                           (ch == '-'))
-                               continue;
-                       return 0;
-               }
-       }
-       return 1;
-}
-
-void append_signoff(struct strbuf *msgbuf, int ignore_footer)
+void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
 {
+       unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
        struct strbuf sob = STRBUF_INIT;
-       int i;
+       int has_footer;
 
        strbuf_addstr(&sob, sign_off_header);
        strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
                                getenv("GIT_COMMITTER_EMAIL")));
        strbuf_addch(&sob, '\n');
-       for (i = msgbuf->len - 1 - ignore_footer; i > 0 && msgbuf->buf[i - 1] != '\n'; i--)
-               ; /* do nothing */
-       if (prefixcmp(msgbuf->buf + i, sob.buf)) {
-               if (!i || !ends_rfc2822_footer(msgbuf, ignore_footer))
-                       strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, "\n", 1);
-               strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, sob.buf, sob.len);
+
+       /*
+        * If the whole message buffer is equal to the sob, pretend that we
+        * found a conforming footer with a matching sob
+        */
+       if (msgbuf->len - ignore_footer == sob.len &&
+           !strncmp(msgbuf->buf, sob.buf, sob.len))
+               has_footer = 3;
+       else
+               has_footer = has_conforming_footer(msgbuf, &sob, ignore_footer);
+
+       if (!has_footer) {
+               const char *append_newlines = NULL;
+               size_t len = msgbuf->len - ignore_footer;
+
+               if (!len) {
+                       /*
+                        * The buffer is completely empty.  Leave foom for
+                        * the title and body to be filled in by the user.
+                        */
+                       append_newlines = "\n\n";
+               } else if (msgbuf->buf[len - 1] != '\n') {
+                       /*
+                        * Incomplete line.  Complete the line and add a
+                        * blank one so that there is an empty line between
+                        * the message body and the sob.
+                        */
+                       append_newlines = "\n\n";
+               } else if (len == 1) {
+                       /*
+                        * Buffer contains a single newline.  Add another
+                        * so that we leave room for the title and body.
+                        */
+                       append_newlines = "\n";
+               } else if (msgbuf->buf[len - 2] != '\n') {
+                       /*
+                        * Buffer ends with a single newline.  Add another
+                        * so that there is an empty line between the message
+                        * body and the sob.
+                        */
+                       append_newlines = "\n";
+               } /* else, the buffer already ends with two newlines. */
+
+               if (append_newlines)
+                       strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
+                               append_newlines, strlen(append_newlines));
        }
+
+       if (has_footer != 3 && (!no_dup_sob || has_footer != 2))
+               strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
+                               sob.buf, sob.len);
+
        strbuf_release(&sob);
 }