diff options
Diffstat (limited to 'builtin/merge.c')
| -rw-r--r-- | builtin/merge.c | 280 | 
1 files changed, 177 insertions, 103 deletions
| diff --git a/builtin/merge.c b/builtin/merge.c index 3b0f8f96d4..e6741f3380 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -231,9 +231,9 @@ static struct option builtin_merge_options[] = {  /* Cleans up metadata that is uninteresting after a succeeded merge. */  static void drop_save(void)  { -	unlink(git_path("MERGE_HEAD")); -	unlink(git_path("MERGE_MSG")); -	unlink(git_path("MERGE_MODE")); +	unlink(git_path_merge_head()); +	unlink(git_path_merge_msg()); +	unlink(git_path_merge_mode());  }  static int save_state(unsigned char *stash) @@ -338,7 +338,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead  	struct pretty_print_context ctx = {0};  	printf(_("Squash commit -- not updating HEAD\n")); -	filename = git_path("SQUASH_MSG"); +	filename = git_path_squash_msg();  	fd = open(filename, O_WRONLY | O_CREAT, 0666);  	if (fd < 0)  		die_errno(_("Could not write to '%s'"), filename); @@ -492,8 +492,7 @@ static void merge_name(const char *remote, struct strbuf *msg)  	}  	if (len) {  		struct strbuf truname = STRBUF_INIT; -		strbuf_addstr(&truname, "refs/heads/"); -		strbuf_addstr(&truname, remote); +		strbuf_addf(&truname, "refs/heads/%s", remote);  		strbuf_setlen(&truname, truname.len - len);  		if (ref_exists(truname.buf)) {  			strbuf_addf(msg, @@ -504,28 +503,7 @@ static void merge_name(const char *remote, struct strbuf *msg)  			strbuf_release(&truname);  			goto cleanup;  		} -	} - -	if (!strcmp(remote, "FETCH_HEAD") && -			!access(git_path("FETCH_HEAD"), R_OK)) { -		const char *filename; -		FILE *fp; -		struct strbuf line = STRBUF_INIT; -		char *ptr; - -		filename = git_path("FETCH_HEAD"); -		fp = fopen(filename, "r"); -		if (!fp) -			die_errno(_("could not open '%s' for reading"), -				  filename); -		strbuf_getline(&line, fp, '\n'); -		fclose(fp); -		ptr = strstr(line.buf, "\tnot-for-merge\t"); -		if (ptr) -			strbuf_remove(&line, ptr-line.buf+1, 13); -		strbuf_addbuf(msg, &line); -		strbuf_release(&line); -		goto cleanup; +		strbuf_release(&truname);  	}  	if (remote_head->util) { @@ -776,7 +754,7 @@ static void add_strategies(const char *string, unsigned attr)  static void write_merge_msg(struct strbuf *msg)  { -	const char *filename = git_path("MERGE_MSG"); +	const char *filename = git_path_merge_msg();  	int fd = open(filename, O_WRONLY | O_CREAT, 0666);  	if (fd < 0)  		die_errno(_("Could not open '%s' for writing"), @@ -788,7 +766,7 @@ static void write_merge_msg(struct strbuf *msg)  static void read_merge_msg(struct strbuf *msg)  { -	const char *filename = git_path("MERGE_MSG"); +	const char *filename = git_path_merge_msg();  	strbuf_reset(msg);  	if (strbuf_read_file(msg, filename, 0) < 0)  		die_errno(_("Could not read from '%s'"), filename); @@ -821,14 +799,14 @@ static void prepare_to_commit(struct commit_list *remoteheads)  		strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);  	write_merge_msg(&msg);  	if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg", -			    git_path("MERGE_MSG"), "merge", NULL)) +			    git_path_merge_msg(), "merge", NULL))  		abort_commit(remoteheads, NULL);  	if (0 < option_edit) { -		if (launch_editor(git_path("MERGE_MSG"), NULL, NULL)) +		if (launch_editor(git_path_merge_msg(), NULL, NULL))  			abort_commit(remoteheads, NULL);  	}  	read_merge_msg(&msg); -	stripspace(&msg, 0 < option_edit); +	strbuf_stripspace(&msg, 0 < option_edit);  	if (!msg.len)  		abort_commit(remoteheads, _("Empty commit message."));  	strbuf_release(&merge_msg); @@ -887,7 +865,7 @@ static int suggest_conflicts(void)  	FILE *fp;  	struct strbuf msgbuf = STRBUF_INIT; -	filename = git_path("MERGE_MSG"); +	filename = git_path_merge_msg();  	fp = fopen(filename, "a");  	if (!fp)  		die_errno(_("Could not open '%s' for writing"), filename); @@ -955,7 +933,7 @@ static int setup_with_upstream(const char ***argv)  	if (!branch)  		die(_("No current branch.")); -	if (!branch->remote) +	if (!branch->remote_name)  		die(_("No remote for the current branch."));  	if (!branch->merge_nr)  		die(_("No default upstream defined for the current branch.")); @@ -989,7 +967,7 @@ static void write_merge_state(struct commit_list *remoteheads)  		}  		strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));  	} -	filename = git_path("MERGE_HEAD"); +	filename = git_path_merge_head();  	fd = open(filename, O_WRONLY | O_CREAT, 0666);  	if (fd < 0)  		die_errno(_("Could not open '%s' for writing"), filename); @@ -999,7 +977,7 @@ static void write_merge_state(struct commit_list *remoteheads)  	strbuf_addch(&merge_msg, '\n');  	write_merge_msg(&merge_msg); -	filename = git_path("MERGE_MODE"); +	filename = git_path_merge_mode();  	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);  	if (fd < 0)  		die_errno(_("Could not open '%s' for writing"), filename); @@ -1037,28 +1015,24 @@ static int default_edit_option(void)  		st_stdin.st_mode == st_stdout.st_mode);  } -static struct commit_list *collect_parents(struct commit *head_commit, -					   int *head_subsumed, -					   int argc, const char **argv) +static struct commit_list *reduce_parents(struct commit *head_commit, +					  int *head_subsumed, +					  struct commit_list *remoteheads)  { -	int i; -	struct commit_list *remoteheads = NULL, *parents, *next; -	struct commit_list **remotes = &remoteheads; +	struct commit_list *parents, *next, **remotes = &remoteheads; -	if (head_commit) -		remotes = &commit_list_insert(head_commit, remotes)->next; -	for (i = 0; i < argc; i++) { -		struct commit *commit = get_merge_parent(argv[i]); -		if (!commit) -			help_unknown_ref(argv[i], "merge", -					 "not something we can merge"); -		remotes = &commit_list_insert(commit, remotes)->next; -	} -	*remotes = NULL; +	/* +	 * Is the current HEAD reachable from another commit being +	 * merged?  If so we do not want to record it as a parent of +	 * the resulting merge, unless --no-ff is given.  We will flip +	 * this variable to 0 when we find HEAD among the independent +	 * tips being merged. +	 */ +	*head_subsumed = 1; +	/* Find what parents to record by checking independent ones. */  	parents = reduce_heads(remoteheads); -	*head_subsumed = 1; /* we will flip this to 0 when we find it */  	for (remoteheads = NULL, remotes = &remoteheads;  	     parents;  	     parents = next) { @@ -1068,7 +1042,119 @@ static struct commit_list *collect_parents(struct commit *head_commit,  			*head_subsumed = 0;  		else  			remotes = &commit_list_insert(commit, remotes)->next; +		free(parents); +	} +	return remoteheads; +} + +static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *merge_msg) +{ +	struct fmt_merge_msg_opts opts; + +	memset(&opts, 0, sizeof(opts)); +	opts.add_title = !have_message; +	opts.shortlog_len = shortlog_len; +	opts.credit_people = (0 < option_edit); + +	fmt_merge_msg(merge_names, merge_msg, &opts); +	if (merge_msg->len) +		strbuf_setlen(merge_msg, merge_msg->len - 1); +} + +static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge_names) +{ +	const char *filename; +	int fd, pos, npos; +	struct strbuf fetch_head_file = STRBUF_INIT; + +	if (!merge_names) +		merge_names = &fetch_head_file; + +	filename = git_path_fetch_head(); +	fd = open(filename, O_RDONLY); +	if (fd < 0) +		die_errno(_("could not open '%s' for reading"), filename); + +	if (strbuf_read(merge_names, fd, 0) < 0) +		die_errno(_("could not read '%s'"), filename); +	if (close(fd) < 0) +		die_errno(_("could not close '%s'"), filename); + +	for (pos = 0; pos < merge_names->len; pos = npos) { +		unsigned char sha1[20]; +		char *ptr; +		struct commit *commit; + +		ptr = strchr(merge_names->buf + pos, '\n'); +		if (ptr) +			npos = ptr - merge_names->buf + 1; +		else +			npos = merge_names->len; + +		if (npos - pos < 40 + 2 || +		    get_sha1_hex(merge_names->buf + pos, sha1)) +			commit = NULL; /* bad */ +		else if (memcmp(merge_names->buf + pos + 40, "\t\t", 2)) +			continue; /* not-for-merge */ +		else { +			char saved = merge_names->buf[pos + 40]; +			merge_names->buf[pos + 40] = '\0'; +			commit = get_merge_parent(merge_names->buf + pos); +			merge_names->buf[pos + 40] = saved; +		} +		if (!commit) { +			if (ptr) +				*ptr = '\0'; +			die("not something we can merge in %s: %s", +			    filename, merge_names->buf + pos); +		} +		remotes = &commit_list_insert(commit, remotes)->next; +	} + +	if (merge_names == &fetch_head_file) +		strbuf_release(&fetch_head_file); +} + +static struct commit_list *collect_parents(struct commit *head_commit, +					   int *head_subsumed, +					   int argc, const char **argv, +					   struct strbuf *merge_msg) +{ +	int i; +	struct commit_list *remoteheads = NULL; +	struct commit_list **remotes = &remoteheads; +	struct strbuf merge_names = STRBUF_INIT, *autogen = NULL; + +	if (merge_msg && (!have_message || shortlog_len)) +		autogen = &merge_names; + +	if (head_commit) +		remotes = &commit_list_insert(head_commit, remotes)->next; + +	if (argc == 1 && !strcmp(argv[0], "FETCH_HEAD")) { +		handle_fetch_head(remotes, autogen); +		remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads); +	} else { +		for (i = 0; i < argc; i++) { +			struct commit *commit = get_merge_parent(argv[i]); +			if (!commit) +				help_unknown_ref(argv[i], "merge", +						 "not something we can merge"); +			remotes = &commit_list_insert(commit, remotes)->next; +		} +		remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads); +		if (autogen) { +			struct commit_list *p; +			for (p = remoteheads; p; p = p->next) +				merge_name(merge_remote_util(p->item)->name, autogen); +		}  	} + +	if (autogen) { +		prepare_merge_message(autogen, merge_msg); +		strbuf_release(autogen); +	} +  	return remoteheads;  } @@ -1118,7 +1204,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)  		int nargc = 2;  		const char *nargv[] = {"reset", "--merge", NULL}; -		if (!file_exists(git_path("MERGE_HEAD"))) +		if (!file_exists(git_path_merge_head()))  			die(_("There is no merge to abort (MERGE_HEAD missing)."));  		/* Invoke 'git reset --merge' */ @@ -1129,7 +1215,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)  	if (read_cache_unmerged())  		die_resolve_conflict("merge"); -	if (file_exists(git_path("MERGE_HEAD"))) { +	if (file_exists(git_path_merge_head())) {  		/*  		 * There is no unmerged entry, don't advise 'git  		 * add/rm <file>', just 'git commit'. @@ -1140,7 +1226,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)  		else  			die(_("You have not concluded your merge (MERGE_HEAD exists)."));  	} -	if (file_exists(git_path("CHERRY_PICK_HEAD"))) { +	if (file_exists(git_path_cherry_pick_head())) {  		if (advice_resolve_conflict)  			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"  			    "Please, commit your changes before you merge.")); @@ -1158,61 +1244,62 @@ int cmd_merge(int argc, const char **argv, const char *prefix)  		option_commit = 0;  	} -	if (!abort_current_merge) { -		if (!argc) { -			if (default_to_upstream) -				argc = setup_with_upstream(&argv); -			else -				die(_("No commit specified and merge.defaultToUpstream not set.")); -		} else if (argc == 1 && !strcmp(argv[0], "-")) -			argv[0] = "@{-1}"; +	if (!argc) { +		if (default_to_upstream) +			argc = setup_with_upstream(&argv); +		else +			die(_("No commit specified and merge.defaultToUpstream not set.")); +	} else if (argc == 1 && !strcmp(argv[0], "-")) { +		argv[0] = "@{-1}";  	} +  	if (!argc)  		usage_with_options(builtin_merge_usage,  			builtin_merge_options); -	/* -	 * This could be traditional "merge <msg> HEAD <commit>..."  and -	 * the way we can tell it is to see if the second token is HEAD, -	 * but some people might have misused the interface and used a -	 * commit-ish that is the same as HEAD there instead. -	 * Traditional format never would have "-m" so it is an -	 * additional safety measure to check for it. -	 */ - -	if (!have_message && head_commit && -	    is_old_style_invocation(argc, argv, head_commit->object.sha1)) { -		strbuf_addstr(&merge_msg, argv[0]); -		head_arg = argv[1]; -		argv += 2; -		argc -= 2; -		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); -	} else if (!head_commit) { +	if (!head_commit) {  		struct commit *remote_head;  		/*  		 * If the merged head is a valid one there is no reason  		 * to forbid "git merge" into a branch yet to be born.  		 * We do the same for "git pull".  		 */ -		if (argc != 1) -			die(_("Can merge only exactly one commit into " -				"empty head"));  		if (squash)  			die(_("Squash commit into empty head not supported yet"));  		if (fast_forward == FF_NO)  			die(_("Non-fast-forward commit does not make sense into "  			    "an empty head")); -		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); +		remoteheads = collect_parents(head_commit, &head_subsumed, +					      argc, argv, NULL);  		remote_head = remoteheads->item;  		if (!remote_head)  			die(_("%s - not something we can merge"), argv[0]); +		if (remoteheads->next) +			die(_("Can merge only exactly one commit into empty head"));  		read_empty(remote_head->object.sha1, 0);  		update_ref("initial pull", "HEAD", remote_head->object.sha1,  			   NULL, 0, UPDATE_REFS_DIE_ON_ERR);  		goto done; -	} else { -		struct strbuf merge_names = STRBUF_INIT; +	} +	/* +	 * This could be traditional "merge <msg> HEAD <commit>..."  and +	 * the way we can tell it is to see if the second token is HEAD, +	 * but some people might have misused the interface and used a +	 * commit-ish that is the same as HEAD there instead. +	 * Traditional format never would have "-m" so it is an +	 * additional safety measure to check for it. +	 */ +	if (!have_message && +	    is_old_style_invocation(argc, argv, head_commit->object.sha1)) { +		warning("old-style 'git merge <msg> HEAD <commit>' is deprecated."); +		strbuf_addstr(&merge_msg, argv[0]); +		head_arg = argv[1]; +		argv += 2; +		argc -= 2; +		remoteheads = collect_parents(head_commit, &head_subsumed, +					      argc, argv, NULL); +	} else {  		/* We are invoked directly as the first-class UI. */  		head_arg = "HEAD"; @@ -1221,21 +1308,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)  		 * the standard merge summary message to be appended  		 * to the given message.  		 */ -		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); -		for (p = remoteheads; p; p = p->next) -			merge_name(merge_remote_util(p->item)->name, &merge_names); - -		if (!have_message || shortlog_len) { -			struct fmt_merge_msg_opts opts; -			memset(&opts, 0, sizeof(opts)); -			opts.add_title = !have_message; -			opts.shortlog_len = shortlog_len; -			opts.credit_people = (0 < option_edit); - -			fmt_merge_msg(&merge_names, &merge_msg, &opts); -			if (merge_msg.len) -				strbuf_setlen(&merge_msg, merge_msg.len - 1); -		} +		remoteheads = collect_parents(head_commit, &head_subsumed, +					      argc, argv, &merge_msg);  	}  	if (!head_commit || !argc) | 
