summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVicent Marti <tanoku@gmail.com>2010-09-21 17:17:10 +0300
committerVicent Marti <tanoku@gmail.com>2010-09-21 17:17:10 +0300
commit2a884588b405c4dee78494119a123fb1878f3490 (patch)
tree04be673b6bd0ea4dd6ac00d8ccf8101e1a9baaec /src
parentd45b4a9a1bcbb157a4f02cf5ed23fde5222db9c8 (diff)
downloadlibgit2-2a884588b405c4dee78494119a123fb1878f3490.tar.gz
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>
Diffstat (limited to 'src')
-rw-r--r--src/git/tree.h84
-rw-r--r--src/repository.c3
-rw-r--r--src/tree.c207
-rw-r--r--src/tree.h8
4 files changed, 256 insertions, 46 deletions
diff --git a/src/git/tree.h b/src/git/tree.h
index 32fd6527a..bec49f245 100644
--- a/src/git/tree.h
+++ b/src/git/tree.h
@@ -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
diff --git a/src/repository.c b/src/repository.c
index dc1fe0e9d..22f2bba40 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -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;
diff --git a/src/tree.c b/src/tree.c
index 670f99838..43a50423d 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -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;
}
-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)
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;
-
- 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));
+ if (tree->entry_count >= tree->array_size)
+ resize_tree_array(tree);
- 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;
diff --git a/src/tree.h b/src/tree.h
index dd15b9b9b..118d2c3d6 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -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