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

[PATCH] merge-recursive: handle file mode and links similarly to file content

From
Clemens Buchacher <drizzd@aon.at>
Date
Mar 14, 2008, 09:21 UTC
Message-ID
<20080314092105.GA18683@localhost>
In-Reply-To
<alpine.LSU.1.00.0803140039250.4174@racer.site>

If the file mode or link changed in only one branch, keep the changed version. If the file mode or link changed differently in both branches, report a conflict. If this happens, the user is more likely to be aware of the change in the head branch. Choose the remote version by default, in order to make the user think about the change.

Signed-off-by: Clemens Buchacher <drizzd@aon.at>
---
On Fri, Mar 14, 2008 at 12:40:05AM +0100, Johannes Schindelin wrote:
Show 8 quoted lines
> On Thu, 13 Mar 2008, Clemens Buchacher wrote:
> > +		result.merge = !result.clean;
> 
> That is new.  Doesn't this overwrite what has been set in
> 
>         } else {
>                 if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1))
>                         result.merge = 1;

Yeah, that's no good. I think I understand the meaning of result.merge and result.clean now.

result.merge indicates that there are changes in both branches, whereas result.clean indicates that the merge was trivial.

I amended the patch to reflect this.

I also noticed that in case of LINKs or GITLINKs which changed in both branches, the head version is kept by default. By the same rationale I gave for the file modes, I think the remote version should be kept instead, in order to make the user aware of this change.

Regards, Clemens

 merge-recursive.c          |   34 +++++++++++++++++++++++-----------
 t/t6031-merge-recursive.sh |   39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+), 11 deletions(-)
 create mode 100755 t/t6031-merge-recursive.sh
diff --git a/merge-recursive.c b/merge-recursive.c
index 34e3167..36f78a2 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1025,16 +1025,31 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
 			hashcpy(result.sha, b->sha1);
 		}
 	} else {
-		if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1))
-			result.merge = 1;
+		/*
+		 * If the file changed in only one branch, keep the changed
+		 * version. If the file changed in both, try to merge
+		 * automatically. If the merge is not trivial, report a
+		 * conflict. In case of conflicting file modes or links, choose
+		 * remote version by default. They can only be merged trivially
+		 * if they are equal.
+		 */
 
-		result.mode = a->mode == o->mode ? b->mode: a->mode;
+		if (a->mode != o->mode && b->mode != o->mode) {
+			result.mode = b->mode;
+			if (a->mode != b->mode)
+				result.clean = 0;
+			result.merge = 1;
+		} else
+			result.mode = o->mode == a->mode ? b->mode : a->mode;
 
 		if (sha_eq(a->sha1, o->sha1))
 			hashcpy(result.sha, b->sha1);
 		else if (sha_eq(b->sha1, o->sha1))
 			hashcpy(result.sha, a->sha1);
-		else if (S_ISREG(a->mode)) {
+		else if (sha_eq(a->sha1, b->sha1)) {
+			hashcpy(result.sha, a->sha1);
+			result.merge = 1;
+		} else if (S_ISREG(a->mode)) {
 			mmbuffer_t result_buf;
 			int merge_status;
 
@@ -1051,14 +1066,11 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
 
 			free(result_buf.ptr);
 			result.clean = (merge_status == 0);
-		} else if (S_ISGITLINK(a->mode)) {
+			result.merge = 1;
+		} else if (S_ISGITLINK(a->mode) || S_ISLNK(a->mode)) {
+			hashcpy(result.sha, b->sha1);
 			result.clean = 0;
-			hashcpy(result.sha, a->sha1);
-		} else if (S_ISLNK(a->mode)) {
-			hashcpy(result.sha, a->sha1);
-
-			if (!sha_eq(a->sha1, b->sha1))
-				result.clean = 0;
+			result.merge = 1;
 		} else {
 			die("unsupported object type in the tree");
 		}
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
new file mode 100755
index 0000000..36cd664
--- /dev/null
+++ b/t/t6031-merge-recursive.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='merge-recursive: handle file mode'
+. ./test-lib.sh
+
+test_expect_success 'mode change in one branch: keep changed version' '
+	: >file1 &&
+	git add file1 &&
+	git commit -m initial &&
+	git checkout -b a1 master &&
+	: >dummy &&
+	git add dummy &&
+	git commit -m a &&
+	git checkout -b b1 master &&
+	chmod +x file1 &&
+	git add file1 &&
+	git commit -m b1 &&
+	git checkout a1 &&
+	git merge-recursive master -- a1 b1 &&
+	test -x file1
+'
+
+test_expect_success 'mode change in both branches: expect conflict' '
+	git reset --hard HEAD &&
+	git checkout -b a2 master &&
+	: >file2 &&
+	chmod +x file2 &&
+	git add file2 &&
+	git commit -m a2 &&
+	git checkout -b b2 master &&
+	: >file2 &&
+	git add file2 &&
+	git commit -m b2 &&
+	git checkout a2 &&
+	! (git merge-recursive master -- a2 b2 || test $? -ne 1) &&
+	! test -x file2
+'
+
+test_done
-- 
1.5.4.4
Previous: Johannes SchindelinNext: Clemens Buchacher
Message 11 of 23 in “fix recursive-merge of empty files with different permissions”
  1. fix recursive-merge of empty files with different permissionsClemens Buchacher, Mar 8, 2008
  2. Johannes SchindelinMar 8, 2008
  3. Clemens BuchacherMar 13, 2008
  4. Johannes SchindelinMar 13, 2008
  5. Junio C HamanoMar 13, 2008
  6. Johannes SchindelinMar 13, 2008
  7. merge-recursive: cause a conflict if file mode does not matchClemens Buchacher, Mar 13, 2008
  8. Johannes SchindelinMar 13, 2008
  9. merge-recursive: handle file mode changesClemens Buchacher, Mar 13, 2008
  10. Johannes SchindelinMar 13, 2008
  11. merge-recursive: handle file mode and links similarly to file contentClemens Buchacher, Mar 14, 2008
  12. Clemens BuchacherMar 14, 2008
  13. Junio C HamanoMar 14, 2008
  14. Junio C HamanoMar 14, 2008
  15. Clemens BuchacherMar 14, 2008
  16. Jakub NarebskiMar 14, 2008
  17. Clemens BuchacherMar 14, 2008
  18. Junio C HamanoMar 14, 2008
  19. Clemens BuchacherMar 14, 2008
  20. Junio C HamanoMar 14, 2008
  21. Clemens BuchacherMar 14, 2008
  22. Junio C HamanoMar 14, 2008
  23. Clemens BuchacherMar 14, 2008

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.