From 55d060771a15f4ce75e71b966d8117d8a32cc47b Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:38 +0100 Subject: (trivial) notes.h: Minor documentation fixes to copy_notes() Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- notes.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'notes.h') diff --git a/notes.h b/notes.h index 65fc3a66b2..c0288b0d32 100644 --- a/notes.h +++ b/notes.h @@ -104,6 +104,10 @@ const unsigned char *get_note(struct notes_tree *t, * Copy a note from one object to another in the given notes_tree. * * Fails if the to_obj already has a note unless 'force' is true. + * + * IMPORTANT: The changes made by copy_note() to the given notes_tree structure + * are not persistent until a subsequent call to write_notes_tree() returns + * zero. */ int copy_note(struct notes_tree *t, const unsigned char *from_obj, const unsigned char *to_obj, @@ -149,6 +153,7 @@ int copy_note(struct notes_tree *t, * notes tree) from within the callback: * - add_note() * - remove_note() + * - copy_note() * - free_notes() */ typedef int each_note_fn(const unsigned char *object_sha1, -- cgit v1.2.3 From 4a9cf1cefc0fd05e0eb46f862e398f1e4ac1c9a7 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:39 +0100 Subject: notes.h: Make default_notes_ref() available in notes API Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- notes.c | 2 +- notes.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'notes.h') diff --git a/notes.c b/notes.c index bb03eb0364..d71c0a30bf 100644 --- a/notes.c +++ b/notes.c @@ -898,7 +898,7 @@ static int notes_display_config(const char *k, const char *v, void *cb) return 0; } -static const char *default_notes_ref(void) +const char *default_notes_ref(void) { const char *notes_ref = NULL; if (!notes_ref) diff --git a/notes.h b/notes.h index c0288b0d32..20db42fe95 100644 --- a/notes.h +++ b/notes.h @@ -43,6 +43,20 @@ extern struct notes_tree { int dirty; } default_notes_tree; +/* + * Return the default notes ref. + * + * The default notes ref is the notes ref that is used when notes_ref == NULL + * is passed to init_notes(). + * + * This the first of the following to be defined: + * 1. The '--ref' option to 'git notes', if given + * 2. The $GIT_NOTES_REF environment variable, if set + * 3. The value of the core.notesRef config variable, if set + * 4. GIT_NOTES_DEFAULT_REF (i.e. "refs/notes/commits") + */ +const char *default_notes_ref(void); + /* * Flags controlling behaviour of notes tree initialization * -- cgit v1.2.3 From e2656c82fd836a3d410230c98f6a725368f15642 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Tue, 9 Nov 2010 22:49:41 +0100 Subject: notes.h/c: Allow combine_notes functions to remove notes Allow combine_notes functions to request that a note be removed, by setting the resulting note SHA1 to null_sha1 (0000000...). For consistency, also teach note_tree_insert() to skip insertion of an empty note (a note with entry->val_sha1 equal to null_sha1) when there is no note to combine it with. In general, an empty note (null_sha1) is treated identically to no note at all, but when adding an empty note where there already exists a non-empty note, we allow the combine_notes function to potentially record a new/changed note. Document this behaviour, and clearly specify how combine_notes functions are expected to handle null_sha1 in input. Before this patch, storing null_sha1s in the notes tree were silently allowed, causing an invalid notes tree (referring to blobs with null_sha1) to be produced by write_notes_tree(). Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- notes.c | 12 +++++++++++- notes.h | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'notes.h') diff --git a/notes.c b/notes.c index bfb3ea5b53..0c13a36eb7 100644 --- a/notes.c +++ b/notes.c @@ -248,7 +248,10 @@ static void note_tree_insert(struct notes_tree *t, struct int_node *tree, switch (GET_PTR_TYPE(*p)) { case PTR_TYPE_NULL: assert(!*p); - *p = SET_PTR_TYPE(entry, type); + if (is_null_sha1(entry->val_sha1)) + free(entry); + else + *p = SET_PTR_TYPE(entry, type); return; case PTR_TYPE_NOTE: switch (type) { @@ -264,6 +267,9 @@ static void note_tree_insert(struct notes_tree *t, struct int_node *tree, sha1_to_hex(l->val_sha1), sha1_to_hex(entry->val_sha1), sha1_to_hex(l->key_sha1)); + + if (is_null_sha1(l->val_sha1)) + note_tree_remove(t, tree, n, entry); free(entry); return; } @@ -295,6 +301,10 @@ static void note_tree_insert(struct notes_tree *t, struct int_node *tree, /* non-matching leaf_node */ assert(GET_PTR_TYPE(*p) == PTR_TYPE_NOTE || GET_PTR_TYPE(*p) == PTR_TYPE_SUBTREE); + if (is_null_sha1(entry->val_sha1)) { /* skip insertion of empty note */ + free(entry); + return; + } new_node = (struct int_node *) xcalloc(sizeof(struct int_node), 1); note_tree_insert(t, new_node, n + 1, l, GET_PTR_TYPE(*p), combine_notes); diff --git a/notes.h b/notes.h index 20db42fe95..79ea7976ab 100644 --- a/notes.h +++ b/notes.h @@ -12,7 +12,10 @@ * resulting SHA1 into the first SHA1 argument (cur_sha1). A non-zero return * value indicates failure. * - * The two given SHA1s must both be non-NULL and different from each other. + * The two given SHA1s shall both be non-NULL and different from each other. + * Either of them (but not both) may be == null_sha1, which indicates an + * empty/non-existent note. If the resulting SHA1 (cur_sha1) is == null_sha1, + * the note will be removed from the notes tree. * * The default combine_notes function (you get this when passing NULL) is * combine_notes_concatenate(), which appends the contents of the new note to @@ -90,6 +93,17 @@ void init_notes(struct notes_tree *t, const char *notes_ref, /* * Add the given note object to the given notes_tree structure * + * If there already exists a note for the given object_sha1, the given + * combine_notes function is invoked to break the tie. If not given (i.e. + * combine_notes == NULL), the default combine_notes function for the given + * notes_tree is used. + * + * Passing note_sha1 == null_sha1 indicates the addition of an + * empty/non-existent note. This is a (potentially expensive) no-op unless + * there already exists a note for the given object_sha1, AND combining that + * note with the empty note (using the given combine_notes function) results + * in a new/changed note. + * * IMPORTANT: The changes made by add_note() to the given notes_tree structure * are not persistent until a subsequent call to write_notes_tree() returns * zero. -- cgit v1.2.3 From 180619a58572b17de0ebeb96989e0aa832765186 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Mon, 15 Nov 2010 00:52:26 +0100 Subject: notes.h/c: Propagate combine_notes_fn return value to add_note() and beyond The combine_notes_fn functions uses a non-zero return value to indicate failure. However, this return value was converted to a call to die() in note_tree_insert(). Instead, propagate this return value out to add_note(), and return it from there to enable the caller to handle errors appropriately. Existing add_note() callers are updated to die() upon failure, thus preserving the current behaviour. The only exceptions are copy_note() and notes_cache_put() where we are able to propagate the add_note() return value instead. This patch has been improved by the following contributions: - Jonathan Nieder: Future-proof by always checking add_note() return value - Jonathan Nieder: Improve clarity of final if-condition in note_tree_insert() Thanks-to: Jonathan Nieder Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano --- builtin/notes.c | 11 ++++++----- notes-cache.c | 3 +-- notes.c | 55 ++++++++++++++++++++++++++++--------------------------- notes.h | 11 ++++++++--- 4 files changed, 43 insertions(+), 37 deletions(-) (limited to 'notes.h') diff --git a/builtin/notes.c b/builtin/notes.c index fbc347c9f0..51a11ba388 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -573,8 +573,8 @@ static int add(int argc, const char **argv, const char *prefix) if (is_null_sha1(new_note)) remove_note(t, object); - else - add_note(t, object, new_note, combine_notes_overwrite); + else if (add_note(t, object, new_note, combine_notes_overwrite)) + die("BUG: combine_notes_overwrite failed"); snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'", is_null_sha1(new_note) ? "removed" : "added", "add"); @@ -653,7 +653,8 @@ static int copy(int argc, const char **argv, const char *prefix) goto out; } - add_note(t, object, from_note, combine_notes_overwrite); + if (add_note(t, object, from_note, combine_notes_overwrite)) + die("BUG: combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes copy'"); out: free_notes(t); @@ -712,8 +713,8 @@ static int append_edit(int argc, const char **argv, const char *prefix) if (is_null_sha1(new_note)) remove_note(t, object); - else - add_note(t, object, new_note, combine_notes_overwrite); + else if (add_note(t, object, new_note, combine_notes_overwrite)) + die("BUG: combine_notes_overwrite failed"); snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'", is_null_sha1(new_note) ? "removed" : "added", argv[0]); diff --git a/notes-cache.c b/notes-cache.c index dee6d62e72..4c8984ede1 100644 --- a/notes-cache.c +++ b/notes-cache.c @@ -89,6 +89,5 @@ int notes_cache_put(struct notes_cache *c, unsigned char key_sha1[20], if (write_sha1_file(data, size, "blob", value_sha1) < 0) return -1; - add_note(&c->tree, key_sha1, value_sha1, NULL); - return 0; + return add_note(&c->tree, key_sha1, value_sha1, NULL); } diff --git a/notes.c b/notes.c index 0c13a36eb7..1674391d38 100644 --- a/notes.c +++ b/notes.c @@ -235,13 +235,14 @@ static void note_tree_remove(struct notes_tree *t, struct int_node *tree, * - Else, create a new int_node, holding both the node-at-location and the * node-to-be-inserted, and store the new int_node into the location. */ -static void note_tree_insert(struct notes_tree *t, struct int_node *tree, +static int note_tree_insert(struct notes_tree *t, struct int_node *tree, unsigned char n, struct leaf_node *entry, unsigned char type, combine_notes_fn combine_notes) { struct int_node *new_node; struct leaf_node *l; void **p = note_tree_search(t, &tree, &n, entry->key_sha1); + int ret = 0; assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */ l = (struct leaf_node *) CLR_PTR_TYPE(*p); @@ -252,26 +253,21 @@ static void note_tree_insert(struct notes_tree *t, struct int_node *tree, free(entry); else *p = SET_PTR_TYPE(entry, type); - return; + return 0; case PTR_TYPE_NOTE: switch (type) { case PTR_TYPE_NOTE: if (!hashcmp(l->key_sha1, entry->key_sha1)) { /* skip concatenation if l == entry */ if (!hashcmp(l->val_sha1, entry->val_sha1)) - return; + return 0; - if (combine_notes(l->val_sha1, entry->val_sha1)) - die("failed to combine notes %s and %s" - " for object %s", - sha1_to_hex(l->val_sha1), - sha1_to_hex(entry->val_sha1), - sha1_to_hex(l->key_sha1)); - - if (is_null_sha1(l->val_sha1)) + ret = combine_notes(l->val_sha1, + entry->val_sha1); + if (!ret && is_null_sha1(l->val_sha1)) note_tree_remove(t, tree, n, entry); free(entry); - return; + return ret; } break; case PTR_TYPE_SUBTREE: @@ -280,7 +276,7 @@ static void note_tree_insert(struct notes_tree *t, struct int_node *tree, /* unpack 'entry' */ load_subtree(t, entry, tree, n); free(entry); - return; + return 0; } break; } @@ -291,9 +287,8 @@ static void note_tree_insert(struct notes_tree *t, struct int_node *tree, *p = NULL; load_subtree(t, l, tree, n); free(l); - note_tree_insert(t, tree, n, entry, type, - combine_notes); - return; + return note_tree_insert(t, tree, n, entry, type, + combine_notes); } break; } @@ -303,13 +298,15 @@ static void note_tree_insert(struct notes_tree *t, struct int_node *tree, GET_PTR_TYPE(*p) == PTR_TYPE_SUBTREE); if (is_null_sha1(entry->val_sha1)) { /* skip insertion of empty note */ free(entry); - return; + return 0; } new_node = (struct int_node *) xcalloc(sizeof(struct int_node), 1); - note_tree_insert(t, new_node, n + 1, l, GET_PTR_TYPE(*p), - combine_notes); + ret = note_tree_insert(t, new_node, n + 1, l, GET_PTR_TYPE(*p), + combine_notes); + if (ret) + return ret; *p = SET_PTR_TYPE(new_node, PTR_TYPE_INTERNAL); - note_tree_insert(t, new_node, n + 1, entry, type, combine_notes); + return note_tree_insert(t, new_node, n + 1, entry, type, combine_notes); } /* Free the entire notes data contained in the given tree */ @@ -452,8 +449,12 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, l->key_sha1[19] = (unsigned char) len; type = PTR_TYPE_SUBTREE; } - note_tree_insert(t, node, n, l, type, - combine_notes_concatenate); + if (note_tree_insert(t, node, n, l, type, + combine_notes_concatenate)) + die("Failed to load %s %s into notes tree " + "from %s", + type == PTR_TYPE_NOTE ? "note" : "subtree", + sha1_to_hex(l->key_sha1), t->ref); } continue; @@ -1014,7 +1015,7 @@ void init_display_notes(struct display_notes_opt *opt) string_list_clear(&display_notes_refs, 0); } -void add_note(struct notes_tree *t, const unsigned char *object_sha1, +int add_note(struct notes_tree *t, const unsigned char *object_sha1, const unsigned char *note_sha1, combine_notes_fn combine_notes) { struct leaf_node *l; @@ -1028,7 +1029,7 @@ void add_note(struct notes_tree *t, const unsigned char *object_sha1, l = (struct leaf_node *) xmalloc(sizeof(struct leaf_node)); hashcpy(l->key_sha1, object_sha1); hashcpy(l->val_sha1, note_sha1); - note_tree_insert(t, t->root, 0, l, PTR_TYPE_NOTE, combine_notes); + return note_tree_insert(t, t->root, 0, l, PTR_TYPE_NOTE, combine_notes); } void remove_note(struct notes_tree *t, const unsigned char *object_sha1) @@ -1204,7 +1205,7 @@ void format_display_notes(const unsigned char *object_sha1, int copy_note(struct notes_tree *t, const unsigned char *from_obj, const unsigned char *to_obj, - int force, combine_notes_fn combine_fn) + int force, combine_notes_fn combine_notes) { const unsigned char *note = get_note(t, from_obj); const unsigned char *existing_note = get_note(t, to_obj); @@ -1213,9 +1214,9 @@ int copy_note(struct notes_tree *t, return 1; if (note) - add_note(t, to_obj, note, combine_fn); + return add_note(t, to_obj, note, combine_notes); else if (existing_note) - add_note(t, to_obj, null_sha1, combine_fn); + return add_note(t, to_obj, null_sha1, combine_notes); return 0; } diff --git a/notes.h b/notes.h index 79ea7976ab..b3725750bb 100644 --- a/notes.h +++ b/notes.h @@ -104,11 +104,13 @@ void init_notes(struct notes_tree *t, const char *notes_ref, * note with the empty note (using the given combine_notes function) results * in a new/changed note. * + * Returns zero on success; non-zero means combine_notes failed. + * * IMPORTANT: The changes made by add_note() to the given notes_tree structure * are not persistent until a subsequent call to write_notes_tree() returns * zero. */ -void add_note(struct notes_tree *t, const unsigned char *object_sha1, +int add_note(struct notes_tree *t, const unsigned char *object_sha1, const unsigned char *note_sha1, combine_notes_fn combine_notes); /* @@ -131,7 +133,10 @@ const unsigned char *get_note(struct notes_tree *t, /* * Copy a note from one object to another in the given notes_tree. * - * Fails if the to_obj already has a note unless 'force' is true. + * Returns 1 if the to_obj already has a note and 'force' is false. Otherwise, + * returns non-zero if 'force' is true, but the given combine_notes function + * failed to combine from_obj's note with to_obj's existing note. + * Returns zero on success. * * IMPORTANT: The changes made by copy_note() to the given notes_tree structure * are not persistent until a subsequent call to write_notes_tree() returns @@ -139,7 +144,7 @@ const unsigned char *get_note(struct notes_tree *t, */ int copy_note(struct notes_tree *t, const unsigned char *from_obj, const unsigned char *to_obj, - int force, combine_notes_fn combine_fn); + int force, combine_notes_fn combine_notes); /* * Flags controlling behaviour of for_each_note() -- cgit v1.2.3 From a6a09095a08339afc8468d053ff978ed4662a1d5 Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Mon, 15 Nov 2010 00:57:17 +0100 Subject: git notes merge: Add another auto-resolving strategy: "cat_sort_uniq" 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 Signed-off-by: Junio C Hamano --- Documentation/git-notes.txt | 12 ++- builtin/notes.c | 8 +- notes-merge.c | 7 ++ notes-merge.h | 3 +- notes.c | 76 +++++++++++++++++++ notes.h | 1 + t/t3309-notes-merge-auto-resolve.sh | 145 ++++++++++++++++++++++++++++++++++++ 7 files changed, 248 insertions(+), 4 deletions(-) (limited to 'notes.h') diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt index a82255119a..1de1417d82 100644 --- a/Documentation/git-notes.txt +++ b/Documentation/git-notes.txt @@ -155,7 +155,7 @@ OPTIONS --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 710a89da28..455046e88a 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 459b1c2bf2..71c4d45fcd 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -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); } diff --git a/notes-merge.h b/notes-merge.h index 195222f9da..168a6724cd 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 09a93abca1..96cde42134 100644 --- 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 b3725750bb..93a73657e2 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 1a1fc7edd3..461fd84755 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 <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 < 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 <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 < Non-conflicting 3-way merge' ' + git notes merge --strategy=cat_sort_uniq y && + verify_notes z cat_sort_uniq +' + test_done -- cgit v1.2.3