summaryrefslogtreecommitdiff
path: root/src/tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tree.c')
-rw-r--r--src/tree.c227
1 files changed, 219 insertions, 8 deletions
diff --git a/src/tree.c b/src/tree.c
index 588b5e9a..693ed1ca 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -33,6 +33,10 @@
#define MAX_FILEMODE 0777777
#define MAX_FILEMODE_BYTES 6
+static int valid_attributes(const int attributes) {
+ return attributes >= 0 && attributes <= MAX_FILEMODE;
+}
+
int entry_search_cmp(const void *key, const void *array_member)
{
const char *filename = (const char *)key;
@@ -79,30 +83,30 @@ const git_oid *git_tree_id(git_tree *c)
return git_object_id((git_object *)c);
}
-unsigned int git_tree_entry_attributes(git_tree_entry *entry)
+unsigned int git_tree_entry_attributes(const git_tree_entry *entry)
{
return entry->attr;
}
-const char *git_tree_entry_name(git_tree_entry *entry)
+const char *git_tree_entry_name(const git_tree_entry *entry)
{
assert(entry);
return entry->filename;
}
-const git_oid *git_tree_entry_id(git_tree_entry *entry)
+const git_oid *git_tree_entry_id(const git_tree_entry *entry)
{
assert(entry);
return &entry->oid;
}
-int git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry)
+int git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry)
{
assert(entry && object_out);
return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY);
}
-git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
+const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
{
int idx;
@@ -115,7 +119,7 @@ git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
return git_vector_get(&tree->entries, idx);
}
-git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx)
+const git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx)
{
assert(tree);
return git_vector_get(&tree->entries, (unsigned int)idx);
@@ -159,6 +163,7 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf
}
entry->filename = git__strdup(buffer);
+ entry->filename_len = strlen(buffer);
while (buffer < buffer_end && *buffer != 0)
buffer++;
@@ -178,7 +183,7 @@ int git_tree__parse(git_tree *tree, git_odb_object *obj)
return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len);
}
-static int write_entry(char *buffer, int mode, const char *path, size_t path_len, const git_oid *oid)
+static int write_index_entry(char *buffer, int mode, const char *path, size_t path_len, const git_oid *oid)
{
int written;
written = sprintf(buffer, "%o %.*s%c", mode, (int)path_len, path, 0);
@@ -254,7 +259,7 @@ static int write_index(git_oid *oid, git_index *index, const char *base, int bas
return GIT_ENOMEM;
}
- offset += write_entry(buffer + offset, write_mode, filename, entrylen, write_oid);
+ offset += write_index_entry(buffer + offset, write_mode, filename, entrylen, write_oid);
}
error = git_odb_write(oid, index->repository->db, buffer, offset, GIT_OBJ_TREE);
@@ -273,3 +278,209 @@ int git_tree_create_fromindex(git_oid *oid, git_index *index)
error = write_index(oid, index, "", 0, 0, git_index_entrycount(index));
return (error < GIT_SUCCESS) ? error : GIT_SUCCESS;
}
+
+static void sort_entries(git_treebuilder *bld)
+{
+ git_vector_sort(&bld->entries);
+}
+
+int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
+{
+ git_treebuilder *bld;
+ size_t i, source_entries = DEFAULT_TREE_SIZE;
+
+ assert(builder_p);
+
+ bld = git__calloc(1, sizeof(git_treebuilder));
+ if (bld == NULL)
+ return GIT_ENOMEM;
+
+ if (source != NULL)
+ source_entries = source->entries.length;
+
+ if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < GIT_SUCCESS) {
+ free(bld);
+ return GIT_ENOMEM;
+ }
+
+ if (source != NULL) {
+ bld->entry_count = source_entries;
+ for (i = 0; i < source->entries.length; ++i) {
+ git_tree_entry *entry_src = source->entries.contents[i];
+ git_tree_entry *entry = git__calloc(1, sizeof(git_tree_entry));
+
+ if (entry == NULL) {
+ git_treebuilder_free(bld);
+ return GIT_ENOMEM;
+ }
+
+ entry->filename = git__strdup(entry_src->filename);
+
+ if (entry->filename == NULL) {
+ free(entry);
+ git_treebuilder_free(bld);
+ return GIT_ENOMEM;
+ }
+
+ entry->filename_len = entry_src->filename_len;
+ git_oid_cpy(&entry->oid, &entry_src->oid);
+ entry->attr = entry_src->attr;
+
+ git_vector_insert(&bld->entries, entry);
+ }
+ }
+
+ *builder_p = bld;
+ return GIT_SUCCESS;
+}
+
+int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)
+{
+ git_tree_entry *entry;
+ int pos;
+
+ assert(bld && id && filename);
+
+ if (!valid_attributes(attributes))
+ return GIT_ERROR;
+
+ if ((pos = git_vector_bsearch2(&bld->entries, entry_search_cmp, filename)) != GIT_ENOTFOUND) {
+ entry = git_vector_get(&bld->entries, pos);
+ if (entry->removed) {
+ entry->removed = 0;
+ bld->entry_count++;
+ }
+ } else {
+ if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
+ return GIT_ENOMEM;
+
+ memset(entry, 0x0, sizeof(git_tree_entry));
+ entry->filename = git__strdup(filename);
+ entry->filename_len = strlen(entry->filename);
+
+ bld->entry_count++;
+ }
+
+ git_oid_cpy(&entry->oid, id);
+ entry->attr = attributes;
+
+ if (pos != GIT_ENOTFOUND) {
+ if (git_vector_insert(&bld->entries, entry) < 0)
+ return GIT_ENOMEM;
+ }
+
+ if (entry_out != NULL)
+ *entry_out = entry;
+
+ return GIT_SUCCESS;
+}
+
+const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename)
+{
+ int idx;
+ git_tree_entry *entry;
+
+ assert(bld && filename);
+
+ sort_entries(bld);
+ idx = git_vector_bsearch2(&bld->entries, entry_search_cmp, filename);
+ if (idx == GIT_ENOTFOUND)
+ return NULL;
+
+ entry = git_vector_get(&bld->entries, idx);
+ if (entry->removed)
+ return NULL;
+
+ return entry;
+}
+
+int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
+{
+ git_tree_entry *remove_ptr = (git_tree_entry *)git_treebuilder_get(bld, filename);
+
+ if (remove_ptr == NULL || remove_ptr->removed)
+ return GIT_ENOTFOUND;
+
+ remove_ptr->removed = 1;
+ bld->entry_count--;
+ return GIT_SUCCESS;
+}
+
+int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
+{
+ size_t i, size = 0;
+ char filemode[MAX_FILEMODE_BYTES + 1 + 1];
+ git_odb_stream *stream;
+ int error;
+
+ assert(bld);
+
+ sort_entries(bld);
+
+ for (i = 0; i < bld->entries.length; ++i) {
+ git_tree_entry *entry = bld->entries.contents[i];
+
+ if (entry->removed)
+ continue;
+
+ size += (entry->attr > 0x7FF) ? 7 : 6;
+ size += entry->filename_len + 1;
+ size += GIT_OID_RAWSZ;
+ }
+
+ if ((error = git_odb_open_wstream(&stream, git_repository_database(repo), size, GIT_OBJ_TREE)) < GIT_SUCCESS)
+ return error;
+
+ for (i = 0; i < bld->entries.length; ++i) {
+ git_tree_entry *entry = bld->entries.contents[i];
+
+ if (entry->removed)
+ continue;
+
+ snprintf(filemode, sizeof(filemode), "%o ", entry->attr);
+ stream->write(stream, filemode, strlen(filemode));
+ stream->write(stream, entry->filename, entry->filename_len + 1);
+ stream->write(stream, (char *)entry->oid.id, GIT_OID_RAWSZ);
+ }
+
+ error = stream->finalize_write(oid, stream);
+ stream->free(stream);
+
+ return error;
+}
+
+void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload)
+{
+ size_t i;
+
+ assert(bld && filter);
+
+ for (i = 0; i < bld->entries.length; ++i) {
+ git_tree_entry *entry = bld->entries.contents[i];
+ if (!entry->removed && filter(entry, payload))
+ entry->removed = 1;
+ }
+}
+
+void git_treebuilder_clear(git_treebuilder *bld)
+{
+ size_t i;
+ assert(bld);
+
+ for (i = 0; i < bld->entries.length; ++i) {
+ git_tree_entry *e = bld->entries.contents[i];
+ free(e->filename);
+ free(e);
+ }
+
+ git_vector_clear(&bld->entries);
+}
+
+void git_treebuilder_free(git_treebuilder *bld)
+{
+ git_treebuilder_clear(bld);
+ git_vector_free(&bld->entries);
+ free(bld);
+}
+
+