git-am.sh supports mbox, stgit and mercurial patches. Re-implement
support for splitting out mbox/maildirs using git-mailsplit, while also
implementing the framework required to support other patch formats in
the future.

Re-implement support for the --patch-format option (since a5a6755
(git-am foreign patch support: introduce patch_format, 2009-05-27)) to
allow the user to choose between the different patch formats.

Helped-by: Junio C Hamano <gits...@pobox.com>
Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 builtin/am.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 104 insertions(+), 3 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index ac172c4..5f3c131 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -8,6 +8,12 @@
 #include "exec_cmd.h"
 #include "parse-options.h"
 #include "dir.h"
+#include "run-command.h"
+
+enum patch_format {
+       PATCH_FORMAT_UNKNOWN = 0,
+       PATCH_FORMAT_MBOX
+};
 
 struct am_state {
        /* state directory path */
@@ -16,6 +22,9 @@ struct am_state {
        /* current and last patch numbers, 1-indexed */
        int cur;
        int last;
+
+       /* number of digits in patch filename */
+       int prec;
 };
 
 /**
@@ -28,6 +37,8 @@ static void am_state_init(struct am_state *state, const char 
*dir)
 
        assert(dir);
        state->dir = xstrdup(dir);
+
+       state->prec = 4;
 }
 
 /**
@@ -117,13 +128,71 @@ static void am_destroy(const struct am_state *state)
 }
 
 /**
+ * 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)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf last = STRBUF_INIT;
+
+       cp.git_cmd = 1;
+       argv_array_push(&cp.args, "mailsplit");
+       argv_array_pushf(&cp.args, "-d%d", state->prec);
+       argv_array_pushf(&cp.args, "-o%s", state->dir);
+       argv_array_push(&cp.args, "-b");
+       argv_array_push(&cp.args, "--");
+       argv_array_pushv(&cp.args, paths);
+
+       if (capture_command(&cp, &last, 8))
+               return -1;
+
+       state->cur = 1;
+       state->last = strtol(last.buf, NULL, 10);
+
+       return 0;
+}
+
+/**
+ * Splits a list of files/directories into individual email patches. Each path
+ * in `paths` must be a file/directory that is formatted according to
+ * `patch_format`.
+ *
+ * Once split out, the individual email patches will be stored in the state
+ * directory, with each patch's filename being its index, padded to state->prec
+ * digits.
+ *
+ * state->cur will be set to the index of the first mail, and state->last will
+ * be set to the index of the last mail.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail(struct am_state *state, enum patch_format patch_format,
+                       const char **paths)
+{
+       switch (patch_format) {
+       case PATCH_FORMAT_MBOX:
+               return split_mail_mbox(state, paths);
+       default:
+               die("BUG: invalid patch_format");
+       }
+       return -1;
+}
+
+/**
  * Setup a new am session for applying patches
  */
-static void am_setup(struct am_state *state)
+static void am_setup(struct am_state *state, enum patch_format patch_format,
+                       const char **paths)
 {
        if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
                die_errno(_("failed to create directory '%s'"), state->dir);
 
+       if (split_mail(state, patch_format, paths) < 0) {
+               am_destroy(state);
+               die(_("Failed to split patches."));
+       }
+
        /*
         * NOTE: Since the "next" and "last" files determine if an am_state
         * session is in progress, they should be written last.
@@ -159,9 +228,25 @@ static void am_run(struct am_state *state)
        am_destroy(state);
 }
 
+/**
+ * parse_options() callback that validates and sets opt->value to the
+ * PATCH_FORMAT_* enum value corresponding to `arg`.
+ */
+static int parse_opt_patchformat(const struct option *opt, const char *arg, 
int unset)
+{
+       int *opt_value = opt->value;
+
+       if (!strcmp(arg, "mbox"))
+               *opt_value = PATCH_FORMAT_MBOX;
+       else
+               return error(_("Invalid value for --patch-format: %s"), arg);
+       return 0;
+}
+
 int cmd_am(int argc, const char **argv, const char *prefix)
 {
        struct am_state state;
+       int patch_format = PATCH_FORMAT_UNKNOWN;
 
        const char * const usage[] = {
                N_("git am [options] [(<mbox>|<Maildir>)...]"),
@@ -169,6 +254,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
        };
 
        struct option options[] = {
+               OPT_CALLBACK(0, "patch-format", &patch_format, N_("format"),
+                       N_("format the patch(es) are in"),
+                       parse_opt_patchformat),
                OPT_END()
        };
 
@@ -195,8 +283,21 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 
        if (am_in_progress(&state))
                am_load(&state);
-       else
-               am_setup(&state);
+       else {
+               struct argv_array paths = ARGV_ARRAY_INIT;
+               int i;
+
+               for (i = 0; i < argc; i++) {
+                       if (is_absolute_path(argv[i]) || !prefix)
+                               argv_array_push(&paths, argv[i]);
+                       else
+                               argv_array_push(&paths, mkpath("%s/%s", prefix, 
argv[i]));
+               }
+
+               am_setup(&state, patch_format, paths.argv);
+
+               argv_array_clear(&paths);
+       }
 
        am_run(&state);
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to