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

[WIP PATCH 05/10] merge-recursive: Fix sorting order and directory change assumptions

From
Elijah Newren <newren@gmail.com>
Date
Mar 21, 2011, 18:30 UTC
Message-ID
<1300732264-9638-6-git-send-email-newren@gmail.com>
In-Reply-To
<1300732264-9638-1-git-send-email-newren@gmail.com>

As pointed out in the review of my earlier series, we cannot assume that directory/file conflicts will appear in sorted order; for example, 'letters.txt' comes between 'letters' and 'letters/file'.

Also, we should not make changes to the working directory when merging merge-bases, so removing files to make room for directories of d/f conflicts should be skipped in such cases.

(These changes were made last September/October, but were mixed in with
an update of my series that I never got around to resubmitting.  Now that
the original series was accepted, I split these changes back out.)
---
 merge-recursive.c   |   38 +++++++++++++++++++++++++++++++++-----
 t/t6020-merge-df.sh |   26 ++++++++++++++++++--------
 2 files changed, 51 insertions(+), 13 deletions(-)
diff --git a/merge-recursive.c b/merge-recursive.c
index 1da385c..7a04ca7 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -344,6 +344,28 @@ static struct string_list *get_unmerged(void)
 	return unmerged;
 }
 
+static int string_list_df_name_compare(const void *a, const void *b)
+{
+	const struct string_list_item *one = a;
+	const struct string_list_item *two = b;
+	/*
+	 * Here we only care that entries for D/F conflicts are
+	 * adjacent, in particular with the file of the D/F conflict
+	 * appearing before files below the corresponding directory.
+	 * The order of the rest of the list is irrelevant for us.
+	 *
+	 * To achieve this, we sort with df_name_compare and provide
+	 * the mode S_IFDIR so that D/F conflicts will sort correctly.
+	 * We use the mode S_IFDIR for everything else for simplicity,
+	 * since in other cases any changes in their order due to
+	 * sorting cause no problems for us.
+	 */
+	return df_name_compare(one->string, strlen(one->string), S_IFDIR,
+			       two->string, strlen(two->string), S_IFDIR);
+}
+
+
+
 static void make_room_for_directories_of_df_conflicts(struct merge_options *o,
 						      struct string_list *entries)
 {
@@ -356,17 +378,23 @@ static void make_room_for_directories_of_df_conflicts(struct merge_options *o,
 	 * otherwise, if the file is not supposed to be removed by the
 	 * merge, the contents of the file will be placed in another
 	 * unique filename.
-	 *
-	 * NOTE: This function relies on the fact that entries for a
-	 * D/F conflict will appear adjacent in the index, with the
-	 * entries for the file appearing before entries for paths
-	 * below the corresponding directory.
 	 */
 	const char *last_file = NULL;
 	int last_len = 0;
 	struct stage_data *last_e;
 	int i;
 
+	/*
+	 * If we're merging merge-bases, we don't want to bother with
+	 * any working directory changes.
+	 */
+	if (o->call_depth)
+		return;
+
+	/* Ensure D/F conflicts are adjacent in the entries list. */
+	qsort(entries->items, entries->nr, sizeof(*entries->items),
+	      string_list_df_name_compare);
+
 	for (i = 0; i < entries->nr; i++) {
 		const char *path = entries->items[i].string;
 		int len = strlen(path);
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
index eec8f4e..27c3d73 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6020-merge-df.sh
@@ -59,15 +59,19 @@ test_expect_success 'setup modify/delete + directory/file conflict' '
 	git add letters &&
 	git commit -m initial &&
 
+	# Throw in letters.txt for sorting order fun
+	# ("letters.txt" sorts between "letters" and "letters/file")
 	echo i >>letters &&
-	git add letters &&
+	echo "version 2" >letters.txt &&
+	git add letters letters.txt &&
 	git commit -m modified &&
 
 	git checkout -b delete HEAD^ &&
 	git rm letters &&
 	mkdir letters &&
 	>letters/file &&
-	git add letters &&
+	echo "version 1" >letters.txt &&
+	git add letters letters.txt &&
 	git commit -m deleted
 '
 
@@ -75,25 +79,31 @@ test_expect_success 'modify/delete + directory/file conflict' '
 	git checkout delete^0 &&
 	test_must_fail git merge modify &&
 
-	test 3 = $(git ls-files -s | wc -l) &&
-	test 2 = $(git ls-files -u | wc -l) &&
-	test 1 = $(git ls-files -o | wc -l) &&
+	test 5 -eq $(git ls-files -s | wc -l) &&
+	test 4 -eq $(git ls-files -u | wc -l) &&
+	test 1 -eq $(git ls-files -o | wc -l) &&
 
 	test -f letters/file &&
+	test -f letters.txt &&
 	test -f letters~modify
 '
 
 test_expect_success 'modify/delete + directory/file conflict; other way' '
+	# Yes, we really need the double reset since "letters" appears as
+	# both a file and a directory.
+	git reset --hard &&
 	git reset --hard &&
 	git clean -f &&
 	git checkout modify^0 &&
+
 	test_must_fail git merge delete &&
 
-	test 3 = $(git ls-files -s | wc -l) &&
-	test 2 = $(git ls-files -u | wc -l) &&
-	test 1 = $(git ls-files -o | wc -l) &&
+	test 5 -eq $(git ls-files -s | wc -l) &&
+	test 4 -eq $(git ls-files -u | wc -l) &&
+	test 1 -eq $(git ls-files -o | wc -l) &&
 
 	test -f letters/file &&
+	test -f letters.txt &&
 	test -f letters~HEAD
 '
 
-- 
1.7.4
Previous: Elijah NewrenNext: Elijah Newren
Message 6 of 11 in “Work-in-progress merge-recursive work”
  1. 00/10 Work-in-progress merge-recursive workElijah Newren, Mar 21, 2011
  2. 01/10 merge-recursive: Remove redundant check for removing rename sourceElijah Newren, Mar 21, 2011
  3. 02/10 Reminder to fix o->call_depth handling in conflict_rename_rename_1to2Elijah Newren, Mar 21, 2011
  4. 03/10 A bunch of fixes and FIXMEsElijah Newren, Mar 21, 2011
  5. 04/10 Correct a commentElijah Newren, Mar 21, 2011
  6. 05/10 merge-recursive: Fix sorting order and directory change assumptionsElijah Newren, Mar 21, 2011
  7. 06/10 Add a comment pointing out a bugElijah Newren, Mar 21, 2011
  8. 07/10 Good testcasesElijah Newren, Mar 21, 2011
  9. 08/10 More test scriptsElijah Newren, Mar 21, 2011
  10. 09/10 Tests and fixes associated with rename/rename conflictsElijah Newren, Mar 21, 2011
  11. 10/10 Add new testcase (temp14) showing how undetected renames can cause or spuriously avoid merge conflictsElijah Newren, Mar 21, 2011

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.