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

[PATCHv4 19/21] git notes merge: Add another auto-resolving strategy: "cat_sort_uniq"

From
Johan Herland <johan@herland.net>
Date
Oct 21, 2010, 02:08 UTC
Message-ID
<1287626936-32232-20-git-send-email-johan@herland.net>
In-Reply-To
<1287626936-32232-1-git-send-email-johan@herland.net>

This new strategy is similar to "concatenate", but in addition to concatenating the two note candidates, this strategy sorts the resulting lines, and removes duplicate lines from the result. This is equivalent to applying the "cat | sort | uniq" shell pipeline to the two note candidates.

This strategy is useful if the notes follow a line-based format where one wants to avoid duplicate lines in the merge result.

Note that if either of the note candidates contain duplicate lines _prior_ to the merge, these will also be removed by this merge strategy.

The patch also contains tests and documentation for the new strategy.
Signed-off-by: Johan Herland <johan@herland.net>
---
 Documentation/git-notes.txt         |   12 +++-
 builtin/notes.c                     |    8 ++-
 notes-merge.c                       |    6 ++
 notes-merge.h                       |    3 +-
 notes.c                             |   76 ++++++++++++++++++
 notes.h                             |    1 +
 t/t3309-notes-merge-auto-resolve.sh |  145 +++++++++++++++++++++++++++++++++++
 7 files changed, 247 insertions(+), 4 deletions(-)
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index 5bb9784..466885a 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -155,7 +155,7 @@ OPTIONS
 --strategy=<strategy>::
 	When merging notes, resolve notes conflicts using the given
 	strategy. The following strategies are recognized: "manual"
-	(default), "ours", "theirs" and "union".
+	(default), "ours", "theirs", "union" and "cat_sort_uniq".
 	See the "NOTES MERGE STRATEGIES" section below for more
 	information on each notes merge strategy.
 
@@ -230,6 +230,16 @@ ref).
 "union" automatically resolves notes conflicts by concatenating the
 local and remote versions.
 
+"cat_sort_uniq" is similar to "union", but in addition to concatenating
+the local and remote versions, this strategy also sorts the resulting
+lines, and removes duplicate lines from the result. This is equivalent
+to applying the "cat | sort | uniq" shell pipeline to the local and
+remote versions. This strategy is useful if the notes follow a line-based
+format where one wants to avoid duplicated lines in the merge result.
+Note that if either the local or remote version contain duplicate lines
+prior to the merge, these will also be removed by this notes merge
+strategy.
+
 
 EXAMPLES
 --------
diff --git a/builtin/notes.c b/builtin/notes.c
index d3754ab..abf26e5 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -324,6 +324,8 @@ combine_notes_fn parse_combine_notes_fn(const char *v)
 		return combine_notes_ignore;
 	else if (!strcasecmp(v, "concatenate"))
 		return combine_notes_concatenate;
+	else if (!strcasecmp(v, "cat_sort_uniq"))
+		return combine_notes_cat_sort_uniq;
 	else
 		return NULL;
 }
@@ -846,8 +848,8 @@ static int merge(int argc, const char **argv, const char *prefix)
 		OPT__VERBOSITY(&verbosity),
 		OPT_GROUP("Merge options"),
 		OPT_STRING('s', "strategy", &strategy, "strategy",
-			   "resolve notes conflicts using the given "
-			   "strategy (manual/ours/theirs/union)"),
+			   "resolve notes conflicts using the given strategy "
+			   "(manual/ours/theirs/union/cat_sort_uniq)"),
 		OPT_GROUP("Committing unmerged notes"),
 		{ OPTION_BOOLEAN, 0, "commit", &do_commit, NULL,
 			"finalize notes merge by committing unmerged notes",
@@ -899,6 +901,8 @@ static int merge(int argc, const char **argv, const char *prefix)
 			o.strategy = NOTES_MERGE_RESOLVE_THEIRS;
 		else if (!strcmp(strategy, "union"))
 			o.strategy = NOTES_MERGE_RESOLVE_UNION;
+		else if (!strcmp(strategy, "cat_sort_uniq"))
+			o.strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;
 		else {
 			error("Unknown -s/--strategy: %s", strategy);
 			usage_with_options(git_notes_merge_usage, options);
diff --git a/notes-merge.c b/notes-merge.c
index c7659ce..c32979e 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -452,6 +452,12 @@ static int merge_one_change(struct notes_merge_options *o,
 		if (add_note(t, p->obj, p->remote, combine_notes_concatenate))
 			die("confused: combine_notes_concatenate failed");
 		return 0;
+	case NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ:
+		OUTPUT(o, 2, "Concatenating unique lines in local and remote "
+		       "notes for %s", sha1_to_hex(p->obj));
+		if (add_note(t, p->obj, p->remote, combine_notes_cat_sort_uniq))
+			die("confused: combine_notes_cat_sort_uniq failed");
+		return 0;
 	}
 	die("Unknown strategy (%i).", o->strategy);
 }
diff --git a/notes-merge.h b/notes-merge.h
index f9988cd..1bb9cb7 100644
--- a/notes-merge.h
+++ b/notes-merge.h
@@ -17,7 +17,8 @@ struct notes_merge_options {
 		NOTES_MERGE_RESOLVE_MANUAL = 0,
 		NOTES_MERGE_RESOLVE_OURS,
 		NOTES_MERGE_RESOLVE_THEIRS,
-		NOTES_MERGE_RESOLVE_UNION
+		NOTES_MERGE_RESOLVE_UNION,
+		NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ
 	} strategy;
 	unsigned has_worktree:1;
 };
diff --git a/notes.c b/notes.c
index a7a901a..521f5d0 100644
--- a/notes.c
+++ b/notes.c
@@ -847,6 +847,82 @@ int combine_notes_ignore(unsigned char *cur_sha1,
 	return 0;
 }
 
+static int string_list_add_note_lines(struct string_list *sort_uniq_list,
+				      const unsigned char *sha1)
+{
+	char *data;
+	unsigned long len;
+	enum object_type t;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf **lines = NULL;
+	int i, list_index;
+
+	if (is_null_sha1(sha1))
+		return 0;
+
+	/* read_sha1_file NUL-terminates */
+	data = read_sha1_file(sha1, &t, &len);
+	if (t != OBJ_BLOB || !data || !len) {
+		free(data);
+		return t != OBJ_BLOB || !data;
+	}
+
+	strbuf_attach(&buf, data, len, len + 1);
+	lines = strbuf_split(&buf, '\n');
+
+	for (i = 0; lines[i]; i++) {
+		if (lines[i]->buf[lines[i]->len - 1] == '\n')
+			strbuf_setlen(lines[i], lines[i]->len - 1);
+		if (!lines[i]->len)
+			continue; /* skip empty lines */
+		list_index = string_list_find_insert_index(sort_uniq_list,
+							   lines[i]->buf, 0);
+		if (list_index < 0)
+			continue; /* skip duplicate lines */
+		string_list_insert_at_index(sort_uniq_list, list_index,
+					    lines[i]->buf);
+	}
+
+	strbuf_list_free(lines);
+	strbuf_release(&buf);
+	return 0;
+}
+
+static int string_list_join_lines_helper(struct string_list_item *item,
+					 void *cb_data)
+{
+	struct strbuf *buf = cb_data;
+	strbuf_addstr(buf, item->string);
+	strbuf_addch(buf, '\n');
+	return 0;
+}
+
+int combine_notes_cat_sort_uniq(unsigned char *cur_sha1,
+		const unsigned char *new_sha1)
+{
+	struct string_list sort_uniq_list = { NULL, 0, 0, 1 };
+	struct strbuf buf = STRBUF_INIT;
+	int ret = 1;
+
+	/* read both note blob objects into unique_lines */
+	if (string_list_add_note_lines(&sort_uniq_list, cur_sha1))
+		goto out;
+	if (string_list_add_note_lines(&sort_uniq_list, new_sha1))
+		goto out;
+
+	/* create a new blob object from sort_uniq_list */
+	if (for_each_string_list(&sort_uniq_list,
+				 string_list_join_lines_helper, &buf))
+		goto out;
+
+	ret = write_sha1_file(buf.buf, buf.len, blob_type, cur_sha1);
+
+out:
+	strbuf_release(&buf);
+	string_list_clear(&sort_uniq_list, 0);
+	return ret;
+}
+
 static int string_list_add_one_ref(const char *path, const unsigned char *sha1,
 				   int flag, void *cb)
 {
diff --git a/notes.h b/notes.h
index b372575..93a7365 100644
--- a/notes.h
+++ b/notes.h
@@ -27,6 +27,7 @@ typedef int (*combine_notes_fn)(unsigned char *cur_sha1, const unsigned char *ne
 int combine_notes_concatenate(unsigned char *cur_sha1, const unsigned char *new_sha1);
 int combine_notes_overwrite(unsigned char *cur_sha1, const unsigned char *new_sha1);
 int combine_notes_ignore(unsigned char *cur_sha1, const unsigned char *new_sha1);
+int combine_notes_cat_sort_uniq(unsigned char *cur_sha1, const unsigned char *new_sha1);
 
 /*
  * Notes tree object
diff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh
index 1a1fc7e..461fd84 100755
--- a/t/t3309-notes-merge-auto-resolve.sh
+++ b/t/t3309-notes-merge-auto-resolve.sh
@@ -499,4 +499,149 @@ test_expect_success 'merge z into y with "union" strategy => Non-conflicting 3-w
 	verify_notes y union
 '
 
+test_expect_success 'reset to pre-merge state (y)' '
+	git update-ref refs/notes/y refs/notes/y^1 &&
+	# Verify pre-merge state
+	verify_notes y y
+'
+
+cat <<EOF | sort >expect_notes_union2
+d682107b8bf7a7aea1e537a8d5cb6a12b60135f1 $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+357b6ca14c7afd59b7f8b8aaaa6b8b723771135b $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_union2 <<EOF
+$commit_sha15 15th
+z notes on 15th commit
+
+y notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+z notes on 5th commit
+
+y notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge y into z with "union" strategy => Non-conflicting 3-way merge' '
+	git config core.notesRef refs/notes/z &&
+	git notes merge --strategy=union y &&
+	verify_notes z union2
+'
+
+test_expect_success 'reset to pre-merge state (z)' '
+	git update-ref refs/notes/z refs/notes/z^1 &&
+	# Verify pre-merge state
+	verify_notes z z
+'
+
+cat <<EOF | sort >expect_notes_cat_sort_uniq
+6be90240b5f54594203e25d9f2f64b7567175aee $commit_sha15
+5de7ea7ad4f47e7ff91989fb82234634730f75df $commit_sha14
+3a631fdb6f41b05b55d8f4baf20728ba8f6fccbc $commit_sha13
+a66055fa82f7a03fe0c02a6aba3287a85abf7c62 $commit_sha12
+7e3c53503a3db8dd996cb62e37c66e070b44b54d $commit_sha11
+b8d03e173f67f6505a76f6e00cf93440200dd9be $commit_sha10
+851e1638784a884c7dd26c5d41f3340f6387413a $commit_sha8
+660311d7f78dc53db12ac373a43fca7465381a7e $commit_sha5
+e2bfd06a37dd2031684a59a6e2b033e212239c78 $commit_sha4
+5772f42408c0dd6f097a7ca2d24de0e78d1c46b1 $commit_sha3
+283b48219aee9a4105f6cab337e789065c82c2b9 $commit_sha2
+EOF
+
+cat >expect_log_cat_sort_uniq <<EOF
+$commit_sha15 15th
+y notes on 15th commit
+z notes on 15th commit
+
+$commit_sha14 14th
+y notes on 14th commit
+
+$commit_sha13 13th
+y notes on 13th commit
+
+$commit_sha12 12th
+y notes on 12th commit
+
+$commit_sha11 11th
+z notes on 11th commit
+
+$commit_sha10 10th
+x notes on 10th commit
+
+$commit_sha9 9th
+
+$commit_sha8 8th
+z notes on 8th commit
+
+$commit_sha7 7th
+
+$commit_sha6 6th
+
+$commit_sha5 5th
+y notes on 5th commit
+z notes on 5th commit
+
+$commit_sha4 4th
+y notes on 4th commit
+
+$commit_sha3 3rd
+y notes on 3rd commit
+
+$commit_sha2 2nd
+z notes on 2nd commit
+
+$commit_sha1 1st
+
+EOF
+
+test_expect_success 'merge y into z with "cat_sort_uniq" strategy => Non-conflicting 3-way merge' '
+	git notes merge --strategy=cat_sort_uniq y &&
+	verify_notes z cat_sort_uniq
+'
+
 test_done
-- 
1.7.3.98.g5ad7d9
Previous: Johan HerlandNext: Johan Herland
Message 26 of 43 in “[PATCHv4 00/21] git notes merge”
  1. Johan HerlandOct 21, 2010
  2. 01/21 notes.c: Hexify SHA1 in die() message from init_notes()Johan Herland, Oct 21, 2010
  3. 02/21 (trivial) notes.h: Minor documentation fixes to copy_notes()Johan Herland, Oct 21, 2010
  4. 03/21 notes.h: Make default_notes_ref() available in notes APIJohan Herland, Oct 21, 2010
  5. 04/21 notes.c: Reorder functions in preparation for next commitJohan Herland, Oct 21, 2010
  6. 05/21 notes.h/c: Clarify the handling of notes objects that are == null_sha1Johan Herland, Oct 21, 2010
  7. Jonathan NiederOct 21, 2010
  8. Johan HerlandOct 21, 2010
  9. Jonathan NiederOct 21, 2010
  10. Johan HerlandOct 22, 2010
  11. 06/21 notes.h/c: Propagate combine_notes_fn return value to add_note() and beyondJohan Herland, Oct 21, 2010
  12. Jonathan NiederOct 21, 2010
  13. Johan HerlandOct 21, 2010
  14. 07/21 (trivial) t3303: Indent with tabs instead of spaces for consistencyJohan Herland, Oct 21, 2010
  15. 08/21 notes.c: Use two newlines (instead of one) when concatenating notesJohan Herland, Oct 21, 2010
  16. 09/21 builtin/notes.c: Split notes ref DWIMmery into a separate functionJohan Herland, Oct 21, 2010
  17. 10/21 git notes merge: Initial implementation handling trivial merges onlyJohan Herland, Oct 21, 2010
  18. 11/21 builtin/notes.c: Refactor creation of notes commits.Johan Herland, Oct 21, 2010
  19. 12/21 git notes merge: Handle real, non-conflicting notes mergesJohan Herland, Oct 21, 2010
  20. 13/21 git notes merge: Add automatic conflict resolvers (ours, theirs, union)Johan Herland, Oct 21, 2010
  21. 14/21 Documentation: Preliminary docs on 'git notes merge'Johan Herland, Oct 21, 2010
  22. 15/21 git notes merge: Manual conflict resolution, part 1/2Johan Herland, Oct 21, 2010
  23. 16/21 git notes merge: Manual conflict resolution, part 2/2Johan Herland, Oct 21, 2010
  24. 17/21 git notes merge: List conflicting notes in notes merge commit messageJohan Herland, Oct 21, 2010
  25. 18/21 git notes merge: --commit should fail if underlying notes ref has movedJohan Herland, Oct 21, 2010
  26. 19/21 git notes merge: Add another auto-resolving strategy: "cat_sort_uniq"Johan Herland, Oct 21, 2010
  27. 20/21 git notes merge: Add testcases for merging notes trees at different fanoutsJohan Herland, Oct 21, 2010
  28. 21/21 Provide 'git notes get-ref' to easily retrieve current notes refJohan Herland, Oct 21, 2010
  29. Sverre RabbelierOct 21, 2010
  30. Junio C HamanoOct 21, 2010
  31. Jonathan NiederOct 21, 2010
  32. git merge --abort? [was: Re: [PATCHv4 00/21] git notes merge]Johan Herland, Oct 22, 2010
  33. Jonathan NiederOct 22, 2010
  34. Johan HerlandOct 22, 2010
  35. Sverre RabbelierOct 22, 2010
  36. Jonathan NiederOct 22, 2010
  37. Sverre RabbelierOct 22, 2010
  38. Johan HerlandOct 22, 2010
  39. Sverre RabbelierOct 22, 2010
  40. Johan HerlandOct 23, 2010
  41. Sverre RabbelierOct 23, 2010
  42. Johan HerlandOct 22, 2010
  43. Sverre RabbelierOct 23, 2010

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.