git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 13/14] merge: handle FETCH_HEAD internally

From
Junio C Hamano <gitster@pobox.com>
Date
Apr 26, 2015, 05:26 UTC
Message-ID
<1430025967-24479-14-git-send-email-gitster@pobox.com>
In-Reply-To
<1430025967-24479-1-git-send-email-gitster@pobox.com>
The collect_parents() function now is responsible for
 1. parsing the commits given on the command line into a list of
    commits to be merged;
 2. filtering these parents into independent ones; and
 3. optionally calling fmt_merge_msg() via prepare_merge_message()
    to prepare an auto-generated merge log message, using fake
    contents that FETCH_HEAD would have had if these commits were
    fetched from the current repository with "git pull . $args..."
Make "git merge FETCH_HEAD" to be the same as the traditional
    git merge "$(git fmt-merge-msg <.git/FETCH_HEAD)" $commits

invocation of the command in "git pull", where $commits are the ones that appear in FETCH_HEAD that are not marked as not-for-merge, by making it do a bit more, specifically:

 - noticing "FETCH_HEAD" is the only "commit" on the command line
   and picking the commits that are not marked as not-for-merge as
   the list of commits to be merged (substitute for step #1 above);
 - letting the resulting list fed to step #2 above;
 - doing the step #3 above, using the contents of the FETCH_HEAD
   instead of fake contents crafted from the list of commits parsed
   in the step #1 above.

Note that this changes the semantics. "git merge FETCH_HEAD" has always behaved as if the first commit in the FETCH_HEAD file were directly specified on the command line, creating a two-way merge whose auto-generated merge log said "merge commit xyz". With this change, if the previous fetch was to grab multiple branches (e.g. "git fetch $there topic-a topic-b"), the new world order is to create an octopus, behaving as if "git pull $there topic-a topic-b" were run. This is a deliberate change to make that happen.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/git-merge.txt |   4 ++
 builtin/merge.c             | 105 ++++++++++++++++++++++++++++++--------------
 2 files changed, 76 insertions(+), 33 deletions(-)
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index cf2c374..d9aa6b6 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -104,6 +104,10 @@ commit or stash your changes before running 'git merge'.
 If no commit is given from the command line, merge the remote-tracking
 branches that the current branch is configured to use as its upstream.
 See also the configuration section of this manual page.
++
+When `FETCH_HEAD` (and no other commit) is specified, the branches
+recorded in the `.git/FETCH_HEAD` file by the previous invocation
+of `git fetch` for merging are merged to the current branch.
 
 
 PRE-MERGE CHECKS
diff --git a/builtin/merge.c b/builtin/merge.c
index c7d9d6e..42f9fcc 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -505,28 +505,6 @@ static void merge_name(const char *remote, struct strbuf *msg)
 		strbuf_release(&truname);
 	}
 
-	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;
-	}
-
 	if (remote_head->util) {
 		struct merge_remote_desc *desc;
 		desc = merge_remote_util(remote_head);
@@ -1090,6 +1068,60 @@ static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *mer
 		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,
@@ -1105,20 +1137,27 @@ static struct commit_list *collect_parents(struct commit *head_commit,
 
 	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;
-	}
 
-	remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
+	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) {
-		for (p = remoteheads; p; p = p->next)
-			merge_name(merge_remote_util(p->item)->name, autogen);
-
 		prepare_merge_message(autogen, merge_msg);
 		strbuf_release(autogen);
 	}
-- 
2.4.0-rc3-238-g36f5934
Previous: Junio C HamanoNext: Junio C Hamano
Message 21 of 42 in “[BUG] "git pull" will regress between 'master' and 'pu'”
  1. Junio C HamanoApr 19, 2015
  2. Jeff KingApr 19, 2015
  3. brian m. carlsonApr 19, 2015
  4. Jeff KingApr 19, 2015
  5. Junio C HamanoApr 20, 2015
  6. Jeff KingApr 20, 2015
  7. Junio C HamanoApr 20, 2015
  8. 00/14 Teach "git merge FETCH_HEAD" octopus mergesJunio C Hamano, Apr 26, 2015
  9. 01/14 merge: simplify code flowJunio C Hamano, Apr 26, 2015
  10. 02/14 t5520: style fixesJunio C Hamano, Apr 26, 2015
  11. 03/14 t5520: test pulling an octopus into an unborn branchJunio C Hamano, Apr 26, 2015
  12. 04/14 merge: clarify "pulling into void" special caseJunio C Hamano, Apr 26, 2015
  13. 05/14 merge: do not check argc to determine number of remote headsJunio C Hamano, Apr 26, 2015
  14. 06/14 merge: small leakfix and code simplificationJunio C Hamano, Apr 26, 2015
  15. 07/14 merge: clarify collect_parents() logicJunio C Hamano, Apr 26, 2015
  16. 08/14 merge: split reduce_parents() out of collect_parents()Junio C Hamano, Apr 26, 2015
  17. 09/14 merge: narrow scope of merge_namesJunio C Hamano, Apr 26, 2015
  18. 10/14 merge: extract prepare_merge_message() logic outJunio C Hamano, Apr 26, 2015
  19. 11/14 merge: make collect_parents() auto-generate the merge messageJunio C Hamano, Apr 26, 2015
  20. 12/14 merge: decide if we auto-generate the message early in collect_parents()Junio C Hamano, Apr 26, 2015
  21. 13/14 merge: handle FETCH_HEAD internallyJunio C Hamano, Apr 26, 2015
  22. 14/14 merge: deprecate 'git merge <message> HEAD <commit>' syntaxJunio C Hamano, Apr 26, 2015
  23. 00/15 Teach "git merge FETCH_HEAD" octopus mergesJunio C Hamano, Apr 29, 2015
  24. 01/15 merge: test the top-level merge driverJunio C Hamano, Apr 29, 2015
  25. 02/15 merge: simplify code flowJunio C Hamano, Apr 29, 2015
  26. 03/15 t5520: style fixesJunio C Hamano, Apr 29, 2015
  27. Paul TanMay 1, 2015
  28. Junio C HamanoMay 3, 2015
  29. 04/15 t5520: test pulling an octopus into an unborn branchJunio C Hamano, Apr 29, 2015
  30. 05/15 merge: clarify "pulling into void" special caseJunio C Hamano, Apr 29, 2015
  31. 06/15 merge: do not check argc to determine number of remote headsJunio C Hamano, Apr 29, 2015
  32. 07/15 merge: small leakfix and code simplificationJunio C Hamano, Apr 29, 2015
  33. 08/15 merge: clarify collect_parents() logicJunio C Hamano, Apr 29, 2015
  34. 09/15 merge: split reduce_parents() out of collect_parents()Junio C Hamano, Apr 29, 2015
  35. 10/15 merge: narrow scope of merge_namesJunio C Hamano, Apr 29, 2015
  36. 11/15 merge: extract prepare_merge_message() logic outJunio C Hamano, Apr 29, 2015
  37. 12/15 merge: make collect_parents() auto-generate the merge messageJunio C Hamano, Apr 29, 2015
  38. 13/15 merge: decide if we auto-generate the message early in collect_parents()Junio C Hamano, Apr 29, 2015
  39. 14/15 merge: handle FETCH_HEAD internallyJunio C Hamano, Apr 29, 2015
  40. 15/15 merge: deprecate 'git merge <message> HEAD <commit>' syntaxJunio C Hamano, Apr 29, 2015
  41. Junio C HamanoApr 20, 2015
  42. Johannes SchindelinApr 21, 2015

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.