summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/git/tree.h56
-rw-r--r--src/tree.c97
-rw-r--r--src/tree.h10
-rw-r--r--tests/t0901-readtree.c42
4 files changed, 182 insertions, 23 deletions
diff --git a/src/git/tree.h b/src/git/tree.h
index 95b233009..bd589ea11 100644
--- a/src/git/tree.h
+++ b/src/git/tree.h
@@ -14,6 +14,10 @@
*/
GIT_BEGIN_DECL
+
+/** Representation of each one of the entries in a tree object. */
+typedef struct git_tree_entry git_tree_entry;
+
/** Representation of a tree object. */
typedef struct git_tree git_tree;
@@ -35,6 +39,58 @@ GIT_EXTERN(git_tree *) git_tree_lookup(git_repository *repo, const git_oid *id);
*/
GIT_EXTERN(const git_oid *) git_tree_id(git_tree *tree);
+
+/**
+ * Get the number of entries listed in a tree
+ * @param tree a previously loaded tree.
+ * @return the number of entries in the tree
+ */
+GIT_EXTERN(size_t) git_tree_entrycount(git_tree *tree);
+
+/**
+ * Lookup a tree entry by its filename
+ * @param tree a previously loaded tree.
+ * @param filename the filename of the desired entry
+ * @return the tree entry; NULL if not found
+ */
+GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(git_tree *tree, const char *filename);
+
+/**
+ * Lookup a tree entry by its position in the tree
+ * @param tree a previously loaded tree.
+ * @param idx the position in the entry list
+ * @return the tree entry; NULL if not found
+ */
+GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, int idx);
+
+/**
+ * Get the UNIX file attributes of a tree entry
+ * @param entry a tree entry
+ * @return attributes as an integer
+ */
+GIT_EXTERN(uint32_t) git_tree_entry_attributes(const git_tree_entry *entry);
+
+/**
+ * Get the filename of a tree entry
+ * @param entry a tree entry
+ * @return the name of the file
+ */
+GIT_EXTERN(const char *) git_tree_entry_name(const git_tree_entry *entry);
+
+/**
+ * Get the id of the object pointed by the entry
+ * @param entry a tree entry
+ * @return the oid of the object
+ */
+GIT_EXTERN(const git_oid *) git_tree_entry_id(const git_tree_entry *entry);
+
+/**
+ * Convert a tree entry to the git_repository_object it points too.
+ * @param entry a tree entry
+ * @return a reference to the pointed object in the repository
+ */
+GIT_EXTERN(git_repository_object *) git_tree_entry_2object(const git_tree_entry *entry);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/src/tree.c b/src/tree.c
index 15603e7a9..0373f3c22 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -31,6 +31,12 @@
void git_tree__free(git_tree *tree)
{
+ size_t i;
+
+ for (i = 0; i < tree->entry_count; ++i)
+ free(tree->entries[i].filename);
+
+ free(tree->entries);
free(tree);
}
@@ -44,13 +50,67 @@ git_tree *git_tree_lookup(git_repository *repo, const git_oid *id)
return (git_tree *)git_repository_lookup(repo, id, GIT_OBJ_TREE);
}
+uint32_t git_tree_entry_attributes(const git_tree_entry *entry)
+{
+ return entry->attr;
+}
+
+const char *git_tree_entry_name(const git_tree_entry *entry)
+{
+ return entry->filename;
+}
+
+const git_oid *git_tree_entry_id(const git_tree_entry *entry)
+{
+ return &entry->oid;
+}
+
+git_repository_object *git_tree_entry_2object(const git_tree_entry *entry)
+{
+ return git_repository_lookup(entry->owner->object.repo, &entry->oid, GIT_OBJ_ANY);
+}
+
+int entry_cmp(const void *key, const void *array_member)
+{
+ const char *filename = (const char *)key;
+ const git_tree_entry *entry = (const git_tree_entry *)array_member;
+
+ return strcmp(filename, entry->filename);
+}
+
+const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
+{
+ if (tree->entries == NULL)
+ git_tree__parse(tree);
+
+ return bsearch(filename, tree->entries, tree->entry_count, sizeof(git_tree_entry), entry_cmp);
+}
+
+const git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx)
+{
+ if (tree->entries == NULL)
+ git_tree__parse(tree);
+
+ return (tree->entries && idx >= 0 && idx < (int)tree->entry_count) ?
+ &tree->entries[idx] : NULL;
+}
+
+size_t git_tree_entrycount(git_tree *tree)
+{
+ return tree->entry_count;
+}
+
int git_tree__parse(git_tree *tree)
{
- static const char tree_header[] = {'t', 'r', 'e', 'e', ' '};
+ static const size_t avg_entry_size = 40;
int error = 0;
git_obj odb_object;
char *buffer, *buffer_end;
+ size_t entries_size;
+
+ if (tree->entries != NULL)
+ return GIT_SUCCESS;
error = git_odb_read(&odb_object, tree->object.repo->db, &tree->object.id);
if (error < 0)
@@ -59,23 +119,29 @@ int git_tree__parse(git_tree *tree)
buffer = odb_object.data;
buffer_end = odb_object.data + odb_object.len;
- if (memcmp(buffer, tree_header, 5) != 0)
- return GIT_EOBJCORRUPTED;
+ tree->entry_count = 0;
+ entries_size = (odb_object.len / avg_entry_size) + 1;
+ tree->entries = git__malloc(entries_size * sizeof(git_tree_entry));
- buffer += 5;
+ while (buffer < buffer_end) {
+ git_tree_entry *entry;
- tree->byte_size = strtol(buffer, &buffer, 10);
+ if (tree->entry_count >= entries_size) {
+ git_tree_entry *new_entries;
- if (*buffer++ != 0)
- return GIT_EOBJCORRUPTED;
+ entries_size = entries_size * 2;
- while (buffer < buffer_end) {
- git_tree_entry *entry;
+ new_entries = git__malloc(entries_size * sizeof(git_tree_entry));
+ memcpy(new_entries, tree->entries, tree->entry_count * sizeof(git_tree_entry));
- entry = git__malloc(sizeof(git_tree_entry));
- entry->next = tree->entries;
+ free(tree->entries);
+ tree->entries = new_entries;
+ }
- entry->attr = strtol(buffer, &buffer, 10);
+ entry = &tree->entries[tree->entry_count++];
+ entry->owner = tree;
+
+ entry->attr = strtol(buffer, &buffer, 8);
if (*buffer++ != ' ') {
error = GIT_EOBJCORRUPTED;
@@ -86,15 +152,14 @@ int git_tree__parse(git_tree *tree)
if (entry->filename == NULL) {
error = GIT_EOBJCORRUPTED;
- break;
}
- buffer += strlen(entry->filename);
+
+ buffer += strlen(entry->filename) + 1;
git_oid_mkraw(&entry->oid, (const unsigned char *)buffer);
buffer += GIT_OID_RAWSZ;
-
- tree->entries = entry;
}
+ git_obj_close(&odb_object);
return error;
}
diff --git a/src/tree.h b/src/tree.h
index f21668586..2222b0349 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -5,22 +5,18 @@
#include "repository.h"
struct git_tree_entry {
-
- unsigned int attr;
+ uint32_t attr;
char *filename;
git_oid oid;
- struct git_tree_entry *next;
+ git_tree *owner;
};
-typedef struct git_tree_entry git_tree_entry;
-
struct git_tree {
git_repository_object object;
- size_t byte_size;
git_tree_entry *entries;
- unsigned int entry_count;
+ size_t entry_count;
};
void git_tree__free(git_tree *tree);
diff --git a/tests/t0901-readtree.c b/tests/t0901-readtree.c
new file mode 100644
index 000000000..307bbea12
--- /dev/null
+++ b/tests/t0901-readtree.c
@@ -0,0 +1,42 @@
+#include "test_lib.h"
+#include "test_helpers.h"
+#include "commit.h"
+
+#include <git/odb.h>
+#include <git/commit.h>
+#include <git/revwalk.h>
+
+static const char *odb_dir = "../resources/sample-odb";
+static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
+
+BEGIN_TEST(tree_read_test)
+ git_odb *db;
+ git_oid id;
+ git_repository *repo;
+ git_tree *tree;
+ const git_tree_entry *entry;
+
+ must_pass(git_odb_open(&db, odb_dir));
+
+ repo = git_repository_alloc(db);
+ must_be_true(repo != NULL);
+
+ git_oid_mkstr(&id, tree_oid);
+
+ tree = git_tree_lookup(repo, &id);
+ must_be_true(tree != NULL);
+
+ must_pass(git_tree__parse(tree));
+
+ must_be_true(git_tree_entrycount(tree) == 3);
+
+ entry = git_tree_entry_byname(tree, "README");
+ must_be_true(entry != NULL);
+
+ must_be_true(strcmp(git_tree_entry_name(entry), "README") == 0);
+
+ must_be_true(git_tree_entry_2object(entry) != NULL);
+
+ git_repository_free(repo);
+ git_odb_close(db);
+END_TEST