When an object is made unreachable by Git, any notes that annotate that object are not automagically made unreachable, since all notes are always trivially reachable from a notes ref. In order to remove notes for non-existing objects, we therefore need to add functionality for traversing the notes tree and explicitly removing references to notes that annotate non-reachable objects. Thus the notes objects themselves also become unreachable, and are removed by a later garbage collect. prune_notes() performs this traversal (by using for_each_note() internally), and removes the notes in question from the notes tree. Note that the effect of prune_notes() is not persistent unless a subsequent call to write_notes_tree() is made. Signed-off-by: Johan Herland <johan@xxxxxxxxxxx> --- notes.c | 39 +++++++++++++++++++++++++++++++++++++++ notes.h | 12 ++++++++++++ 2 files changed, 51 insertions(+), 0 deletions(-) diff --git a/notes.c b/notes.c index d432517..3ba3e6d 100644 --- a/notes.c +++ b/notes.c @@ -749,6 +749,29 @@ static int write_each_note(const unsigned char *object_sha1, write_each_note_helper(d->root, note_path, mode, note_sha1); } +struct note_delete_list { + struct note_delete_list *next; + const unsigned char *sha1; +}; + +static int prune_notes_helper(const unsigned char *object_sha1, + const unsigned char *note_sha1, char *note_path, + void *cb_data) +{ + struct note_delete_list **l = (struct note_delete_list **) cb_data; + struct note_delete_list *n; + + if (has_sha1_file(object_sha1)) + return 0; /* nothing to do for this note */ + + /* failed to find object => prune this note */ + n = (struct note_delete_list *) xmalloc(sizeof(*n)); + n->next = *l; + n->sha1 = object_sha1; + *l = n; + return 0; +} + int combine_notes_concatenate(unsigned char *cur_sha1, const unsigned char *new_sha1) { @@ -922,6 +945,22 @@ int write_notes_tree(struct notes_tree *t, unsigned char *result) return ret; } +void prune_notes(struct notes_tree *t) +{ + struct note_delete_list *l = NULL; + + if (!t) + t = &default_notes_tree; + assert(t->initialized); + + for_each_note(t, 0, prune_notes_helper, &l); + + while (l) { + remove_note(t, l->sha1); + l = l->next; + } +} + void free_notes(struct notes_tree *t) { if (!t) diff --git a/notes.h b/notes.h index f25643e..c0c9c5a 100644 --- a/notes.h +++ b/notes.h @@ -158,6 +158,18 @@ int for_each_note(struct notes_tree *t, int flags, each_note_fn fn, int write_notes_tree(struct notes_tree *t, unsigned char *result); /* + * Remove all notes annotating non-existing objects from the given notes tree + * + * All notes in the given notes_tree that are associated with objects that no + * longer exist in the database, are removed from the notes tree. + * + * IMPORTANT: The changes made by prune_notes() to the given notes_tree + * structure are not persistent until a subsequent call to write_notes_tree() + * returns zero. + */ +void prune_notes(struct notes_tree *t); + +/* * Free (and de-initialize) the given notes_tree structure * * IMPORTANT: Changes made to the given notes_tree since the last, successful -- 1.6.6.405.g80ed6 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html