Commit 003c2690 by Vicent Marti

Finish the tree object API

The interface for loading and parsing tree objects from a repository has
been completed with all the required accesor methods for attributes,
support for manipulating individual tree entries and a new unit test
t0901-readtree which tries to load and parse a tree object from a
repository.

Signed-off-by: Vicent Marti <tanoku@gmail.com>
parent ff17642d
......@@ -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
......@@ -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;
}
......@@ -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);
......
#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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment