Commit 2a884588 by Vicent Marti

Add write-back support for git_tree

All the setter methods for git_tree have been added, including the
setters for attributes on each git_tree_entry and methods to add/remove
entries of the tree.

Modified trees and trees created in-memory from scratch can be written
back to the repository using git_object_write().

Signed-off-by: Vicent Marti <tanoku@gmail.com>
parent d45b4a9a
......@@ -65,7 +65,7 @@ GIT_EXTERN(size_t) git_tree_entrycount(git_tree *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);
GIT_EXTERN(git_tree_entry *) git_tree_entry_byname(git_tree *tree, const char *filename);
/**
* Lookup a tree entry by its position in the tree
......@@ -73,35 +73,107 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(git_tree *tree, const c
* @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);
GIT_EXTERN(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(unsigned int) git_tree_entry_attributes(const git_tree_entry *entry);
GIT_EXTERN(unsigned int) git_tree_entry_attributes(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);
GIT_EXTERN(const char *) git_tree_entry_name(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);
GIT_EXTERN(const git_oid *) git_tree_entry_id(git_tree_entry *entry);
/**
* Convert a tree entry to the git_object it points too.
* @param entry a tree entry
* @return a reference to the pointed object in the repository
*/
GIT_EXTERN(git_object *) git_tree_entry_2object(const git_tree_entry *entry);
GIT_EXTERN(git_object *) git_tree_entry_2object(git_tree_entry *entry);
/**
* Add a new entry to a tree.
*
* This will mark the tree as modified; the new entry will
* be written back to disk on the next git_object_write()
*
* @param tree Tree object to store the entry
* @iparam id OID for the tree entry
* @param filename Filename for the tree entry
* @param attributes UNIX file attributes for the entry
*/
GIT_EXTERN(void) git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename, int attributes);
/**
* Remove an entry by its index.
*
* Index must be >= 0 and < than git_tree_entrycount().
*
* This will mark the tree as modified; the modified entry will
* be written back to disk on the next git_object_write()
*
* @param tree Tree where to remove the entry
* @param idx index of the entry
* @return 0 on successful removal; GIT_ENOTFOUND if the entry wasn't found
*/
GIT_EXTERN(int) git_tree_remove_entry_byindex(git_tree *tree, int idx);
/**
* Remove an entry by its filename.
*
* This will mark the tree as modified; the modified entry will
* be written back to disk on the next git_object_write()
*
* @param tree Tree where to remove the entry
* @param filename File name of the entry
* @return 0 on successful removal; GIT_ENOTFOUND if the entry wasn't found
*/
GIT_EXTERN(int) git_tree_remove_entry_byname(git_tree *tree, const char *filename);
/**
* Change the SHA1 id of a tree entry.
*
* This will mark the tree that contains the entry as modified;
* the modified entry will be written back to disk on the next git_object_write()
*
* @param entry Entry object which will be modified
* @param oid new SHA1 oid for the entry
*/
GIT_EXTERN(void) git_tree_entry_set_id(git_tree_entry *entry, const git_oid *oid);
/**
* Change the filename of a tree entry.
*
* This will mark the tree that contains the entry as modified;
* the modified entry will be written back to disk on the next git_object_write()
*
* @param entry Entry object which will be modified
* @param oid new filename for the entry
*/
GIT_EXTERN(void) git_tree_entry_set_name(git_tree_entry *entry, const char *name);
/**
* Change the attributes of a tree entry.
*
* This will mark the tree that contains the entry as modified;
* the modified entry will be written back to disk on the next git_object_write()
*
* @param entry Entry object which will be modified
* @param oid new attributes for the entry
*/
GIT_EXTERN(void) git_tree_entry_set_attributes(git_tree_entry *entry, int attr);
/** @} */
GIT_END_DECL
......
......@@ -270,6 +270,9 @@ int git_object_write(git_object *object)
break;
case GIT_OBJ_TREE:
error = git_tree__writeback((git_tree *)object, source);
break;
case GIT_OBJ_TAG:
default:
error = GIT_ERROR;
......
......@@ -29,12 +29,49 @@
#include "tree.h"
#include "git/repository.h"
static void resize_tree_array(git_tree *tree)
{
git_tree_entry **new_entries;
tree->array_size = tree->array_size * 2;
new_entries = git__malloc(tree->array_size * sizeof(git_tree_entry *));
memcpy(new_entries, tree->entries, tree->entry_count * sizeof(git_tree_entry *));
free(tree->entries);
tree->entries = new_entries;
}
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);
}
int entry_sort_cmp(const void *a, const void *b)
{
const git_tree_entry *entry_a = *(const git_tree_entry **)(a);
const git_tree_entry *entry_b = *(const git_tree_entry **)(b);
return strcmp(entry_a->filename, entry_b->filename);
}
static void entry_resort(git_tree *tree)
{
qsort(tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_sort_cmp);
}
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[i]);
free(tree->entries);
free(tree);
......@@ -55,45 +92,62 @@ git_tree *git_tree_lookup(git_repository *repo, const git_oid *id)
return (git_tree *)git_repository_lookup(repo, id, GIT_OBJ_TREE);
}
unsigned int git_tree_entry_attributes(const git_tree_entry *entry)
void git_tree_entry_set_attributes(git_tree_entry *entry, int attr)
{
return entry->attr;
assert(entry && entry->owner);
entry->attr = attr;
entry->owner->object.modified = 1;
}
const char *git_tree_entry_name(const git_tree_entry *entry)
void git_tree_entry_set_name(git_tree_entry *entry, const char *name)
{
return entry->filename;
assert(entry && entry->owner);
strncpy(entry->filename, name, GIT_TREE_MAX_FILENAME);
entry_resort(entry->owner);
entry->owner->object.modified = 1;
}
const git_oid *git_tree_entry_id(const git_tree_entry *entry)
void git_tree_entry_set_id(git_tree_entry *entry, const git_oid *oid)
{
return &entry->oid;
assert(entry && entry->owner);
git_oid_cpy(&entry->oid, oid);
entry->owner->object.modified = 1;
}
git_object *git_tree_entry_2object(const git_tree_entry *entry)
unsigned int git_tree_entry_attributes(git_tree_entry *entry)
{
return git_repository_lookup(entry->owner->object.repo, &entry->oid, GIT_OBJ_ANY);
return entry->attr;
}
int entry_cmp(const void *key, const void *array_member)
const char *git_tree_entry_name(git_tree_entry *entry)
{
const char *filename = (const char *)key;
const git_tree_entry *entry = (const git_tree_entry *)array_member;
return entry->filename;
}
return strcmp(filename, entry->filename);
const git_oid *git_tree_entry_id(git_tree_entry *entry)
{
return &entry->oid;
}
git_object *git_tree_entry_2object(git_tree_entry *entry)
{
return git_repository_lookup(entry->owner->object.repo, &entry->oid, GIT_OBJ_ANY);
}
const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
{
return bsearch(filename, tree->entries, tree->entry_count, sizeof(git_tree_entry), entry_cmp);
return *(git_tree_entry **)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)
git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx)
{
if (tree->entries == NULL)
return NULL;
return (idx >= 0 && idx < (int)tree->entry_count) ? &tree->entries[idx] : NULL;
return (idx >= 0 && idx < (int)tree->entry_count) ? tree->entries[idx] : NULL;
}
size_t git_tree_entrycount(git_tree *tree)
......@@ -101,16 +155,97 @@ size_t git_tree_entrycount(git_tree *tree)
return tree->entry_count;
}
void git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename, int attributes)
{
git_tree_entry *entry;
if (tree->entry_count >= tree->array_size)
resize_tree_array(tree);
if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
return;
memset(entry, 0x0, sizeof(git_tree_entry));
strncpy(entry->filename, filename, GIT_TREE_MAX_FILENAME);
git_oid_cpy(&entry->oid, id);
entry->attr = attributes;
entry->owner = tree;
tree->entries[tree->entry_count++] = entry;
entry_resort(tree);
tree->object.modified = 1;
}
int git_tree_remove_entry_byindex(git_tree *tree, int idx)
{
git_tree_entry *remove_ptr;
if (idx < 0 || idx >= (int)tree->entry_count)
return GIT_ENOTFOUND;
remove_ptr = tree->entries[idx];
tree->entries[idx] = tree->entries[--tree->entry_count];
free(remove_ptr);
entry_resort(tree);
tree->object.modified = 1;
return GIT_SUCCESS;
}
int git_tree_remove_entry_byname(git_tree *tree, const char *filename)
{
git_tree_entry **entry_ptr;
int idx;
entry_ptr = bsearch(filename, tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_cmp);
if (entry_ptr == NULL)
return GIT_ENOTFOUND;
idx = (int)(entry_ptr - tree->entries);
return git_tree_remove_entry_byindex(tree, idx);
}
int git_tree__writeback(git_tree *tree, git_odb_source *src)
{
size_t i;
if (tree->entries == NULL)
return GIT_ERROR;
entry_resort(tree);
for (i = 0; i < tree->entry_count; ++i) {
git_tree_entry *entry;
entry = tree->entries[i];
git__source_printf(src, "%06o %s\0", entry->attr, entry->filename);
git__source_write(src, entry->oid.id, GIT_OID_RAWSZ);
}
return GIT_SUCCESS;
}
int git_tree__parse(git_tree *tree)
{
static const size_t avg_entry_size = 40;
int error = 0;
char *buffer, *buffer_end;
size_t entries_size;
if (tree->entries != NULL)
return GIT_SUCCESS;
assert(!tree->object.in_memory);
if (tree->entries != NULL) {
size_t i;
for (i = 0; i < tree->entry_count; ++i)
free(tree->entries[i]);
free(tree->entries);
}
error = git_object__source_open((git_object *)tree);
if (error < 0)
......@@ -120,27 +255,24 @@ int git_tree__parse(git_tree *tree)
buffer_end = buffer + tree->object.source.raw.len;
tree->entry_count = 0;
entries_size = (tree->object.source.raw.len / avg_entry_size) + 1;
tree->entries = git__malloc(entries_size * sizeof(git_tree_entry));
tree->array_size = (tree->object.source.raw.len / avg_entry_size) + 1;
tree->entries = git__malloc(tree->array_size * sizeof(git_tree_entry *));
while (buffer < buffer_end) {
git_tree_entry *entry;
if (tree->entry_count >= entries_size) {
git_tree_entry *new_entries;
if (tree->entry_count >= tree->array_size)
resize_tree_array(tree);
entries_size = entries_size * 2;
new_entries = git__malloc(entries_size * sizeof(git_tree_entry));
memcpy(new_entries, tree->entries, tree->entry_count * sizeof(git_tree_entry));
free(tree->entries);
tree->entries = new_entries;
entry = git__malloc(sizeof(git_tree_entry));
if (entry == NULL) {
error = GIT_ENOMEM;
break;
}
entry = &tree->entries[tree->entry_count++];
entry->owner = tree;
tree->entries[tree->entry_count++] = entry;
entry->owner = tree;
entry->attr = strtol(buffer, &buffer, 8);
if (*buffer++ != ' ') {
......@@ -148,13 +280,12 @@ int git_tree__parse(git_tree *tree)
break;
}
entry->filename = git__strdup(buffer);
strncpy(entry->filename, buffer, GIT_TREE_MAX_FILENAME);
if (entry->filename == NULL) {
error = GIT_EOBJCORRUPTED;
}
while (buffer < buffer_end && *buffer != 0)
buffer++;
buffer += strlen(entry->filename) + 1;
buffer++;
git_oid_mkraw(&entry->oid, (const unsigned char *)buffer);
buffer += GIT_OID_RAWSZ;
......
......@@ -4,9 +4,11 @@
#include <git/tree.h>
#include "repository.h"
#define GIT_TREE_MAX_FILENAME 255
struct git_tree_entry {
unsigned int attr;
char *filename;
char filename[GIT_TREE_MAX_FILENAME];
git_oid oid;
git_tree *owner;
......@@ -15,11 +17,13 @@ struct git_tree_entry {
struct git_tree {
git_object object;
git_tree_entry *entries;
git_tree_entry **entries;
size_t entry_count;
size_t array_size;
};
void git_tree__free(git_tree *tree);
int git_tree__parse(git_tree *tree);
int git_tree__writeback(git_tree *tree, git_odb_source *src);
#endif
......@@ -14,7 +14,7 @@ BEGIN_TEST(tree_read_test)
git_oid id;
git_repository *repo;
git_tree *tree;
const git_tree_entry *entry;
git_tree_entry *entry;
must_pass(git_odb_open(&db, odb_dir));
......@@ -35,6 +35,7 @@ BEGIN_TEST(tree_read_test)
must_be_true(strcmp(git_tree_entry_name(entry), "README") == 0);
must_be_true(git_tree_entry_2object(entry) != NULL);
git_repository_free(repo);
......
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