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>
Paul Tan committed
Aug 4, 2015 at 21:51 UTC
11c2177f2c264513f8d3abe64666fa05bcd84a57
1 file changed
+104
-3
builtin/am.c
+104
-3
@@ -8,6 +8,12 @@
8
#include "exec_cmd.h"
9
#include "parse-options.h"
10
#include "dir.h"
11
+#include "run-command.h"
12
+
13
+enum patch_format {
14
+ PATCH_FORMAT_UNKNOWN = 0,
15
+ PATCH_FORMAT_MBOX
16
+};
17
18
struct am_state {
19
/* state directory path */
@@ -16,6 +22,9 @@ struct am_state {
22
/* current and last patch numbers, 1-indexed */
23
int cur;
24
int last;
25
+
26
+ /* number of digits in patch filename */
27
+ int prec;
28
};
29
30
/**
@@ -28,6 +37,8 @@ static void am_state_init(struct am_state *state, const char *dir)
37
38
assert(dir);
39
state->dir = xstrdup(dir);
40
+
41
+ state->prec = 4;
42
}
43
44
/**
@@ -116,14 +127,72 @@ static void am_destroy(const struct am_state *state)
127
strbuf_release(&sb);
128
}
129
130
+/**
131
+ * Splits out individual email patches from `paths`, where each path is either
132
+ * a mbox file or a Maildir. Returns 0 on success, -1 on failure.
133
+ */
134
+static int split_mail_mbox(struct am_state *state, const char **paths)
135
+{
136
+ struct child_process cp = CHILD_PROCESS_INIT;
137
+ struct strbuf last = STRBUF_INIT;
138
+
139
+ cp.git_cmd = 1;
140
+ argv_array_push(&cp.args, "mailsplit");
141
+ argv_array_pushf(&cp.args, "-d%d", state->prec);
142
+ argv_array_pushf(&cp.args, "-o%s", state->dir);
143
+ argv_array_push(&cp.args, "-b");
144
+ argv_array_push(&cp.args, "--");
145
+ argv_array_pushv(&cp.args, paths);
146
+
147
+ if (capture_command(&cp, &last, 8))
148
+ return -1;
149
+
150
+ state->cur = 1;
151
+ state->last = strtol(last.buf, NULL, 10);
152
+
153
+ return 0;
154
+}
155
+
156
+/**
157
+ * Splits a list of files/directories into individual email patches. Each path
158
+ * in `paths` must be a file/directory that is formatted according to
159
+ * `patch_format`.
160
+ *
161
+ * Once split out, the individual email patches will be stored in the state
162
+ * directory, with each patch's filename being its index, padded to state->prec
163
+ * digits.
164
+ *
165
+ * state->cur will be set to the index of the first mail, and state->last will
166
+ * be set to the index of the last mail.
167
+ *
168
+ * Returns 0 on success, -1 on failure.
169
+ */
170
+static int split_mail(struct am_state *state, enum patch_format patch_format,
171
+ const char **paths)
172
+{
173
+ switch (patch_format) {
174
+ case PATCH_FORMAT_MBOX:
175
+ return split_mail_mbox(state, paths);
176
+ default:
177
+ die("BUG: invalid patch_format");
178
+ }
179
+ return -1;
180
+}
181
+
182
/**
183
* Setup a new am session for applying patches
184
*/
122
-static void am_setup(struct am_state *state)
185
+static void am_setup(struct am_state *state, enum patch_format patch_format,
186
+ const char **paths)
187
{
188
if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
189
die_errno(_("failed to create directory '%s'"), state->dir);
190
191
+ if (split_mail(state, patch_format, paths) < 0) {
192
+ am_destroy(state);
193
+ die(_("Failed to split patches."));
194
+ }
195
+
196
/*
197
* NOTE: Since the "next" and "last" files determine if an am_state
198
* session is in progress, they should be written last.
@@ -159,9 +228,25 @@ static void am_run(struct am_state *state)
228
am_destroy(state);
229
}
230
231
+/**
232
+ * parse_options() callback that validates and sets opt->value to the
233
+ * PATCH_FORMAT_* enum value corresponding to `arg`.
234
+ */
235
+static int parse_opt_patchformat(const struct option *opt, const char *arg, int unset)
236
+{
237
+ int *opt_value = opt->value;
238
+
239
+ if (!strcmp(arg, "mbox"))
240
+ *opt_value = PATCH_FORMAT_MBOX;
241
+ else
242
+ return error(_("Invalid value for --patch-format: %s"), arg);
243
+ return 0;
244
+}
245
+
246
int cmd_am(int argc, const char **argv, const char *prefix)
247
{
248
struct am_state state;
249
+ int patch_format = PATCH_FORMAT_UNKNOWN;
250
251
const char * const usage[] = {
252
N_("git am [options] [(<mbox>|<Maildir>)...]"),
@@ -169,6 +254,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
254
};
255
256
struct option options[] = {
257
+ OPT_CALLBACK(0, "patch-format", &patch_format, N_("format"),
258
+ N_("format the patch(es) are in"),
259
+ parse_opt_patchformat),
260
OPT_END()
261
};
262
@@ -195,8 +283,21 @@ int cmd_am(int argc, const char **argv, const char *prefix)
283
284
if (am_in_progress(&state))
285
am_load(&state);
198
- else
199
- am_setup(&state);
286
+ else {
287
+ struct argv_array paths = ARGV_ARRAY_INIT;
288
+ int i;
289
+
290
+ for (i = 0; i < argc; i++) {
291
+ if (is_absolute_path(argv[i]) || !prefix)
292
+ argv_array_push(&paths, argv[i]);
293
+ else
294
+ argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
295
+ }
296
+
297
+ am_setup(&state, patch_format, paths.argv);
298
+
299
+ argv_array_clear(&paths);
300
+ }
301
302
am_run(&state);
303