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); ...@@ -65,7 +65,7 @@ GIT_EXTERN(size_t) git_tree_entrycount(git_tree *tree);
* @param filename the filename of the desired entry * @param filename the filename of the desired entry
* @return the tree entry; NULL if not found * @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 * 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 ...@@ -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 * @param idx the position in the entry list
* @return the tree entry; NULL if not found * @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 * Get the UNIX file attributes of a tree entry
* @param entry a tree entry * @param entry a tree entry
* @return attributes as an integer * @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 * Get the filename of a tree entry
* @param entry a tree entry * @param entry a tree entry
* @return the name of the file * @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 * Get the id of the object pointed by the entry
* @param entry a tree entry * @param entry a tree entry
* @return the oid of the object * @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. * Convert a tree entry to the git_object it points too.
* @param entry a tree entry * @param entry a tree entry
* @return a reference to the pointed object in the repository * @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 GIT_END_DECL
......
...@@ -270,6 +270,9 @@ int git_object_write(git_object *object) ...@@ -270,6 +270,9 @@ int git_object_write(git_object *object)
break; break;
case GIT_OBJ_TREE: case GIT_OBJ_TREE:
error = git_tree__writeback((git_tree *)object, source);
break;
case GIT_OBJ_TAG: case GIT_OBJ_TAG:
default: default:
error = GIT_ERROR; error = GIT_ERROR;
......
...@@ -29,12 +29,49 @@ ...@@ -29,12 +29,49 @@
#include "tree.h" #include "tree.h"
#include "git/repository.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) void git_tree__free(git_tree *tree)
{ {
size_t i; size_t i;
for (i = 0; i < tree->entry_count; ++i) for (i = 0; i < tree->entry_count; ++i)
free(tree->entries[i].filename); free(tree->entries[i]);
free(tree->entries); free(tree->entries);
free(tree); free(tree);
...@@ -55,45 +92,62 @@ git_tree *git_tree_lookup(git_repository *repo, const git_oid *id) ...@@ -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); 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; return entry->filename;
const git_tree_entry *entry = (const git_tree_entry *)array_member; }
return strcmp(filename, entry->filename); const git_oid *git_tree_entry_id(git_tree_entry *entry)
{
return &entry->oid;
} }
const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) git_object *git_tree_entry_2object(git_tree_entry *entry)
{ {
return bsearch(filename, tree->entries, tree->entry_count, sizeof(git_tree_entry), entry_cmp); return git_repository_lookup(entry->owner->object.repo, &entry->oid, GIT_OBJ_ANY);
} }
const git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx) git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
{
return *(git_tree_entry **)bsearch(filename, tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_cmp);
}
git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx)
{ {
if (tree->entries == NULL) if (tree->entries == NULL)
return 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) size_t git_tree_entrycount(git_tree *tree)
...@@ -101,16 +155,97 @@ size_t git_tree_entrycount(git_tree *tree) ...@@ -101,16 +155,97 @@ size_t git_tree_entrycount(git_tree *tree)
return tree->entry_count; 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) int git_tree__parse(git_tree *tree)
{ {
static const size_t avg_entry_size = 40; static const size_t avg_entry_size = 40;
int error = 0; int error = 0;
char *buffer, *buffer_end; char *buffer, *buffer_end;
size_t entries_size;
if (tree->entries != NULL) assert(!tree->object.in_memory);
return GIT_SUCCESS;
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); error = git_object__source_open((git_object *)tree);
if (error < 0) if (error < 0)
...@@ -120,27 +255,24 @@ int git_tree__parse(git_tree *tree) ...@@ -120,27 +255,24 @@ int git_tree__parse(git_tree *tree)
buffer_end = buffer + tree->object.source.raw.len; buffer_end = buffer + tree->object.source.raw.len;
tree->entry_count = 0; tree->entry_count = 0;
entries_size = (tree->object.source.raw.len / avg_entry_size) + 1; tree->array_size = (tree->object.source.raw.len / avg_entry_size) + 1;
tree->entries = git__malloc(entries_size * sizeof(git_tree_entry)); tree->entries = git__malloc(tree->array_size * sizeof(git_tree_entry *));
while (buffer < buffer_end) { while (buffer < buffer_end) {
git_tree_entry *entry; git_tree_entry *entry;
if (tree->entry_count >= entries_size) { if (tree->entry_count >= tree->array_size)
git_tree_entry *new_entries; 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); entry = git__malloc(sizeof(git_tree_entry));
tree->entries = new_entries; if (entry == NULL) {
error = GIT_ENOMEM;
break;
} }
entry = &tree->entries[tree->entry_count++]; tree->entries[tree->entry_count++] = entry;
entry->owner = tree;
entry->owner = tree;
entry->attr = strtol(buffer, &buffer, 8); entry->attr = strtol(buffer, &buffer, 8);
if (*buffer++ != ' ') { if (*buffer++ != ' ') {
...@@ -148,13 +280,12 @@ int git_tree__parse(git_tree *tree) ...@@ -148,13 +280,12 @@ int git_tree__parse(git_tree *tree)
break; break;
} }
entry->filename = git__strdup(buffer); strncpy(entry->filename, buffer, GIT_TREE_MAX_FILENAME);
if (entry->filename == NULL) { while (buffer < buffer_end && *buffer != 0)
error = GIT_EOBJCORRUPTED; buffer++;
}
buffer += strlen(entry->filename) + 1; buffer++;
git_oid_mkraw(&entry->oid, (const unsigned char *)buffer); git_oid_mkraw(&entry->oid, (const unsigned char *)buffer);
buffer += GIT_OID_RAWSZ; buffer += GIT_OID_RAWSZ;
......
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
#include <git/tree.h> #include <git/tree.h>
#include "repository.h" #include "repository.h"
#define GIT_TREE_MAX_FILENAME 255
struct git_tree_entry { struct git_tree_entry {
unsigned int attr; unsigned int attr;
char *filename; char filename[GIT_TREE_MAX_FILENAME];
git_oid oid; git_oid oid;
git_tree *owner; git_tree *owner;
...@@ -15,11 +17,13 @@ struct git_tree_entry { ...@@ -15,11 +17,13 @@ struct git_tree_entry {
struct git_tree { struct git_tree {
git_object object; git_object object;
git_tree_entry *entries; git_tree_entry **entries;
size_t entry_count; size_t entry_count;
size_t array_size;
}; };
void git_tree__free(git_tree *tree); void git_tree__free(git_tree *tree);
int git_tree__parse(git_tree *tree); int git_tree__parse(git_tree *tree);
int git_tree__writeback(git_tree *tree, git_odb_source *src);
#endif #endif
...@@ -14,7 +14,7 @@ BEGIN_TEST(tree_read_test) ...@@ -14,7 +14,7 @@ BEGIN_TEST(tree_read_test)
git_oid id; git_oid id;
git_repository *repo; git_repository *repo;
git_tree *tree; git_tree *tree;
const git_tree_entry *entry; git_tree_entry *entry;
must_pass(git_odb_open(&db, odb_dir)); must_pass(git_odb_open(&db, odb_dir));
...@@ -35,6 +35,7 @@ BEGIN_TEST(tree_read_test) ...@@ -35,6 +35,7 @@ BEGIN_TEST(tree_read_test)
must_be_true(strcmp(git_tree_entry_name(entry), "README") == 0); must_be_true(strcmp(git_tree_entry_name(entry), "README") == 0);
must_be_true(git_tree_entry_2object(entry) != NULL); must_be_true(git_tree_entry_2object(entry) != NULL);
git_repository_free(repo); 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