diff options
Diffstat (limited to 'notes.c')
-rw-r--r-- | notes.c | 347 |
1 files changed, 213 insertions, 134 deletions
@@ -150,86 +150,6 @@ static struct leaf_node *note_tree_find(struct notes_tree *t, } /* - * To insert a leaf_node: - * Search to the tree location appropriate for the given leaf_node's key: - * - If location is unused (NULL), store the tweaked pointer directly there - * - If location holds a note entry that matches the note-to-be-inserted, then - * combine the two notes (by calling the given combine_notes function). - * - If location holds a note entry that matches the subtree-to-be-inserted, - * then unpack the subtree-to-be-inserted into the location. - * - If location holds a matching subtree entry, unpack the subtree at that - * location, and restart the insert operation from that level. - * - 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, - 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); - - assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */ - l = (struct leaf_node *) CLR_PTR_TYPE(*p); - switch (GET_PTR_TYPE(*p)) { - case PTR_TYPE_NULL: - assert(!*p); - *p = SET_PTR_TYPE(entry, type); - return; - 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; - - 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)); - free(entry); - return; - } - break; - case PTR_TYPE_SUBTREE: - if (!SUBTREE_SHA1_PREFIXCMP(l->key_sha1, - entry->key_sha1)) { - /* unpack 'entry' */ - load_subtree(t, entry, tree, n); - free(entry); - return; - } - break; - } - break; - case PTR_TYPE_SUBTREE: - if (!SUBTREE_SHA1_PREFIXCMP(entry->key_sha1, l->key_sha1)) { - /* unpack 'l' and restart insert */ - *p = NULL; - load_subtree(t, l, tree, n); - free(l); - note_tree_insert(t, tree, n, entry, type, - combine_notes); - return; - } - break; - } - - /* non-matching leaf_node */ - assert(GET_PTR_TYPE(*p) == PTR_TYPE_NOTE || - GET_PTR_TYPE(*p) == PTR_TYPE_SUBTREE); - 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); - *p = SET_PTR_TYPE(new_node, PTR_TYPE_INTERNAL); - note_tree_insert(t, new_node, n + 1, entry, type, combine_notes); -} - -/* * How to consolidate an int_node: * If there are > 1 non-NULL entries, give up and return non-zero. * Otherwise replace the int_node at the given index in the given parent node @@ -263,11 +183,13 @@ static int note_tree_consolidate(struct int_node *tree, * To remove a leaf_node: * Search to the tree location appropriate for the given leaf_node's key: * - If location does not hold a matching entry, abort and do nothing. + * - Copy the matching entry's value into the given entry. * - Replace the matching leaf_node with a NULL entry (and free the leaf_node). * - Consolidate int_nodes repeatedly, while walking up the tree towards root. */ -static void note_tree_remove(struct notes_tree *t, struct int_node *tree, - unsigned char n, struct leaf_node *entry) +static void note_tree_remove(struct notes_tree *t, + struct int_node *tree, unsigned char n, + struct leaf_node *entry) { struct leaf_node *l; struct int_node *parent_stack[20]; @@ -282,6 +204,7 @@ static void note_tree_remove(struct notes_tree *t, struct int_node *tree, return; /* key mismatch, nothing to remove */ /* we have found a matching entry */ + hashcpy(entry->val_sha1, l->val_sha1); free(l); *p = SET_PTR_TYPE(NULL, PTR_TYPE_NULL); @@ -302,6 +225,93 @@ static void note_tree_remove(struct notes_tree *t, struct int_node *tree, i--; } +/* + * To insert a leaf_node: + * Search to the tree location appropriate for the given leaf_node's key: + * - If location is unused (NULL), store the tweaked pointer directly there + * - If location holds a note entry that matches the note-to-be-inserted, then + * combine the two notes (by calling the given combine_notes function). + * - If location holds a note entry that matches the subtree-to-be-inserted, + * then unpack the subtree-to-be-inserted into the location. + * - If location holds a matching subtree entry, unpack the subtree at that + * location, and restart the insert operation from that level. + * - 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 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); + switch (GET_PTR_TYPE(*p)) { + case PTR_TYPE_NULL: + assert(!*p); + if (is_null_sha1(entry->val_sha1)) + free(entry); + else + *p = SET_PTR_TYPE(entry, type); + 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 0; + + 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 ret; + } + break; + case PTR_TYPE_SUBTREE: + if (!SUBTREE_SHA1_PREFIXCMP(l->key_sha1, + entry->key_sha1)) { + /* unpack 'entry' */ + load_subtree(t, entry, tree, n); + free(entry); + return 0; + } + break; + } + break; + case PTR_TYPE_SUBTREE: + if (!SUBTREE_SHA1_PREFIXCMP(entry->key_sha1, l->key_sha1)) { + /* unpack 'l' and restart insert */ + *p = NULL; + load_subtree(t, l, tree, n); + free(l); + return note_tree_insert(t, tree, n, entry, type, + combine_notes); + } + break; + } + + /* 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 0; + } + new_node = (struct int_node *) xcalloc(sizeof(struct int_node), 1); + 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); + return note_tree_insert(t, new_node, n + 1, entry, type, combine_notes); +} + /* Free the entire notes data contained in the given tree */ static void note_tree_free(struct int_node *tree) { @@ -442,8 +452,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; @@ -716,7 +730,7 @@ static int write_each_non_note_until(const char *note_path, struct write_each_note_data *d) { struct non_note *n = d->next_non_note; - int cmp, ret; + int cmp = 0, ret; while (n && (!note_path || (cmp = strcmp(n->path, note_path)) <= 0)) { if (note_path && cmp == 0) ; /* do nothing, prefer note to non-note */ @@ -801,16 +815,17 @@ int combine_notes_concatenate(unsigned char *cur_sha1, return 0; } - /* we will separate the notes by a newline anyway */ + /* we will separate the notes by two newlines anyway */ if (cur_msg[cur_len - 1] == '\n') cur_len--; /* concatenate cur_msg and new_msg into buf */ - buf_len = cur_len + 1 + new_len; + buf_len = cur_len + 2 + new_len; buf = (char *) xmalloc(buf_len); memcpy(buf, cur_msg, cur_len); buf[cur_len] = '\n'; - memcpy(buf + cur_len + 1, new_msg, new_len); + buf[cur_len + 1] = '\n'; + memcpy(buf + cur_len + 2, new_msg, new_len); free(cur_msg); free(new_msg); @@ -833,12 +848,88 @@ 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) { struct string_list *refs = cb; if (!unsorted_string_list_has_string(refs, path)) - string_list_append(path, refs); + string_list_append(refs, path); return 0; } @@ -851,7 +942,7 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob) if (get_sha1(glob, sha1)) warning("notes ref %s is invalid", glob); if (!unsorted_string_list_has_string(list, glob)) - string_list_append(glob, list); + string_list_append(list, glob); } } @@ -877,14 +968,6 @@ void string_list_add_refs_from_colon_sep(struct string_list *list, strbuf_release(&globbuf); } -static int string_list_add_refs_from_list(struct string_list_item *item, - void *cb) -{ - struct string_list *list = cb; - string_list_add_refs_by_glob(list, item->string); - return 0; -} - static int notes_display_config(const char *k, const char *v, void *cb) { int *load_refs = cb; @@ -898,7 +981,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) @@ -940,37 +1023,25 @@ void init_notes(struct notes_tree *t, const char *notes_ref, return; if (get_tree_entry(object_sha1, "", sha1, &mode)) die("Failed to read notes tree referenced by %s (%s)", - notes_ref, object_sha1); + notes_ref, sha1_to_hex(object_sha1)); hashclr(root_tree.key_sha1); hashcpy(root_tree.val_sha1, sha1); load_subtree(t, &root_tree, t->root, 0); } -struct load_notes_cb_data { - int counter; - struct notes_tree **trees; -}; - -static int load_one_display_note_ref(struct string_list_item *item, - void *cb_data) -{ - struct load_notes_cb_data *c = cb_data; - struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree)); - init_notes(t, item->string, combine_notes_ignore, 0); - c->trees[c->counter++] = t; - return 0; -} - struct notes_tree **load_notes_trees(struct string_list *refs) { + struct string_list_item *item; + int counter = 0; struct notes_tree **trees; - struct load_notes_cb_data cb_data; trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *)); - cb_data.counter = 0; - cb_data.trees = trees; - for_each_string_list(load_one_display_note_ref, refs, &cb_data); - trees[cb_data.counter] = NULL; + for_each_string_list_item(item, refs) { + struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree)); + init_notes(t, item->string, combine_notes_ignore, 0); + trees[counter++] = t; + } + trees[counter] = NULL; return trees; } @@ -983,7 +1054,7 @@ void init_display_notes(struct display_notes_opt *opt) assert(!display_notes_trees); if (!opt || !opt->suppress_default_notes) { - string_list_append(default_notes_ref(), &display_notes_refs); + string_list_append(&display_notes_refs, default_notes_ref()); display_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT); if (display_ref_env) { string_list_add_refs_from_colon_sep(&display_notes_refs, @@ -995,16 +1066,18 @@ void init_display_notes(struct display_notes_opt *opt) git_config(notes_display_config, &load_config_refs); - if (opt && opt->extra_notes_refs) - for_each_string_list(string_list_add_refs_from_list, - opt->extra_notes_refs, - &display_notes_refs); + if (opt && opt->extra_notes_refs) { + struct string_list_item *item; + for_each_string_list_item(item, opt->extra_notes_refs) + string_list_add_refs_by_glob(&display_notes_refs, + item->string); + } display_notes_trees = load_notes_trees(&display_notes_refs); 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; @@ -1018,20 +1091,23 @@ 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) +int remove_note(struct notes_tree *t, const unsigned char *object_sha1) { struct leaf_node l; if (!t) t = &default_notes_tree; assert(t->initialized); - t->dirty = 1; hashcpy(l.key_sha1, object_sha1); hashclr(l.val_sha1); note_tree_remove(t, t->root, 0, &l); + if (is_null_sha1(l.val_sha1)) // no note was removed + return 1; + t->dirty = 1; + return 0; } const unsigned char *get_note(struct notes_tree *t, @@ -1083,7 +1159,7 @@ int write_notes_tree(struct notes_tree *t, unsigned char *result) return ret; } -void prune_notes(struct notes_tree *t) +void prune_notes(struct notes_tree *t, int flags) { struct note_delete_list *l = NULL; @@ -1094,7 +1170,10 @@ void prune_notes(struct notes_tree *t) for_each_note(t, 0, prune_notes_helper, &l); while (l) { - remove_note(t, l->sha1); + if (flags & NOTES_PRUNE_VERBOSE) + printf("%s\n", sha1_to_hex(l->sha1)); + if (!(flags & NOTES_PRUNE_DRYRUN)) + remove_note(t, l->sha1); l = l->next; } } @@ -1191,7 +1270,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); @@ -1200,9 +1279,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; } |