Notes API: write_notes_tree(): Store the notes tree in the database

Uses for_each_note() to traverse the notes tree, and produces tree
objects on the fly representing the "on-disk" version of the notes
tree with appropriate fanout.

Signed-off-by: Johan Herland <johan@herland.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Johan Herland 2010-02-13 22:28:17 +01:00 committed by Junio C Hamano
parent 73f77b909f
commit 61a7cca0c6
2 changed files with 180 additions and 3 deletions

145
notes.c
View File

@ -1,5 +1,6 @@
#include "cache.h"
#include "notes.h"
#include "tree.h"
#include "utf8.h"
#include "strbuf.h"
#include "tree-walk.h"
@ -540,6 +541,126 @@ redo:
return 0;
}
struct tree_write_stack {
struct tree_write_stack *next;
struct strbuf buf;
char path[2]; /* path to subtree in next, if any */
};
static inline int matches_tree_write_stack(struct tree_write_stack *tws,
const char *full_path)
{
return full_path[0] == tws->path[0] &&
full_path[1] == tws->path[1] &&
full_path[2] == '/';
}
static void write_tree_entry(struct strbuf *buf, unsigned int mode,
const char *path, unsigned int path_len, const
unsigned char *sha1)
{
strbuf_addf(buf, "%06o %.*s%c", mode, path_len, path, '\0');
strbuf_add(buf, sha1, 20);
}
static void tree_write_stack_init_subtree(struct tree_write_stack *tws,
const char *path)
{
struct tree_write_stack *n;
assert(!tws->next);
assert(tws->path[0] == '\0' && tws->path[1] == '\0');
n = (struct tree_write_stack *)
xmalloc(sizeof(struct tree_write_stack));
n->next = NULL;
strbuf_init(&n->buf, 256 * (32 + 40)); /* assume 256 entries per tree */
n->path[0] = n->path[1] = '\0';
tws->next = n;
tws->path[0] = path[0];
tws->path[1] = path[1];
}
static int tree_write_stack_finish_subtree(struct tree_write_stack *tws)
{
int ret;
struct tree_write_stack *n = tws->next;
unsigned char s[20];
if (n) {
ret = tree_write_stack_finish_subtree(n);
if (ret)
return ret;
ret = write_sha1_file(n->buf.buf, n->buf.len, tree_type, s);
if (ret)
return ret;
strbuf_release(&n->buf);
free(n);
tws->next = NULL;
write_tree_entry(&tws->buf, 040000, tws->path, 2, s);
tws->path[0] = tws->path[1] = '\0';
}
return 0;
}
static int write_each_note_helper(struct tree_write_stack *tws,
const char *path, unsigned int mode,
const unsigned char *sha1)
{
size_t path_len = strlen(path);
unsigned int n = 0;
int ret;
/* Determine common part of tree write stack */
while (tws && 3 * n < path_len &&
matches_tree_write_stack(tws, path + 3 * n)) {
n++;
tws = tws->next;
}
/* tws point to last matching tree_write_stack entry */
ret = tree_write_stack_finish_subtree(tws);
if (ret)
return ret;
/* Start subtrees needed to satisfy path */
while (3 * n + 2 < path_len && path[3 * n + 2] == '/') {
tree_write_stack_init_subtree(tws, path + 3 * n);
n++;
tws = tws->next;
}
/* There should be no more directory components in the given path */
assert(memchr(path + 3 * n, '/', path_len - (3 * n)) == NULL);
/* Finally add given entry to the current tree object */
write_tree_entry(&tws->buf, mode, path + 3 * n, path_len - (3 * n),
sha1);
return 0;
}
struct write_each_note_data {
struct tree_write_stack *root;
};
static int write_each_note(const unsigned char *object_sha1,
const unsigned char *note_sha1, char *note_path,
void *cb_data)
{
struct write_each_note_data *d =
(struct write_each_note_data *) cb_data;
size_t note_path_len = strlen(note_path);
unsigned int mode = 0100644;
if (note_path[note_path_len - 1] == '/') {
/* subtree entry */
note_path_len--;
note_path[note_path_len] = '\0';
mode = 040000;
}
assert(note_path_len <= 40 + 19);
return write_each_note_helper(d->root, note_path, mode, note_sha1);
}
void init_notes(const char *notes_ref, int flags)
{
unsigned char sha1[20], object_sha1[20];
@ -604,6 +725,30 @@ int for_each_note(int flags, each_note_fn fn, void *cb_data)
return for_each_note_helper(&root_node, 0, 0, flags, fn, cb_data);
}
int write_notes_tree(unsigned char *result)
{
struct tree_write_stack root;
struct write_each_note_data cb_data;
int ret;
assert(initialized);
/* Prepare for traversal of current notes tree */
root.next = NULL; /* last forward entry in list is grounded */
strbuf_init(&root.buf, 256 * (32 + 40)); /* assume 256 entries */
root.path[0] = root.path[1] = '\0';
cb_data.root = &root;
/* Write tree objects representing current notes tree */
ret = for_each_note(FOR_EACH_NOTE_DONT_UNPACK_SUBTREES |
FOR_EACH_NOTE_YIELD_SUBTREES,
write_each_note, &cb_data) ||
tree_write_stack_finish_subtree(&root) ||
write_sha1_file(root.buf.buf, root.buf.len, tree_type, result);
strbuf_release(&root.buf);
return ret;
}
void free_notes(void)
{
note_tree_free(&root_node);

38
notes.h
View File

@ -21,11 +21,23 @@
*/
void init_notes(const char *notes_ref, int flags);
/* Add the given note object to the internal notes tree structure */
/*
* Add the given note object to the internal notes tree structure
*
* IMPORTANT: The changes made by add_note() to the internal notes tree structure
* are not persistent until a subsequent call to write_notes_tree() returns
* zero.
*/
void add_note(const unsigned char *object_sha1,
const unsigned char *note_sha1);
/* Remove the given note object from the internal notes tree structure */
/*
* Remove the given note object from the internal notes tree structure
*
* IMPORTANT: The changes made by remove_note() to the internal notes tree
* structure are not persistent until a subsequent call to write_notes_tree()
* returns zero.
*/
void remove_note(const unsigned char *object_sha1);
/*
@ -82,7 +94,27 @@ typedef int each_note_fn(const unsigned char *object_sha1,
void *cb_data);
int for_each_note(int flags, each_note_fn fn, void *cb_data);
/* Free (and de-initialize) the internal notes tree structure */
/*
* Write the internal notes tree structure to the object database
*
* Creates a new tree object encapsulating the current state of the
* internal notes tree, and stores its SHA1 into the 'result' argument.
*
* Returns zero on success, non-zero on failure.
*
* IMPORTANT: Changes made to the internal notes tree structure are not
* persistent until this function has returned zero. Please also remember
* to create a corresponding commit object, and update the appropriate
* notes ref.
*/
int write_notes_tree(unsigned char *result);
/*
* Free (and de-initialize) the internal notes tree structure
*
* IMPORTANT: Changes made to the notes tree since the last, successful
* call to write_notes_tree() will be lost.
*/
void free_notes(void);
/* Flags controlling how notes are formatted */