]> git.ipfire.org Git - thirdparty/git.git/commitdiff
git notes merge: Add another auto-resolving strategy: "cat_sort_uniq"
authorJohan Herland <johan@herland.net>
Sun, 14 Nov 2010 23:57:17 +0000 (00:57 +0100)
committerJunio C Hamano <gitster@pobox.com>
Wed, 17 Nov 2010 21:22:53 +0000 (13:22 -0800)
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>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-notes.txt
builtin/notes.c
notes-merge.c
notes-merge.h
notes.c
notes.h
t/t3309-notes-merge-auto-resolve.sh

index a82255119a853772868bd3885567ab965998bed1..1de1417d82dac2430631b74b2a92d5fd66f6f397 100644 (file)
@@ -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
 --------
index 710a89da28bf50c0002f3658f90f674db2e42818..455046e88aa19bdb6963a283283f1fc2ab71152a 100644 (file)
@@ -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);
index 459b1c2bf2c4e8a849f9860430bc643a6152b9f5..71c4d45fcd1ac49630bdb8bc7b0aec658011c21b 100644 (file)
@@ -453,6 +453,13 @@ static int merge_one_change(struct notes_merge_options *o,
                        die("failed to concatenate notes "
                            "(combine_notes_concatenate)");
                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("failed to concatenate notes "
+                           "(combine_notes_cat_sort_uniq)");
+               return 0;
        }
        die("Unknown strategy (%i).", o->strategy);
 }
index 195222f9dad9c245c152eef1871f579dc470f6f4..168a6724cd873602e3da1ff3b7e2cc67a5b240ae 100644 (file)
@@ -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 09a93abca1244f7ce699f8630aaf69b5b15247ce..96cde4213403c6b77ca5490b459db5fad17776be 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -845,6 +845,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 b3725750bbfbdef80f73420e29eb666dab3fe9b9..93a73657e249734dc7cd0fea779c12fc102f0557 100644 (file)
--- 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
index 1a1fc7edd3ef8c8084d8b39b6c039c7ffc89dd3b..461fd84755d7acc0df346903e6ac1a74d4a9e458 100755 (executable)
@@ -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