builtin-am: split out mbox/maildir patches with git-mailsplit
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 <gitster@pobox.com>
Signed-off-by: Paul Tan <pyokagan@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
105
builtin/am.c
105
builtin/am.c
@ -8,6 +8,12 @@
|
|||||||
#include "exec_cmd.h"
|
#include "exec_cmd.h"
|
||||||
#include "parse-options.h"
|
#include "parse-options.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
|
#include "run-command.h"
|
||||||
|
|
||||||
|
enum patch_format {
|
||||||
|
PATCH_FORMAT_UNKNOWN = 0,
|
||||||
|
PATCH_FORMAT_MBOX
|
||||||
|
};
|
||||||
|
|
||||||
struct am_state {
|
struct am_state {
|
||||||
/* state directory path */
|
/* state directory path */
|
||||||
@ -16,6 +22,9 @@ struct am_state {
|
|||||||
/* current and last patch numbers, 1-indexed */
|
/* current and last patch numbers, 1-indexed */
|
||||||
int cur;
|
int cur;
|
||||||
int last;
|
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);
|
assert(dir);
|
||||||
state->dir = xstrdup(dir);
|
state->dir = xstrdup(dir);
|
||||||
|
|
||||||
|
state->prec = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,14 +127,72 @@ static void am_destroy(const struct am_state *state)
|
|||||||
strbuf_release(&sb);
|
strbuf_release(&sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* 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)
|
if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
|
||||||
die_errno(_("failed to create directory '%s'"), state->dir);
|
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
|
* NOTE: Since the "next" and "last" files determine if an am_state
|
||||||
* session is in progress, they should be written last.
|
* session is in progress, they should be written last.
|
||||||
@ -159,9 +228,25 @@ static void am_run(struct am_state *state)
|
|||||||
am_destroy(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)
|
int cmd_am(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
struct am_state state;
|
struct am_state state;
|
||||||
|
int patch_format = PATCH_FORMAT_UNKNOWN;
|
||||||
|
|
||||||
const char * const usage[] = {
|
const char * const usage[] = {
|
||||||
N_("git am [options] [(<mbox>|<Maildir>)...]"),
|
N_("git am [options] [(<mbox>|<Maildir>)...]"),
|
||||||
@ -169,6 +254,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct option options[] = {
|
struct option options[] = {
|
||||||
|
OPT_CALLBACK(0, "patch-format", &patch_format, N_("format"),
|
||||||
|
N_("format the patch(es) are in"),
|
||||||
|
parse_opt_patchformat),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -195,8 +283,21 @@ int cmd_am(int argc, const char **argv, const char *prefix)
|
|||||||
|
|
||||||
if (am_in_progress(&state))
|
if (am_in_progress(&state))
|
||||||
am_load(&state);
|
am_load(&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
|
else
|
||||||
am_setup(&state);
|
argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
am_setup(&state, patch_format, paths.argv);
|
||||||
|
|
||||||
|
argv_array_clear(&paths);
|
||||||
|
}
|
||||||
|
|
||||||
am_run(&state);
|
am_run(&state);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user