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

[PATCH 8/8] apply: register conflicted stages to the index

From
Junio C Hamano <gitster@pobox.com>
Date
May 10, 2012, 06:02 UTC
Message-ID
<1336629745-22436-9-git-send-email-gitster@pobox.com>
In-Reply-To
<1336629745-22436-1-git-send-email-gitster@pobox.com>

Now we have all the necessary logic to fall back on three-way merge when the patch does not cleanly apply, insert the conflicted entries to the index as appropriate. This obviously triggers only when the "--index" option is used.

When we fall back to three-way merge and some of the merges fail, just like the case where the "--reject" option was specified and we had to write some "*.rej" files out for unapplicable patches, exit the command with non-zero status without showing the diffstat and summary. Otherwise they would make the list of problematic paths scroll off the display.

This wraps up the series, at least for now. We might want to also arrange to call into the rerere machinery to clean things up, but that is left for later updates.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/apply.c           | 65 +++++++++++++++++++++++++++++++++++----
 t/t4108-apply-threeway.sh | 78 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 137 insertions(+), 6 deletions(-)
 create mode 100755 t/t4108-apply-threeway.sh
diff --git a/builtin/apply.c b/builtin/apply.c
index e090e18..d8c201b 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -3130,6 +3130,9 @@ static int try_threeway_fallback(struct image *image, struct patch *patch,
 	if (get_sha1(patch->old_sha1_prefix, pre_sha1) ||
 	    read_blob_object(&buf, pre_sha1, patch->old_mode))
 		return error("repository lacks necessary blobs to fall back on 3-way merge.");
+
+	fprintf(stderr, "Falling back to three-way merge...\n");
+
 	img = strbuf_detach(&buf, &len);
 	prepare_image(&tmp_image, img, len, 1);
 	/* Apply the patch to get the post image */
@@ -3148,8 +3151,10 @@ static int try_threeway_fallback(struct image *image, struct patch *patch,
 	/* in-core three-way merge between post and our using pre as base */
 	status = three_way_merge(image, patch->new_name,
 				 pre_sha1, our_sha1, post_sha1);
-	if (status < 0)
+	if (status < 0) {
+		fprintf(stderr, "Failed to fall back on three-way merge...\n");
 		return status;
+	}
 
 	patch->did_threeway = 1;
 	if (status) {
@@ -3157,6 +3162,9 @@ static int try_threeway_fallback(struct image *image, struct patch *patch,
 		hashcpy(patch->threeway_stage[0], pre_sha1);
 		hashcpy(patch->threeway_stage[1], our_sha1);
 		hashcpy(patch->threeway_stage[2], post_sha1);
+		fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
+	} else {
+		fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
 	}
 	return 0;
 }
@@ -3702,6 +3710,27 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
 	die_errno(_("unable to write file '%s' mode %o"), path, mode);
 }
 
+static void add_conflicted_stages_file(struct patch *patch)
+{
+	int stage, namelen;
+	unsigned ce_size, mode;
+
+	if (!update_index)
+		return;
+	namelen = strlen(patch->new_name);
+	ce_size = cache_entry_size(namelen);
+	mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644);
+	for (stage = 1; stage < 4; stage++) {
+		struct cache_entry *ce = xcalloc(1, ce_size);
+		memcpy(ce->name, patch->new_name, namelen);
+		ce->ce_mode = create_ce_mode(mode);
+		ce->ce_flags = create_ce_flags(namelen, stage);
+		hashcpy(ce->sha1, patch->threeway_stage[stage - 1]);
+		if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
+			die(_("unable to add cache entry for %s"), patch->new_name);
+	}
+}
+
 static void create_file(struct patch *patch)
 {
 	char *path = patch->new_name;
@@ -3712,7 +3741,11 @@ static void create_file(struct patch *patch)
 	if (!mode)
 		mode = S_IFREG | 0644;
 	create_one_file(path, mode, buf, size);
-	add_index_file(path, mode, buf, size);
+
+	if (patch->conflicted_threeway)
+		add_conflicted_stages_file(patch);
+	else
+		add_index_file(path, mode, buf, size);
 }
 
 /* phase zero is to remove, phase one is to create */
@@ -3814,6 +3847,7 @@ static int write_out_results(struct patch *list)
 	int phase;
 	int errs = 0;
 	struct patch *l;
+	struct string_list cpath = STRING_LIST_INIT_DUP;
 
 	for (phase = 0; phase < 2; phase++) {
 		l = list;
@@ -3822,12 +3856,28 @@ static int write_out_results(struct patch *list)
 				errs = 1;
 			else {
 				write_out_one_result(l, phase);
-				if (phase == 1 && write_out_one_reject(l))
-					errs = 1;
+				if (phase == 1) {
+					if (write_out_one_reject(l))
+						errs = 1;
+					if (l->conflicted_threeway) {
+						string_list_append(&cpath, l->new_name);
+						errs = 1;
+					}
+				}
 			}
 			l = l->next;
 		}
 	}
+
+	if (cpath.nr) {
+		struct string_list_item *item;
+
+		sort_string_list(&cpath);
+		for_each_string_list_item(item, &cpath)
+			fprintf(stderr, "U %s\n", item->string);
+		string_list_clear(&cpath, 0);
+	}
+
 	return errs;
 }
 
@@ -3950,8 +4000,11 @@ static int apply_patch(int fd, const char *filename, int options)
 	    !apply_with_reject)
 		exit(1);
 
-	if (apply && write_out_results(list))
-		exit(1);
+	if (apply && write_out_results(list)) {
+		if (apply_with_reject)
+			exit(1);
+		return 1;
+	}
 
 	if (fake_ancestor)
 		build_fake_ancestor(list, fake_ancestor);
diff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh
new file mode 100755
index 0000000..521c114
--- /dev/null
+++ b/t/t4108-apply-threeway.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+test_description='git apply --threeway'
+
+. ./test-lib.sh
+
+create_file () {
+	for i
+	do
+		echo "$i"
+	done
+}
+
+sanitize_conflicted_diff () {
+	sed -e '
+		/^index /d
+		s/^\(+[<>][<>][<>][<>]*\) .*/\1/
+	'
+}
+
+test_expect_success setup '
+	test_tick &&
+	create_file >one 1 2 3 4 5 6 7 &&
+	cat one >two &&
+	git add one two &&
+	git commit -m initial &&
+
+	git branch side &&
+
+	test_tick &&
+	create_file >one 1 two 3 4 5 six 7 &&
+	create_file >two 1 two 3 4 5 6 7 &&
+	git commit -a -m master &&
+
+	git checkout side &&
+	create_file >one 1 2 3 4 five 6 7 &&
+	create_file >two 1 2 3 4 five 6 7 &&
+	git commit -a -m side &&
+
+	git checkout master
+'
+
+test_expect_success 'apply without --threeway' '
+	git diff side^ side >P.diff &&
+
+	# should fail to apply
+	git reset --hard &&
+	git checkout master^0 &&
+	test_must_fail git apply --index P.diff &&
+	# should leave things intact
+	git diff-files --exit-code &&
+	git diff-index --exit-code --cached HEAD
+'
+
+test_expect_success 'apply with --threeway' '
+	# Merging side should be similar to applying this patch
+	git diff ...side >P.diff &&
+
+	# The corresponding conflicted merge
+	git reset --hard &&
+	git checkout master^0 &&
+	test_must_fail git merge --no-commit side &&
+	git ls-files -s >expect.ls &&
+	git diff HEAD | sanitize_conflicted_diff >expect.diff &&
+
+	# should fail to apply
+	git reset --hard &&
+	git checkout master^0 &&
+	test_must_fail git apply --index --threeway P.diff &&
+	git ls-files -s >actual.ls &&
+	git diff HEAD | sanitize_conflicted_diff >actual.diff &&
+
+	# The result should resemble the corresponding merge
+	test_cmp expect.ls actual.ls &&
+	test_cmp expect.diff actual.diff
+'
+
+test_done
-- 
1.7.10.1.562.gfc79b1c
Previous: Junio C HamanoNext: Matthieu Moy
Message 17 of 19 in “"git apply --threeway"”
  1. 0/8 "git apply --threeway"Junio C Hamano, May 10, 2012
  2. 1/8 apply: reformat commentJunio C Hamano, May 10, 2012
  3. 2/8 apply: accept --threeway command line optionJunio C Hamano, May 10, 2012
  4. Nguyen Thai Ngoc DuyMay 10, 2012
  5. Junio C HamanoMay 10, 2012
  6. 3/8 apply: split load_preimage() helper function outJunio C Hamano, May 10, 2012
  7. 4/8 apply: clear_image() clears things a bit moreJunio C Hamano, May 10, 2012
  8. 5/8 apply: refactor read_file_or_gitlink()Junio C Hamano, May 10, 2012
  9. 6/8 apply: fall back on three-way mergeJunio C Hamano, May 10, 2012
  10. Matthieu MoyMay 10, 2012
  11. Junio C HamanoMay 10, 2012
  12. Matthieu MoyMay 10, 2012
  13. Jeff KingMay 10, 2012
  14. Junio C HamanoMay 10, 2012
  15. Jeff KingMay 10, 2012
  16. 7/8 apply: plug the three-way merge logic inJunio C Hamano, May 10, 2012
  17. 8/8 apply: register conflicted stages to the indexJunio C Hamano, May 10, 2012
  18. Matthieu MoyMay 10, 2012
  19. Junio C HamanoMay 10, 2012

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.