summaryrefslogtreecommitdiff
path: root/builtin/am.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/am.c')
-rw-r--r--builtin/am.c92
1 files changed, 73 insertions, 19 deletions
diff --git a/builtin/am.c b/builtin/am.c
index 473d794..142f884 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -10,6 +10,7 @@
#include "dir.h"
#include "run-command.h"
#include "quote.h"
+#include "tempfile.h"
#include "lockfile.h"
#include "cache-tree.h"
#include "refs.h"
@@ -98,6 +99,12 @@ enum scissors_type {
SCISSORS_TRUE /* pass --scissors to git-mailinfo */
};
+enum signoff_type {
+ SIGNOFF_FALSE = 0,
+ SIGNOFF_TRUE = 1,
+ SIGNOFF_EXPLICIT /* --signoff was set on the command-line */
+};
+
struct am_state {
/* state directory path */
char *dir;
@@ -123,7 +130,7 @@ struct am_state {
int interactive;
int threeway;
int quiet;
- int signoff;
+ int signoff; /* enum signoff_type */
int utf8;
int keep; /* enum keep_type */
int message_id;
@@ -1186,6 +1193,18 @@ static void NORETURN die_user_resolve(const struct am_state *state)
}
/**
+ * Appends signoff to the "msg" field of the am_state.
+ */
+static void am_append_signoff(struct am_state *state)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
+ append_signoff(&sb, 0, 0);
+ state->msg = strbuf_detach(&sb, &state->msg_len);
+}
+
+/**
* Parses `mail` using git-mailinfo, extracting its patch and authorship info.
* state->msg will be set to the patch message. state->author_name,
* state->author_email and state->author_date will be set to the patch author's
@@ -1779,7 +1798,6 @@ static void am_run(struct am_state *state, int resume)
if (resume) {
validate_resume_state(state);
- resume = 0;
} else {
int skip;
@@ -1841,6 +1859,10 @@ static void am_run(struct am_state *state, int resume)
next:
am_next(state);
+
+ if (resume)
+ am_load(state);
+ resume = 0;
}
if (!is_empty_file(am_path(state, "rewritten"))) {
@@ -1895,6 +1917,7 @@ static void am_resolve(struct am_state *state)
next:
am_next(state);
+ am_load(state);
am_run(state, 0);
}
@@ -1940,15 +1963,48 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
}
/**
+ * Merges a tree into the index. The index's stat info will take precedence
+ * over the merged tree's. Returns 0 on success, -1 on failure.
+ */
+static int merge_tree(struct tree *tree)
+{
+ struct lock_file *lock_file;
+ struct unpack_trees_options opts;
+ struct tree_desc t[1];
+
+ if (parse_tree(tree))
+ return -1;
+
+ lock_file = xcalloc(1, sizeof(struct lock_file));
+ hold_locked_index(lock_file, 1);
+
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = 1;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
+ opts.merge = 1;
+ opts.fn = oneway_merge;
+ init_tree_desc(&t[0], tree->buffer, tree->size);
+
+ if (unpack_trees(1, t, &opts)) {
+ rollback_lock_file(lock_file);
+ return -1;
+ }
+
+ if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ die(_("unable to write new index file"));
+
+ return 0;
+}
+
+/**
* 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)
{
- struct lock_file *lock_file;
struct tree *head_tree, *remote_tree, *index_tree;
unsigned char index[GIT_SHA1_RAWSZ];
- struct pathspec pathspec;
head_tree = parse_tree_indirect(head);
if (!head_tree)
@@ -1973,18 +2029,8 @@ static int clean_index(const unsigned char *head, const unsigned char *remote)
if (fast_forward_to(index_tree, remote_tree, 0))
return -1;
- memset(&pathspec, 0, sizeof(pathspec));
-
- lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, 1);
-
- if (read_tree(remote_tree, 0, &pathspec)) {
- rollback_lock_file(lock_file);
+ if (merge_tree(remote_tree))
return -1;
- }
-
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
- die(_("unable to write new index file"));
remove_branch_state();
@@ -2022,6 +2068,7 @@ static void am_skip(struct am_state *state)
die(_("failed to clean index"));
am_next(state);
+ am_load(state);
am_run(state, 0);
}
@@ -2132,6 +2179,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
int keep_cr = -1;
int patch_format = PATCH_FORMAT_UNKNOWN;
enum resume_mode resume = RESUME_FALSE;
+ int in_progress;
const char * const usage[] = {
N_("git am [options] [(<mbox>|<Maildir>)...]"),
@@ -2147,8 +2195,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
OPT_BOOL('3', "3way", &state.threeway,
N_("allow fall back on 3way merging if needed")),
OPT__QUIET(&state.quiet, N_("be quiet")),
- OPT_BOOL('s', "signoff", &state.signoff,
- N_("add a Signed-off-by line to the commit message")),
+ OPT_SET_INT('s', "signoff", &state.signoff,
+ N_("add a Signed-off-by line to the commit message"),
+ SIGNOFF_EXPLICIT),
OPT_BOOL('u', "utf8", &state.utf8,
N_("recode into utf8 (default)")),
OPT_SET_INT('k', "keep", &state.keep,
@@ -2227,6 +2276,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
am_state_init(&state, git_path("rebase-apply"));
+ in_progress = am_in_progress(&state);
+ if (in_progress)
+ am_load(&state);
+
argc = parse_options(argc, argv, prefix, options, usage, 0);
if (binary >= 0)
@@ -2239,7 +2292,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
if (read_index_preload(&the_index, NULL) < 0)
die(_("failed to read the index"));
- if (am_in_progress(&state)) {
+ if (in_progress) {
/*
* Catch user error to feed us patches when there is a session
* in progress:
@@ -2258,7 +2311,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
if (resume == RESUME_FALSE)
resume = RESUME_APPLY;
- am_load(&state);
+ if (state.signoff == SIGNOFF_EXPLICIT)
+ am_append_signoff(&state);
} else {
struct argv_array paths = ARGV_ARRAY_INIT;
int i;