KEEP_NON_PATCH /* pass -b flag to git-mailinfo */
};
+enum scissors_type {
+ SCISSORS_UNSET = -1,
+ SCISSORS_FALSE = 0, /* pass --no-scissors to git-mailinfo */
+ SCISSORS_TRUE /* pass --scissors to git-mailinfo */
+};
+
struct am_state {
/* state directory path */
char *dir;
int utf8;
int keep; /* enum keep_type */
int message_id;
+ int scissors; /* enum scissors_type */
+ struct argv_array git_apply_opts;
const char *resolvemsg;
+ int committer_date_is_author_date;
+ int ignore_date;
+ const char *sign_commit;
int rebasing;
};
*/
static void am_state_init(struct am_state *state, const char *dir)
{
+ int gpgsign;
+
memset(state, 0, sizeof(*state));
assert(dir);
state->utf8 = 1;
git_config_get_bool("am.messageid", &state->message_id);
+
+ state->scissors = SCISSORS_UNSET;
+
+ argv_array_init(&state->git_apply_opts);
+
+ if (!git_config_get_bool("commit.gpgsign", &gpgsign))
+ state->sign_commit = gpgsign ? "" : NULL;
}
/**
free(state->author_email);
free(state->author_date);
free(state->msg);
+ argv_array_clear(&state->git_apply_opts);
}
/**
read_state_file(&sb, state, "messageid", 1);
state->message_id = !strcmp(sb.buf, "t");
+ read_state_file(&sb, state, "scissors", 1);
+ if (!strcmp(sb.buf, "t"))
+ state->scissors = SCISSORS_TRUE;
+ else if (!strcmp(sb.buf, "f"))
+ state->scissors = SCISSORS_FALSE;
+ else
+ state->scissors = SCISSORS_UNSET;
+
+ read_state_file(&sb, state, "apply-opt", 1);
+ argv_array_clear(&state->git_apply_opts);
+ if (sq_dequote_to_argv_array(sb.buf, &state->git_apply_opts) < 0)
+ die(_("could not parse %s"), am_path(state, "apply-opt"));
+
state->rebasing = !!file_exists(am_path(state, "rebasing"));
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)
+static int split_mail_mbox(struct am_state *state, const char **paths, int keep_cr)
{
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf last = STRBUF_INIT;
argv_array_pushf(&cp.args, "-d%d", state->prec);
argv_array_pushf(&cp.args, "-o%s", state->dir);
argv_array_push(&cp.args, "-b");
+ if (keep_cr)
+ argv_array_push(&cp.args, "--keep-cr");
argv_array_push(&cp.args, "--");
argv_array_pushv(&cp.args, paths);
* 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.
*
+ * Set keep_cr to 0 to convert all lines ending with \r\n to end with \n, 1
+ * to disable this behavior, -1 to use the default configured setting.
+ *
* Returns 0 on success, -1 on failure.
*/
static int split_mail(struct am_state *state, enum patch_format patch_format,
- const char **paths)
+ const char **paths, int keep_cr)
{
+ if (keep_cr < 0) {
+ keep_cr = 0;
+ git_config_get_bool("am.keepcr", &keep_cr);
+ }
+
switch (patch_format) {
case PATCH_FORMAT_MBOX:
- return split_mail_mbox(state, paths);
+ return split_mail_mbox(state, paths, keep_cr);
default:
die("BUG: invalid patch_format");
}
* Setup a new am session for applying patches
*/
static void am_setup(struct am_state *state, enum patch_format patch_format,
- const char **paths)
+ const char **paths, int keep_cr)
{
unsigned char curr_head[GIT_SHA1_RAWSZ];
const char *str;
+ struct strbuf sb = STRBUF_INIT;
if (!patch_format)
patch_format = detect_patch_format(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) {
+ if (split_mail(state, patch_format, paths, keep_cr) < 0) {
am_destroy(state);
die(_("Failed to split patches."));
}
write_file(am_path(state, "messageid"), 1, state->message_id ? "t" : "f");
+ switch (state->scissors) {
+ case SCISSORS_UNSET:
+ str = "";
+ break;
+ case SCISSORS_FALSE:
+ str = "f";
+ break;
+ case SCISSORS_TRUE:
+ str = "t";
+ break;
+ default:
+ die("BUG: invalid value for state->scissors");
+ }
+
+ write_file(am_path(state, "scissors"), 1, "%s", str);
+
+ sq_quote_argv(&sb, state->git_apply_opts.argv, 0);
+ write_file(am_path(state, "apply-opt"), 1, "%s", sb.buf);
+
if (state->rebasing)
write_file(am_path(state, "rebasing"), 1, "%s", "");
else
write_file(am_path(state, "next"), 1, "%d", state->cur);
write_file(am_path(state, "last"), 1, "%d", state->last);
+
+ strbuf_release(&sb);
}
/**
if (state->message_id)
argv_array_push(&cp.args, "-m");
+ switch (state->scissors) {
+ case SCISSORS_UNSET:
+ break;
+ case SCISSORS_FALSE:
+ argv_array_push(&cp.args, "--no-scissors");
+ break;
+ case SCISSORS_TRUE:
+ argv_array_push(&cp.args, "--scissors");
+ 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"));
argv_array_push(&cp.args, "apply");
+ argv_array_pushv(&cp.args, state->git_apply_opts.argv);
+
if (index_file)
argv_array_push(&cp.args, "--cached");
else
cp.git_cmd = 1;
argv_array_push(&cp.args, "apply");
+ argv_array_pushv(&cp.args, state->git_apply_opts.argv);
argv_array_pushf(&cp.args, "--build-fake-ancestor=%s", index_file);
argv_array_push(&cp.args, am_path(state, "patch"));
}
author = fmt_ident(state->author_name, state->author_email,
- state->author_date, IDENT_STRICT);
+ state->ignore_date ? NULL : state->author_date,
+ IDENT_STRICT);
+
+ if (state->committer_date_is_author_date)
+ setenv("GIT_COMMITTER_DATE",
+ state->ignore_date ? "" : state->author_date, 1);
if (commit_tree(state->msg, state->msg_len, tree, parents, commit,
- author, NULL))
+ author, state->sign_commit))
die(_("failed to write commit object"));
reflog_msg = getenv("GIT_REFLOG_ACTION");
int cmd_am(int argc, const char **argv, const char *prefix)
{
struct am_state state;
+ int keep_cr = -1;
int patch_format = PATCH_FORMAT_UNKNOWN;
enum resume_mode resume = RESUME_FALSE;
N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),
OPT_BOOL('m', "message-id", &state.message_id,
N_("pass -m flag to git-mailinfo")),
+ { OPTION_SET_INT, 0, "keep-cr", &keep_cr, NULL,
+ N_("pass --keep-cr flag to git-mailsplit for mbox format"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1},
+ { OPTION_SET_INT, 0, "no-keep-cr", &keep_cr, NULL,
+ N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0},
+ OPT_BOOL('c', "scissors", &state.scissors,
+ N_("strip everything before a scissors line")),
+ OPT_PASSTHRU_ARGV(0, "whitespace", &state.git_apply_opts, N_("action"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_PASSTHRU_ARGV(0, "ignore-space-change", &state.git_apply_opts, NULL,
+ N_("pass it through git-apply"),
+ PARSE_OPT_NOARG),
+ OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &state.git_apply_opts, NULL,
+ N_("pass it through git-apply"),
+ PARSE_OPT_NOARG),
+ OPT_PASSTHRU_ARGV(0, "directory", &state.git_apply_opts, N_("root"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_PASSTHRU_ARGV(0, "exclude", &state.git_apply_opts, N_("path"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_PASSTHRU_ARGV(0, "include", &state.git_apply_opts, N_("path"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_PASSTHRU_ARGV('C', NULL, &state.git_apply_opts, N_("n"),
+ N_("pass it through git-apply"),
+ 0),
+ OPT_PASSTHRU_ARGV('p', NULL, &state.git_apply_opts, N_("num"),
+ N_("pass it through git-apply"),
+ 0),
OPT_CALLBACK(0, "patch-format", &patch_format, N_("format"),
N_("format the patch(es) are in"),
parse_opt_patchformat),
+ OPT_PASSTHRU_ARGV(0, "reject", &state.git_apply_opts, NULL,
+ N_("pass it through git-apply"),
+ PARSE_OPT_NOARG),
OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL,
N_("override error message when patch failure occurs")),
OPT_CMDMODE(0, "continue", &resume,
OPT_CMDMODE(0, "abort", &resume,
N_("restore the original branch and abort the patching operation."),
RESUME_ABORT),
+ OPT_BOOL(0, "committer-date-is-author-date",
+ &state.committer_date_is_author_date,
+ N_("lie about committer date")),
+ OPT_BOOL(0, "ignore-date", &state.ignore_date,
+ N_("use current timestamp for author date")),
+ { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
+ N_("GPG-sign commits"),
+ PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
N_("(internal use for git-rebase)")),
OPT_END()
argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
}
- am_setup(&state, patch_format, paths.argv);
+ am_setup(&state, patch_format, paths.argv, keep_cr);
argv_array_clear(&paths);
}