t4201: make use of abbreviation in the test more robust
[gitweb.git] / builtin / am.c
index 4e396c832139aa3c4ae4e68e9d265b0107817aa9..31fb60578f6caacfb376fe84938dda9432dcfc5a 100644 (file)
@@ -27,6 +27,9 @@
 #include "notes-utils.h"
 #include "rerere.h"
 #include "prompt.h"
+#include "mailinfo.h"
+#include "apply.h"
+#include "string-list.h"
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -44,21 +47,6 @@ static int is_empty_file(const char *filename)
        return !st.st_size;
 }
 
-/**
- * Like strbuf_getline(), but treats both '\n' and "\r\n" as line terminators.
- */
-static int strbuf_getline_crlf(struct strbuf *sb, FILE *fp)
-{
-       if (strbuf_getwholeline(sb, fp, '\n'))
-               return EOF;
-       if (sb->buf[sb->len - 1] == '\n') {
-               strbuf_setlen(sb, sb->len - 1);
-               if (sb->len > 0 && sb->buf[sb->len - 1] == '\r')
-                       strbuf_setlen(sb, sb->len - 1);
-       }
-       return 0;
-}
-
 /**
  * Returns the length of the first line of msg.
  */
@@ -84,7 +72,8 @@ enum patch_format {
        PATCH_FORMAT_MBOX,
        PATCH_FORMAT_STGIT,
        PATCH_FORMAT_STGIT_SERIES,
-       PATCH_FORMAT_HG
+       PATCH_FORMAT_HG,
+       PATCH_FORMAT_MBOXRD
 };
 
 enum keep_type {
@@ -121,7 +110,7 @@ struct am_state {
        size_t msg_len;
 
        /* when --rebasing, records the original commit the patch came from */
-       unsigned char orig_commit[GIT_SHA1_RAWSZ];
+       struct object_id orig_commit;
 
        /* number of digits in patch filename */
        int prec;
@@ -197,22 +186,22 @@ static inline const char *am_path(const struct am_state *state, const char *path
 /**
  * For convenience to call write_file()
  */
-static int write_state_text(const struct am_state *state,
-                           const char *name, const char *string)
+static void write_state_text(const struct am_state *state,
+                            const char *name, const char *string)
 {
-       return write_file(am_path(state, name), "%s", string);
+       write_file(am_path(state, name), "%s", string);
 }
 
-static int write_state_count(const struct am_state *state,
-                            const char *name, int value)
+static void write_state_count(const struct am_state *state,
+                             const char *name, int value)
 {
-       return write_file(am_path(state, name), "%d", value);
+       write_file(am_path(state, name), "%d", value);
 }
 
-static int write_state_bool(const struct am_state *state,
-                           const char *name, int value)
+static void write_state_bool(const struct am_state *state,
+                            const char *name, int value)
 {
-       return write_state_text(state, name, value ? "t" : "f");
+       write_state_text(state, name, value ? "t" : "f");
 }
 
 /**
@@ -271,38 +260,29 @@ static int read_state_file(struct strbuf *sb, const struct am_state *state,
 }
 
 /**
- * Reads a KEY=VALUE shell variable assignment from `fp`, returning the VALUE
- * as a newly-allocated string. VALUE must be a quoted string, and the KEY must
- * match `key`. Returns NULL on failure.
- *
- * This is used by read_author_script() to read the GIT_AUTHOR_* variables from
- * the author-script.
+ * Take a series of KEY='VALUE' lines where VALUE part is
+ * sq-quoted, and append <KEY, VALUE> at the end of the string list
  */
-static char *read_shell_var(FILE *fp, const char *key)
-{
-       struct strbuf sb = STRBUF_INIT;
-       const char *str;
-
-       if (strbuf_getline(&sb, fp, '\n'))
-               goto fail;
-
-       if (!skip_prefix(sb.buf, key, &str))
-               goto fail;
-
-       if (!skip_prefix(str, "=", &str))
-               goto fail;
-
-       strbuf_remove(&sb, 0, str - sb.buf);
-
-       str = sq_dequote(sb.buf);
-       if (!str)
-               goto fail;
-
-       return strbuf_detach(&sb, NULL);
-
-fail:
-       strbuf_release(&sb);
-       return NULL;
+static int parse_key_value_squoted(char *buf, struct string_list *list)
+{
+       while (*buf) {
+               struct string_list_item *item;
+               char *np;
+               char *cp = strchr(buf, '=');
+               if (!cp)
+                       return -1;
+               np = strchrnul(cp, '\n');
+               *cp++ = '\0';
+               item = string_list_append(list, buf);
+
+               buf = np + (*np == '\n');
+               *np = '\0';
+               cp = sq_dequote(cp);
+               if (!cp)
+                       return -1;
+               item->util = xstrdup(cp);
+       }
+       return 0;
 }
 
 /**
@@ -324,44 +304,39 @@ static char *read_shell_var(FILE *fp, const char *key)
 static int read_author_script(struct am_state *state)
 {
        const char *filename = am_path(state, "author-script");
-       FILE *fp;
+       struct strbuf buf = STRBUF_INIT;
+       struct string_list kv = STRING_LIST_INIT_DUP;
+       int retval = -1; /* assume failure */
+       int fd;
 
        assert(!state->author_name);
        assert(!state->author_email);
        assert(!state->author_date);
 
-       fp = fopen(filename, "r");
-       if (!fp) {
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
                if (errno == ENOENT)
                        return 0;
                die_errno(_("could not open '%s' for reading"), filename);
        }
+       strbuf_read(&buf, fd, 0);
+       close(fd);
+       if (parse_key_value_squoted(buf.buf, &kv))
+               goto finish;
 
-       state->author_name = read_shell_var(fp, "GIT_AUTHOR_NAME");
-       if (!state->author_name) {
-               fclose(fp);
-               return -1;
-       }
-
-       state->author_email = read_shell_var(fp, "GIT_AUTHOR_EMAIL");
-       if (!state->author_email) {
-               fclose(fp);
-               return -1;
-       }
-
-       state->author_date = read_shell_var(fp, "GIT_AUTHOR_DATE");
-       if (!state->author_date) {
-               fclose(fp);
-               return -1;
-       }
-
-       if (fgetc(fp) != EOF) {
-               fclose(fp);
-               return -1;
-       }
-
-       fclose(fp);
-       return 0;
+       if (kv.nr != 3 ||
+           strcmp(kv.items[0].string, "GIT_AUTHOR_NAME") ||
+           strcmp(kv.items[1].string, "GIT_AUTHOR_EMAIL") ||
+           strcmp(kv.items[2].string, "GIT_AUTHOR_DATE"))
+               goto finish;
+       state->author_name = kv.items[0].util;
+       state->author_email = kv.items[1].util;
+       state->author_date = kv.items[2].util;
+       retval = 0;
+finish:
+       string_list_clear(&kv, !!retval);
+       strbuf_release(&buf);
+       return retval;
 }
 
 /**
@@ -416,13 +391,8 @@ static int read_commit_msg(struct am_state *state)
  */
 static void write_commit_msg(const struct am_state *state)
 {
-       int fd;
        const char *filename = am_path(state, "final-commit");
-
-       fd = xopen(filename, O_WRONLY | O_CREAT, 0666);
-       if (write_in_full(fd, state->msg, state->msg_len) < 0)
-               die_errno(_("could not write to %s"), filename);
-       close(fd);
+       write_file_buf(filename, state->msg, state->msg_len);
 }
 
 /**
@@ -446,8 +416,8 @@ static void am_load(struct am_state *state)
        read_commit_msg(state);
 
        if (read_state_file(&sb, state, "original-commit", 1) < 0)
-               hashclr(state->orig_commit);
-       else if (get_sha1_hex(sb.buf, state->orig_commit) < 0)
+               oidclr(&state->orig_commit);
+       else if (get_oid_hex(sb.buf, &state->orig_commit) < 0)
                die(_("could not parse %s"), am_path(state, "original-commit"));
 
        read_state_file(&sb, state, "threeway", 1);
@@ -572,15 +542,15 @@ static int copy_notes_for_rebase(const struct am_state *state)
 
        fp = xfopen(am_path(state, "rewritten"), "r");
 
-       while (!strbuf_getline(&sb, fp, '\n')) {
-               unsigned char from_obj[GIT_SHA1_RAWSZ], to_obj[GIT_SHA1_RAWSZ];
+       while (!strbuf_getline_lf(&sb, fp)) {
+               struct object_id from_obj, to_obj;
 
                if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
 
-               if (get_sha1_hex(sb.buf, from_obj)) {
+               if (get_oid_hex(sb.buf, &from_obj)) {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
@@ -590,14 +560,14 @@ static int copy_notes_for_rebase(const struct am_state *state)
                        goto finish;
                }
 
-               if (get_sha1_hex(sb.buf + GIT_SHA1_HEXSZ + 1, to_obj)) {
+               if (get_oid_hex(sb.buf + GIT_SHA1_HEXSZ + 1, &to_obj)) {
                        ret = error(invalid_line, sb.buf);
                        goto finish;
                }
 
-               if (copy_note_for_rewrite(c, from_obj, to_obj))
+               if (copy_note_for_rewrite(c, from_obj.hash, to_obj.hash))
                        ret = error(_("Failed to copy notes from '%s' to '%s'"),
-                                       sha1_to_hex(from_obj), sha1_to_hex(to_obj));
+                                       oid_to_hex(&from_obj), oid_to_hex(&to_obj));
        }
 
 finish:
@@ -627,7 +597,7 @@ static int is_mail(FILE *fp)
        if (regcomp(&regex, header_regex, REG_NOSUB | REG_EXTENDED))
                die("invalid pattern: %s", header_regex);
 
-       while (!strbuf_getline_crlf(&sb, fp)) {
+       while (!strbuf_getline(&sb, fp)) {
                if (!sb.len)
                        break; /* End of header */
 
@@ -674,7 +644,7 @@ static int detect_patch_format(const char **paths)
 
        fp = xfopen(*paths, "r");
 
-       while (!strbuf_getline_crlf(&l1, fp)) {
+       while (!strbuf_getline(&l1, fp)) {
                if (l1.len)
                        break;
        }
@@ -695,9 +665,9 @@ static int detect_patch_format(const char **paths)
        }
 
        strbuf_reset(&l2);
-       strbuf_getline_crlf(&l2, fp);
+       strbuf_getline(&l2, fp);
        strbuf_reset(&l3);
-       strbuf_getline_crlf(&l3, fp);
+       strbuf_getline(&l3, fp);
 
        /*
         * If the second line is empty and the third is a From, Author or Date
@@ -726,7 +696,8 @@ static int detect_patch_format(const char **paths)
  * Splits out individual email patches from `paths`, where each path is either
  * a mbox file or a Maildir. Returns 0 on success, -1 on failure.
  */
-static int split_mail_mbox(struct am_state *state, const char **paths, int keep_cr)
+static int split_mail_mbox(struct am_state *state, const char **paths,
+                               int keep_cr, int mboxrd)
 {
        struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf last = STRBUF_INIT;
@@ -738,6 +709,8 @@ static int split_mail_mbox(struct am_state *state, const char **paths, int keep_
        argv_array_push(&cp.args, "-b");
        if (keep_cr)
                argv_array_push(&cp.args, "--keep-cr");
+       if (mboxrd)
+               argv_array_push(&cp.args, "--mboxrd");
        argv_array_push(&cp.args, "--");
        argv_array_pushv(&cp.args, paths);
 
@@ -783,15 +756,15 @@ static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
                        in = fopen(*paths, "r");
 
                if (!in)
-                       return error(_("could not open '%s' for reading: %s"),
-                                       *paths, strerror(errno));
+                       return error_errno(_("could not open '%s' for reading"),
+                                          *paths);
 
                mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
 
                out = fopen(mail, "w");
                if (!out)
-                       return error(_("could not open '%s' for writing: %s"),
-                                       mail, strerror(errno));
+                       return error_errno(_("could not open '%s' for writing"),
+                                          mail);
 
                ret = fn(out, in, keep_cr);
 
@@ -816,7 +789,7 @@ static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr)
        struct strbuf sb = STRBUF_INIT;
        int subject_printed = 0;
 
-       while (!strbuf_getline(&sb, in, '\n')) {
+       while (!strbuf_getline_lf(&sb, in)) {
                const char *str;
 
                if (str_isspace(sb.buf))
@@ -871,10 +844,9 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
 
        fp = fopen(*paths, "r");
        if (!fp)
-               return error(_("could not open '%s' for reading: %s"), *paths,
-                               strerror(errno));
+               return error_errno(_("could not open '%s' for reading"), *paths);
 
-       while (!strbuf_getline(&sb, fp, '\n')) {
+       while (!strbuf_getline_lf(&sb, fp)) {
                if (*sb.buf == '#')
                        continue; /* skip comment lines */
 
@@ -899,7 +871,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 {
        struct strbuf sb = STRBUF_INIT;
 
-       while (!strbuf_getline(&sb, in, '\n')) {
+       while (!strbuf_getline_lf(&sb, in)) {
                const char *str;
 
                if (skip_prefix(sb.buf, "# User ", &str))
@@ -980,13 +952,15 @@ static int split_mail(struct am_state *state, enum patch_format patch_format,
 
        switch (patch_format) {
        case PATCH_FORMAT_MBOX:
-               return split_mail_mbox(state, paths, keep_cr);
+               return split_mail_mbox(state, paths, keep_cr, 0);
        case PATCH_FORMAT_STGIT:
                return split_mail_conv(stgit_patch_to_mail, state, paths, keep_cr);
        case PATCH_FORMAT_STGIT_SERIES:
                return split_mail_stgit_series(state, paths, keep_cr);
        case PATCH_FORMAT_HG:
                return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr);
+       case PATCH_FORMAT_MBOXRD:
+               return split_mail_mbox(state, paths, keep_cr, 1);
        default:
                die("BUG: invalid patch_format");
        }
@@ -999,7 +973,7 @@ static int split_mail(struct am_state *state, enum patch_format patch_format,
 static void am_setup(struct am_state *state, enum patch_format patch_format,
                        const char **paths, int keep_cr)
 {
-       unsigned char curr_head[GIT_SHA1_RAWSZ];
+       struct object_id curr_head;
        const char *str;
        struct strbuf sb = STRBUF_INIT;
 
@@ -1067,10 +1041,10 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
        else
                write_state_text(state, "applying", "");
 
-       if (!get_sha1("HEAD", curr_head)) {
-               write_state_text(state, "abort-safety", sha1_to_hex(curr_head));
+       if (!get_oid("HEAD", &curr_head)) {
+               write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
                if (!state->rebasing)
-                       update_ref("am", "ORIG_HEAD", curr_head, NULL, 0,
+                       update_ref_oid("am", "ORIG_HEAD", &curr_head, NULL, 0,
                                        UPDATE_REFS_DIE_ON_ERR);
        } else {
                write_state_text(state, "abort-safety", "");
@@ -1095,7 +1069,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
  */
 static void am_next(struct am_state *state)
 {
-       unsigned char head[GIT_SHA1_RAWSZ];
+       struct object_id head;
 
        free(state->author_name);
        state->author_name = NULL;
@@ -1113,11 +1087,11 @@ static void am_next(struct am_state *state)
        unlink(am_path(state, "author-script"));
        unlink(am_path(state, "final-commit"));
 
-       hashclr(state->orig_commit);
+       oidclr(&state->orig_commit);
        unlink(am_path(state, "original-commit"));
 
-       if (!get_sha1("HEAD", head))
-               write_state_text(state, "abort-safety", sha1_to_hex(head));
+       if (!get_oid("HEAD", &head))
+               write_state_text(state, "abort-safety", oid_to_hex(&head));
        else
                write_state_text(state, "abort-safety", "");
 
@@ -1145,7 +1119,7 @@ static void refresh_and_write_cache(void)
 {
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
        if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write index file"));
@@ -1159,17 +1133,17 @@ static void refresh_and_write_cache(void)
  */
 static int index_has_changes(struct strbuf *sb)
 {
-       unsigned char head[GIT_SHA1_RAWSZ];
+       struct object_id head;
        int i;
 
-       if (!get_sha1_tree("HEAD", head)) {
+       if (!get_sha1_tree("HEAD", head.hash)) {
                struct diff_options opt;
 
                diff_setup(&opt);
                DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
                if (!sb)
                        DIFF_OPT_SET(&opt, QUICK);
-               do_diff_cache(head, &opt);
+               do_diff_cache(head.hash, &opt);
                diffcore_std(&opt);
                for (i = 0; sb && i < diff_queued_diff.nr; i++) {
                        if (i)
@@ -1258,62 +1232,65 @@ static void am_append_signoff(struct am_state *state)
 static int parse_mail(struct am_state *state, const char *mail)
 {
        FILE *fp;
-       struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf sb = STRBUF_INIT;
        struct strbuf msg = STRBUF_INIT;
        struct strbuf author_name = STRBUF_INIT;
        struct strbuf author_date = STRBUF_INIT;
        struct strbuf author_email = STRBUF_INIT;
        int ret = 0;
+       struct mailinfo mi;
 
-       cp.git_cmd = 1;
-       cp.in = xopen(mail, O_RDONLY, 0);
-       cp.out = xopen(am_path(state, "info"), O_WRONLY | O_CREAT, 0777);
+       setup_mailinfo(&mi);
 
-       argv_array_push(&cp.args, "mailinfo");
-       argv_array_push(&cp.args, state->utf8 ? "-u" : "-n");
+       if (state->utf8)
+               mi.metainfo_charset = get_commit_output_encoding();
+       else
+               mi.metainfo_charset = NULL;
 
        switch (state->keep) {
        case KEEP_FALSE:
                break;
        case KEEP_TRUE:
-               argv_array_push(&cp.args, "-k");
+               mi.keep_subject = 1;
                break;
        case KEEP_NON_PATCH:
-               argv_array_push(&cp.args, "-b");
+               mi.keep_non_patch_brackets_in_subject = 1;
                break;
        default:
                die("BUG: invalid value for state->keep");
        }
 
        if (state->message_id)
-               argv_array_push(&cp.args, "-m");
+               mi.add_message_id = 1;
 
        switch (state->scissors) {
        case SCISSORS_UNSET:
                break;
        case SCISSORS_FALSE:
-               argv_array_push(&cp.args, "--no-scissors");
+               mi.use_scissors = 0;
                break;
        case SCISSORS_TRUE:
-               argv_array_push(&cp.args, "--scissors");
+               mi.use_scissors = 1;
                break;
        default:
                die("BUG: invalid value for state->scissors");
        }
 
-       argv_array_push(&cp.args, am_path(state, "msg"));
-       argv_array_push(&cp.args, am_path(state, "patch"));
-
-       if (run_command(&cp) < 0)
+       mi.input = fopen(mail, "r");
+       if (!mi.input)
+               die("could not open input");
+       mi.output = fopen(am_path(state, "info"), "w");
+       if (!mi.output)
+               die("could not open output 'info'");
+       if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch")))
                die("could not parse patch");
 
-       close(cp.in);
-       close(cp.out);
+       fclose(mi.input);
+       fclose(mi.output);
 
        /* Extract message and author information */
        fp = xfopen(am_path(state, "info"), "r");
-       while (!strbuf_getline(&sb, fp, '\n')) {
+       while (!strbuf_getline_lf(&sb, fp)) {
                const char *x;
 
                if (skip_prefix(sb.buf, "Subject: ", &x)) {
@@ -1341,8 +1318,7 @@ static int parse_mail(struct am_state *state, const char *mail)
        }
 
        strbuf_addstr(&msg, "\n\n");
-       if (strbuf_read_file(&msg, am_path(state, "msg"), 0) < 0)
-               die_errno(_("could not read '%s'"), am_path(state, "msg"));
+       strbuf_addbuf(&msg, &mi.log_message);
        strbuf_stripspace(&msg, 0);
 
        if (state->signoff)
@@ -1366,6 +1342,7 @@ static int parse_mail(struct am_state *state, const char *mail)
        strbuf_release(&author_email);
        strbuf_release(&author_name);
        strbuf_release(&sb);
+       clear_mailinfo(&mi);
        return ret;
 }
 
@@ -1373,19 +1350,19 @@ static int parse_mail(struct am_state *state, const char *mail)
  * Sets commit_id to the commit hash where the mail was generated from.
  * Returns 0 on success, -1 on failure.
  */
-static int get_mail_commit_sha1(unsigned char *commit_id, const char *mail)
+static int get_mail_commit_oid(struct object_id *commit_id, const char *mail)
 {
        struct strbuf sb = STRBUF_INIT;
        FILE *fp = xfopen(mail, "r");
        const char *x;
 
-       if (strbuf_getline(&sb, fp, '\n'))
+       if (strbuf_getline_lf(&sb, fp))
                return -1;
 
        if (!skip_prefix(sb.buf, "From ", &x))
                return -1;
 
-       if (get_sha1_hex(x, commit_id) < 0)
+       if (get_oid_hex(x, commit_id) < 0)
                return -1;
 
        strbuf_release(&sb);
@@ -1437,7 +1414,7 @@ static void get_commit_info(struct am_state *state, struct commit *commit)
        assert(!state->msg);
        msg = strstr(buffer, "\n\n");
        if (!msg)
-               die(_("unable to parse commit %s"), sha1_to_hex(commit->object.sha1));
+               die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
        state->msg = xstrdup(msg + 2);
        state->msg_len = strlen(state->msg);
 }
@@ -1475,12 +1452,12 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
 static void write_index_patch(const struct am_state *state)
 {
        struct tree *tree;
-       unsigned char head[GIT_SHA1_RAWSZ];
+       struct object_id head;
        struct rev_info rev_info;
        FILE *fp;
 
-       if (!get_sha1_tree("HEAD", head))
-               tree = lookup_tree(head);
+       if (!get_sha1_tree("HEAD", head.hash))
+               tree = lookup_tree(head.hash);
        else
                tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
 
@@ -1510,19 +1487,19 @@ static void write_index_patch(const struct am_state *state)
 static int parse_mail_rebase(struct am_state *state, const char *mail)
 {
        struct commit *commit;
-       unsigned char commit_sha1[GIT_SHA1_RAWSZ];
+       struct object_id commit_oid;
 
-       if (get_mail_commit_sha1(commit_sha1, mail) < 0)
+       if (get_mail_commit_oid(&commit_oid, mail) < 0)
                die(_("could not parse %s"), mail);
 
-       commit = lookup_commit_or_die(commit_sha1, mail);
+       commit = lookup_commit_or_die(commit_oid.hash, mail);
 
        get_commit_info(state, commit);
 
        write_commit_patch(state, commit);
 
-       hashcpy(state->orig_commit, commit_sha1);
-       write_state_text(state, "original-commit", sha1_to_hex(commit_sha1));
+       oidcpy(&state->orig_commit, &commit_oid);
+       write_state_text(state, "original-commit", oid_to_hex(&commit_oid));
 
        return 0;
 }
@@ -1533,39 +1510,59 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
  */
 static int run_apply(const struct am_state *state, const char *index_file)
 {
-       struct child_process cp = CHILD_PROCESS_INIT;
+       struct argv_array apply_paths = ARGV_ARRAY_INIT;
+       struct argv_array apply_opts = ARGV_ARRAY_INIT;
+       struct apply_state apply_state;
+       int res, opts_left;
+       static struct lock_file lock_file;
+       int force_apply = 0;
+       int options = 0;
 
-       cp.git_cmd = 1;
+       if (init_apply_state(&apply_state, NULL, &lock_file))
+               die("BUG: init_apply_state() failed");
 
-       if (index_file)
-               argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", index_file);
+       argv_array_push(&apply_opts, "apply");
+       argv_array_pushv(&apply_opts, state->git_apply_opts.argv);
+
+       opts_left = apply_parse_options(apply_opts.argc, apply_opts.argv,
+                                       &apply_state, &force_apply, &options,
+                                       NULL);
+
+       if (opts_left != 0)
+               die("unknown option passed through to git apply");
+
+       if (index_file) {
+               apply_state.index_file = index_file;
+               apply_state.cached = 1;
+       } else
+               apply_state.check_index = 1;
 
        /*
         * If we are allowed to fall back on 3-way merge, don't give false
         * errors during the initial attempt.
         */
-       if (state->threeway && !index_file) {
-               cp.no_stdout = 1;
-               cp.no_stderr = 1;
-       }
+       if (state->threeway && !index_file)
+               apply_state.apply_verbosity = verbosity_silent;
 
-       argv_array_push(&cp.args, "apply");
+       if (check_apply_state(&apply_state, force_apply))
+               die("BUG: check_apply_state() failed");
 
-       argv_array_pushv(&cp.args, state->git_apply_opts.argv);
+       argv_array_push(&apply_paths, am_path(state, "patch"));
 
-       if (index_file)
-               argv_array_push(&cp.args, "--cached");
-       else
-               argv_array_push(&cp.args, "--index");
+       res = apply_all_patches(&apply_state, apply_paths.argc, apply_paths.argv, options);
 
-       argv_array_push(&cp.args, am_path(state, "patch"));
+       argv_array_clear(&apply_paths);
+       argv_array_clear(&apply_opts);
+       clear_apply_state(&apply_state);
 
-       if (run_command(&cp))
-               return -1;
+       if (res)
+               return res;
 
-       /* Reload index as git-apply will have modified it. */
-       discard_cache();
-       read_cache_from(index_file ? index_file : get_index_file());
+       if (index_file) {
+               /* Reload index as apply_all_patches() will have modified it. */
+               discard_cache();
+               read_cache_from(index_file);
+       }
 
        return 0;
 }
@@ -1589,48 +1586,19 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f
        return 0;
 }
 
-/**
- * Do the three-way merge using fake ancestor, his tree constructed
- * from the fake ancestor and the postimage of the patch, and our
- * state.
- */
-static int run_fallback_merge_recursive(const struct am_state *state,
-                                       unsigned char *orig_tree,
-                                       unsigned char *our_tree,
-                                       unsigned char *his_tree)
-{
-       struct child_process cp = CHILD_PROCESS_INIT;
-       int status;
-
-       cp.git_cmd = 1;
-
-       argv_array_pushf(&cp.env_array, "GITHEAD_%s=%.*s",
-                        sha1_to_hex(his_tree), linelen(state->msg), state->msg);
-       if (state->quiet)
-               argv_array_push(&cp.env_array, "GIT_MERGE_VERBOSITY=0");
-
-       argv_array_push(&cp.args, "merge-recursive");
-       argv_array_push(&cp.args, sha1_to_hex(orig_tree));
-       argv_array_push(&cp.args, "--");
-       argv_array_push(&cp.args, sha1_to_hex(our_tree));
-       argv_array_push(&cp.args, sha1_to_hex(his_tree));
-
-       status = run_command(&cp) ? (-1) : 0;
-       discard_cache();
-       read_cache();
-       return status;
-}
-
 /**
  * Attempt a threeway merge, using index_path as the temporary index.
  */
 static int fall_back_threeway(const struct am_state *state, const char *index_path)
 {
-       unsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],
-                     our_tree[GIT_SHA1_RAWSZ];
+       struct object_id orig_tree, their_tree, our_tree;
+       const struct object_id *bases[1] = { &orig_tree };
+       struct merge_options o;
+       struct commit *result;
+       char *their_tree_name;
 
-       if (get_sha1("HEAD", our_tree) < 0)
-               hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
+       if (get_oid("HEAD", &our_tree) < 0)
+               hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);
 
        if (build_fake_ancestor(state, index_path))
                return error("could not build fake ancestor");
@@ -1638,7 +1606,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
        discard_cache();
        read_cache_from(index_path);
 
-       if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))
+       if (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))
                return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
 
        say(state, stdout, _("Using index info to reconstruct a base tree..."));
@@ -1653,8 +1621,8 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 
                init_revisions(&rev_info, NULL);
                rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
-               diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1);
-               add_pending_sha1(&rev_info, "HEAD", our_tree, 0);
+               diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
+               add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0);
                diff_setup_done(&rev_info.diffopt);
                run_diff_index(&rev_info, 1);
        }
@@ -1663,7 +1631,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
                return error(_("Did you hand edit your patch?\n"
                                "It does not apply to blobs recorded in its index."));
 
-       if (write_index_as_tree(his_tree, &the_index, index_path, 0, NULL))
+       if (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))
                return error("could not write tree");
 
        say(state, stdout, _("Falling back to patching base and 3-way merge..."));
@@ -1673,17 +1641,28 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 
        /*
         * This is not so wrong. Depending on which base we picked, orig_tree
-        * may be wildly different from ours, but his_tree has the same set of
+        * may be wildly different from ours, but their_tree has the same set of
         * wildly different changes in parts the patch did not touch, so
         * recursive ends up canceling them, saying that we reverted all those
         * changes.
         */
 
-       if (run_fallback_merge_recursive(state, orig_tree, our_tree, his_tree)) {
+       init_merge_options(&o);
+
+       o.branch1 = "HEAD";
+       their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
+       o.branch2 = their_tree_name;
+
+       if (state->quiet)
+               o.verbosity = 0;
+
+       if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
                rerere(state->allow_rerere_autoupdate);
+               free(their_tree_name);
                return error(_("Failed to merge in the changes."));
        }
 
+       free(their_tree_name);
        return 0;
 }
 
@@ -1694,9 +1673,8 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
  */
 static void do_commit(const struct am_state *state)
 {
-       unsigned char tree[GIT_SHA1_RAWSZ], parent[GIT_SHA1_RAWSZ],
-                     commit[GIT_SHA1_RAWSZ];
-       unsigned char *ptr;
+       struct object_id tree, parent, commit;
+       const struct object_id *old_oid;
        struct commit_list *parents = NULL;
        const char *reflog_msg, *author;
        struct strbuf sb = STRBUF_INIT;
@@ -1704,14 +1682,14 @@ static void do_commit(const struct am_state *state)
        if (run_hook_le(NULL, "pre-applypatch", NULL))
                exit(1);
 
-       if (write_cache_as_tree(tree, 0, NULL))
+       if (write_cache_as_tree(tree.hash, 0, NULL))
                die(_("git write-tree failed to write a tree"));
 
-       if (!get_sha1_commit("HEAD", parent)) {
-               ptr = parent;
-               commit_list_insert(lookup_commit(parent), &parents);
+       if (!get_sha1_commit("HEAD", parent.hash)) {
+               old_oid = &parent;
+               commit_list_insert(lookup_commit(parent.hash), &parents);
        } else {
-               ptr = NULL;
+               old_oid = NULL;
                say(state, stderr, _("applying to an empty history"));
        }
 
@@ -1723,7 +1701,7 @@ static void do_commit(const struct am_state *state)
                setenv("GIT_COMMITTER_DATE",
                        state->ignore_date ? "" : state->author_date, 1);
 
-       if (commit_tree(state->msg, state->msg_len, tree, parents, commit,
+       if (commit_tree(state->msg, state->msg_len, tree.hash, parents, commit.hash,
                                author, state->sign_commit))
                die(_("failed to write commit object"));
 
@@ -1734,14 +1712,15 @@ static void do_commit(const struct am_state *state)
        strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
                        state->msg);
 
-       update_ref(sb.buf, "HEAD", commit, ptr, 0, UPDATE_REFS_DIE_ON_ERR);
+       update_ref_oid(sb.buf, "HEAD", &commit, old_oid, 0,
+                       UPDATE_REFS_DIE_ON_ERR);
 
        if (state->rebasing) {
                FILE *fp = xfopen(am_path(state, "rewritten"), "a");
 
-               assert(!is_null_sha1(state->orig_commit));
-               fprintf(fp, "%s ", sha1_to_hex(state->orig_commit));
-               fprintf(fp, "%s\n", sha1_to_hex(commit));
+               assert(!is_null_oid(&state->orig_commit));
+               fprintf(fp, "%s ", oid_to_hex(&state->orig_commit));
+               fprintf(fp, "%s\n", oid_to_hex(&commit));
                fclose(fp);
        }
 
@@ -1817,7 +1796,7 @@ static int do_interactive(struct am_state *state)
 
                        if (!pager)
                                pager = "cat";
-                       argv_array_push(&cp.args, pager);
+                       prepare_pager_args(&cp, pager);
                        argv_array_push(&cp.args, am_path(state, "patch"));
                        run_command(&cp);
                }
@@ -1851,6 +1830,8 @@ static void am_run(struct am_state *state, int resume)
                const char *mail = am_path(state, msgnum(state));
                int apply_status;
 
+               reset_ident_date();
+
                if (!file_exists(mail))
                        goto next;
 
@@ -1935,6 +1916,7 @@ static void am_run(struct am_state *state, int resume)
         */
        if (!state->rebasing) {
                am_destroy(state);
+               close_all_packs();
                run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
        }
 }
@@ -1994,7 +1976,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
                return -1;
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 
        refresh_cache(REFRESH_QUIET);
 
@@ -2034,7 +2016,7 @@ static int merge_tree(struct tree *tree)
                return -1;
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
@@ -2059,30 +2041,30 @@ static int merge_tree(struct tree *tree)
  * Clean the index without touching entries that are not modified between
  * `head` and `remote`.
  */
-static int clean_index(const unsigned char *head, const unsigned char *remote)
+static int clean_index(const struct object_id *head, const struct object_id *remote)
 {
        struct tree *head_tree, *remote_tree, *index_tree;
-       unsigned char index[GIT_SHA1_RAWSZ];
+       struct object_id index;
 
-       head_tree = parse_tree_indirect(head);
+       head_tree = parse_tree_indirect(head->hash);
        if (!head_tree)
-               return error(_("Could not parse object '%s'."), sha1_to_hex(head));
+               return error(_("Could not parse object '%s'."), oid_to_hex(head));
 
-       remote_tree = parse_tree_indirect(remote);
+       remote_tree = parse_tree_indirect(remote->hash);
        if (!remote_tree)
-               return error(_("Could not parse object '%s'."), sha1_to_hex(remote));
+               return error(_("Could not parse object '%s'."), oid_to_hex(remote));
 
        read_cache_unmerged();
 
        if (fast_forward_to(head_tree, head_tree, 1))
                return -1;
 
-       if (write_cache_as_tree(index, 0, NULL))
+       if (write_cache_as_tree(index.hash, 0, NULL))
                return -1;
 
-       index_tree = parse_tree_indirect(index);
+       index_tree = parse_tree_indirect(index.hash);
        if (!index_tree)
-               return error(_("Could not parse object '%s'."), sha1_to_hex(index));
+               return error(_("Could not parse object '%s'."), oid_to_hex(&index));
 
        if (fast_forward_to(index_tree, remote_tree, 0))
                return -1;
@@ -2110,14 +2092,14 @@ static void am_rerere_clear(void)
  */
 static void am_skip(struct am_state *state)
 {
-       unsigned char head[GIT_SHA1_RAWSZ];
+       struct object_id head;
 
        am_rerere_clear();
 
-       if (get_sha1("HEAD", head))
-               hashcpy(head, EMPTY_TREE_SHA1_BIN);
+       if (get_oid("HEAD", &head))
+               hashcpy(head.hash, EMPTY_TREE_SHA1_BIN);
 
-       if (clean_index(head, head))
+       if (clean_index(&head, &head))
                die(_("failed to clean index"));
 
        am_next(state);
@@ -2135,24 +2117,24 @@ static void am_skip(struct am_state *state)
 static int safe_to_abort(const struct am_state *state)
 {
        struct strbuf sb = STRBUF_INIT;
-       unsigned char abort_safety[GIT_SHA1_RAWSZ], head[GIT_SHA1_RAWSZ];
+       struct object_id abort_safety, head;
 
        if (file_exists(am_path(state, "dirtyindex")))
                return 0;
 
        if (read_state_file(&sb, state, "abort-safety", 1) > 0) {
-               if (get_sha1_hex(sb.buf, abort_safety))
-                       die(_("could not parse %s"), am_path(state, "abort_safety"));
+               if (get_oid_hex(sb.buf, &abort_safety))
+                       die(_("could not parse %s"), am_path(state, "abort-safety"));
        } else
-               hashclr(abort_safety);
+               oidclr(&abort_safety);
 
-       if (get_sha1("HEAD", head))
-               hashclr(head);
+       if (get_oid("HEAD", &head))
+               oidclr(&head);
 
-       if (!hashcmp(head, abort_safety))
+       if (!oidcmp(&head, &abort_safety))
                return 1;
 
-       error(_("You seem to have moved HEAD since the last 'am' failure.\n"
+       warning(_("You seem to have moved HEAD since the last 'am' failure.\n"
                "Not rewinding to ORIG_HEAD"));
 
        return 0;
@@ -2163,7 +2145,7 @@ static int safe_to_abort(const struct am_state *state)
  */
 static void am_abort(struct am_state *state)
 {
-       unsigned char curr_head[GIT_SHA1_RAWSZ], orig_head[GIT_SHA1_RAWSZ];
+       struct object_id curr_head, orig_head;
        int has_curr_head, has_orig_head;
        char *curr_branch;
 
@@ -2174,20 +2156,20 @@ static void am_abort(struct am_state *state)
 
        am_rerere_clear();
 
-       curr_branch = resolve_refdup("HEAD", 0, curr_head, NULL);
-       has_curr_head = !is_null_sha1(curr_head);
+       curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL);
+       has_curr_head = !is_null_oid(&curr_head);
        if (!has_curr_head)
-               hashcpy(curr_head, EMPTY_TREE_SHA1_BIN);
+               hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN);
 
-       has_orig_head = !get_sha1("ORIG_HEAD", orig_head);
+       has_orig_head = !get_oid("ORIG_HEAD", &orig_head);
        if (!has_orig_head)
-               hashcpy(orig_head, EMPTY_TREE_SHA1_BIN);
+               hashcpy(orig_head.hash, EMPTY_TREE_SHA1_BIN);
 
-       clean_index(curr_head, orig_head);
+       clean_index(&curr_head, &orig_head);
 
        if (has_orig_head)
-               update_ref("am --abort", "HEAD", orig_head,
-                               has_curr_head ? curr_head : NULL, 0,
+               update_ref_oid("am --abort", "HEAD", &orig_head,
+                               has_curr_head ? &curr_head : NULL, 0,
                                UPDATE_REFS_DIE_ON_ERR);
        else if (curr_branch)
                delete_ref(curr_branch, NULL, REF_NODEREF);
@@ -2212,6 +2194,8 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int
                *opt_value = PATCH_FORMAT_STGIT_SERIES;
        else if (!strcmp(arg, "hg"))
                *opt_value = PATCH_FORMAT_HG;
+       else if (!strcmp(arg, "mboxrd"))
+               *opt_value = PATCH_FORMAT_MBOXRD;
        else
                return error(_("Invalid value for --patch-format: %s"), arg);
        return 0;
@@ -2246,7 +2230,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
        int in_progress;
 
        const char * const usage[] = {
-               N_("git am [<options>] [(<mbox>|<Maildir>)...]"),
+               N_("git am [<options>] [(<mbox> | <Maildir>)...]"),
                N_("git am [<options>] (--continue | --skip | --abort)"),
                NULL
        };