diff options
Diffstat (limited to 'builtin/am.c')
| -rw-r--r-- | builtin/am.c | 204 | 
1 files changed, 88 insertions, 116 deletions
diff --git a/builtin/am.c b/builtin/am.c index 00e4a09814..b36d1f047d 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -28,6 +28,7 @@  #include "rerere.h"  #include "prompt.h"  #include "mailinfo.h" +#include "string-list.h"  /**   * Returns 1 if the file is empty or does not exist, 0 otherwise. @@ -70,7 +71,8 @@ enum patch_format {  	PATCH_FORMAT_MBOX,  	PATCH_FORMAT_STGIT,  	PATCH_FORMAT_STGIT_SERIES, -	PATCH_FORMAT_HG +	PATCH_FORMAT_HG, +	PATCH_FORMAT_MBOXRD  };  enum keep_type { @@ -183,22 +185,22 @@ static inline const char *am_path(const struct am_state *state, const char *path  /**   * For convenience to call write_file()   */ -static int write_state_text(const struct am_state *state, -			    const char *name, const char *string) +static void write_state_text(const struct am_state *state, +			     const char *name, const char *string)  { -	return write_file(am_path(state, name), "%s", string); +	write_file(am_path(state, name), "%s", string);  } -static int write_state_count(const struct am_state *state, -			     const char *name, int value) +static void write_state_count(const struct am_state *state, +			      const char *name, int value)  { -	return write_file(am_path(state, name), "%d", value); +	write_file(am_path(state, name), "%d", value);  } -static int write_state_bool(const struct am_state *state, -			    const char *name, int value) +static void write_state_bool(const struct am_state *state, +			     const char *name, int value)  { -	return write_state_text(state, name, value ? "t" : "f"); +	write_state_text(state, name, value ? "t" : "f");  }  /** @@ -257,38 +259,29 @@ static int read_state_file(struct strbuf *sb, const struct am_state *state,  }  /** - * Reads a KEY=VALUE shell variable assignment from `fp`, returning the VALUE - * as a newly-allocated string. VALUE must be a quoted string, and the KEY must - * match `key`. Returns NULL on failure. - * - * This is used by read_author_script() to read the GIT_AUTHOR_* variables from - * the author-script. + * Take a series of KEY='VALUE' lines where VALUE part is + * sq-quoted, and append <KEY, VALUE> at the end of the string list   */ -static char *read_shell_var(FILE *fp, const char *key) +static int parse_key_value_squoted(char *buf, struct string_list *list)  { -	struct strbuf sb = STRBUF_INIT; -	const char *str; - -	if (strbuf_getline_lf(&sb, fp)) -		goto fail; - -	if (!skip_prefix(sb.buf, key, &str)) -		goto fail; - -	if (!skip_prefix(str, "=", &str)) -		goto fail; - -	strbuf_remove(&sb, 0, str - sb.buf); - -	str = sq_dequote(sb.buf); -	if (!str) -		goto fail; - -	return strbuf_detach(&sb, NULL); - -fail: -	strbuf_release(&sb); -	return NULL; +	while (*buf) { +		struct string_list_item *item; +		char *np; +		char *cp = strchr(buf, '='); +		if (!cp) +			return -1; +		np = strchrnul(cp, '\n'); +		*cp++ = '\0'; +		item = string_list_append(list, buf); + +		buf = np + (*np == '\n'); +		*np = '\0'; +		cp = sq_dequote(cp); +		if (!cp) +			return -1; +		item->util = xstrdup(cp); +	} +	return 0;  }  /** @@ -310,44 +303,39 @@ fail:  static int read_author_script(struct am_state *state)  {  	const char *filename = am_path(state, "author-script"); -	FILE *fp; +	struct strbuf buf = STRBUF_INIT; +	struct string_list kv = STRING_LIST_INIT_DUP; +	int retval = -1; /* assume failure */ +	int fd;  	assert(!state->author_name);  	assert(!state->author_email);  	assert(!state->author_date); -	fp = fopen(filename, "r"); -	if (!fp) { +	fd = open(filename, O_RDONLY); +	if (fd < 0) {  		if (errno == ENOENT)  			return 0;  		die_errno(_("could not open '%s' for reading"), filename);  	} +	strbuf_read(&buf, fd, 0); +	close(fd); +	if (parse_key_value_squoted(buf.buf, &kv)) +		goto finish; -	state->author_name = read_shell_var(fp, "GIT_AUTHOR_NAME"); -	if (!state->author_name) { -		fclose(fp); -		return -1; -	} - -	state->author_email = read_shell_var(fp, "GIT_AUTHOR_EMAIL"); -	if (!state->author_email) { -		fclose(fp); -		return -1; -	} - -	state->author_date = read_shell_var(fp, "GIT_AUTHOR_DATE"); -	if (!state->author_date) { -		fclose(fp); -		return -1; -	} - -	if (fgetc(fp) != EOF) { -		fclose(fp); -		return -1; -	} - -	fclose(fp); -	return 0; +	if (kv.nr != 3 || +	    strcmp(kv.items[0].string, "GIT_AUTHOR_NAME") || +	    strcmp(kv.items[1].string, "GIT_AUTHOR_EMAIL") || +	    strcmp(kv.items[2].string, "GIT_AUTHOR_DATE")) +		goto finish; +	state->author_name = kv.items[0].util; +	state->author_email = kv.items[1].util; +	state->author_date = kv.items[2].util; +	retval = 0; +finish: +	string_list_clear(&kv, !!retval); +	strbuf_release(&buf); +	return retval;  }  /** @@ -402,13 +390,8 @@ static int read_commit_msg(struct am_state *state)   */  static void write_commit_msg(const struct am_state *state)  { -	int fd;  	const char *filename = am_path(state, "final-commit"); - -	fd = xopen(filename, O_WRONLY | O_CREAT, 0666); -	if (write_in_full(fd, state->msg, state->msg_len) < 0) -		die_errno(_("could not write to %s"), filename); -	close(fd); +	write_file_buf(filename, state->msg, state->msg_len);  }  /** @@ -712,7 +695,8 @@ done:   * 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, int keep_cr) +static int split_mail_mbox(struct am_state *state, const char **paths, +				int keep_cr, int mboxrd)  {  	struct child_process cp = CHILD_PROCESS_INIT;  	struct strbuf last = STRBUF_INIT; @@ -724,6 +708,8 @@ static int split_mail_mbox(struct am_state *state, const char **paths, int keep_  	argv_array_push(&cp.args, "-b");  	if (keep_cr)  		argv_array_push(&cp.args, "--keep-cr"); +	if (mboxrd) +		argv_array_push(&cp.args, "--mboxrd");  	argv_array_push(&cp.args, "--");  	argv_array_pushv(&cp.args, paths); @@ -965,13 +951,15 @@ static int split_mail(struct am_state *state, enum patch_format patch_format,  	switch (patch_format) {  	case PATCH_FORMAT_MBOX: -		return split_mail_mbox(state, paths, keep_cr); +		return split_mail_mbox(state, paths, keep_cr, 0);  	case PATCH_FORMAT_STGIT:  		return split_mail_conv(stgit_patch_to_mail, state, paths, keep_cr);  	case PATCH_FORMAT_STGIT_SERIES:  		return split_mail_stgit_series(state, paths, keep_cr);  	case PATCH_FORMAT_HG:  		return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr); +	case PATCH_FORMAT_MBOXRD: +		return split_mail_mbox(state, paths, keep_cr, 1);  	default:  		die("BUG: invalid patch_format");  	} @@ -1578,47 +1566,18 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f  }  /** - * Do the three-way merge using fake ancestor, their tree constructed - * from the fake ancestor and the postimage of the patch, and our - * state. - */ -static int run_fallback_merge_recursive(const struct am_state *state, -					unsigned char *orig_tree, -					unsigned char *our_tree, -					unsigned char *their_tree) -{ -	struct child_process cp = CHILD_PROCESS_INIT; -	int status; - -	cp.git_cmd = 1; - -	argv_array_pushf(&cp.env_array, "GITHEAD_%s=%.*s", -			 sha1_to_hex(their_tree), linelen(state->msg), state->msg); -	if (state->quiet) -		argv_array_push(&cp.env_array, "GIT_MERGE_VERBOSITY=0"); - -	argv_array_push(&cp.args, "merge-recursive"); -	argv_array_push(&cp.args, sha1_to_hex(orig_tree)); -	argv_array_push(&cp.args, "--"); -	argv_array_push(&cp.args, sha1_to_hex(our_tree)); -	argv_array_push(&cp.args, sha1_to_hex(their_tree)); - -	status = run_command(&cp) ? (-1) : 0; -	discard_cache(); -	read_cache(); -	return status; -} - -/**   * Attempt a threeway merge, using index_path as the temporary index.   */  static int fall_back_threeway(const struct am_state *state, const char *index_path)  { -	unsigned char orig_tree[GIT_SHA1_RAWSZ], their_tree[GIT_SHA1_RAWSZ], -		      our_tree[GIT_SHA1_RAWSZ]; +	struct object_id orig_tree, their_tree, our_tree; +	const struct object_id *bases[1] = { &orig_tree }; +	struct merge_options o; +	struct commit *result; +	char *their_tree_name; -	if (get_sha1("HEAD", our_tree) < 0) -		hashcpy(our_tree, EMPTY_TREE_SHA1_BIN); +	if (get_oid("HEAD", &our_tree) < 0) +		hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);  	if (build_fake_ancestor(state, index_path))  		return error("could not build fake ancestor"); @@ -1626,7 +1585,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa  	discard_cache();  	read_cache_from(index_path); -	if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL)) +	if (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))  		return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));  	say(state, stdout, _("Using index info to reconstruct a base tree...")); @@ -1642,7 +1601,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa  		init_revisions(&rev_info, NULL);  		rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;  		diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix); -		add_pending_sha1(&rev_info, "HEAD", our_tree, 0); +		add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0);  		diff_setup_done(&rev_info.diffopt);  		run_diff_index(&rev_info, 1);  	} @@ -1651,7 +1610,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa  		return error(_("Did you hand edit your patch?\n"  				"It does not apply to blobs recorded in its index.")); -	if (write_index_as_tree(their_tree, &the_index, index_path, 0, NULL)) +	if (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))  		return error("could not write tree");  	say(state, stdout, _("Falling back to patching base and 3-way merge...")); @@ -1667,11 +1626,22 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa  	 * changes.  	 */ -	if (run_fallback_merge_recursive(state, orig_tree, our_tree, their_tree)) { +	init_merge_options(&o); + +	o.branch1 = "HEAD"; +	their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg); +	o.branch2 = their_tree_name; + +	if (state->quiet) +		o.verbosity = 0; + +	if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {  		rerere(state->allow_rerere_autoupdate); +		free(their_tree_name);  		return error(_("Failed to merge in the changes."));  	} +	free(their_tree_name);  	return 0;  } @@ -2203,6 +2173,8 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int  		*opt_value = PATCH_FORMAT_STGIT_SERIES;  	else if (!strcmp(arg, "hg"))  		*opt_value = PATCH_FORMAT_HG; +	else if (!strcmp(arg, "mboxrd")) +		*opt_value = PATCH_FORMAT_MBOXRD;  	else  		return error(_("Invalid value for --patch-format: %s"), arg);  	return 0;  | 
