mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-28 19:23:05 +00:00
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>
This commit is contained in:
parent
d45b4a9a1b
commit
2a884588b4
@ -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;
|
||||
|
207
src/tree.c
207
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)
|
||||
{
|
||||
assert(entry && entry->owner);
|
||||
|
||||
entry->attr = attr;
|
||||
entry->owner->object.modified = 1;
|
||||
}
|
||||
|
||||
void git_tree_entry_set_name(git_tree_entry *entry, const char *name)
|
||||
{
|
||||
assert(entry && entry->owner);
|
||||
|
||||
strncpy(entry->filename, name, GIT_TREE_MAX_FILENAME);
|
||||
entry_resort(entry->owner);
|
||||
entry->owner->object.modified = 1;
|
||||
}
|
||||
|
||||
void git_tree_entry_set_id(git_tree_entry *entry, const git_oid *oid)
|
||||
{
|
||||
assert(entry && entry->owner);
|
||||
|
||||
git_oid_cpy(&entry->oid, oid);
|
||||
entry->owner->object.modified = 1;
|
||||
}
|
||||
|
||||
unsigned int git_tree_entry_attributes(git_tree_entry *entry)
|
||||
{
|
||||
return entry->attr;
|
||||
}
|
||||
|
||||
const char *git_tree_entry_name(const git_tree_entry *entry)
|
||||
const char *git_tree_entry_name(git_tree_entry *entry)
|
||||
{
|
||||
return entry->filename;
|
||||
}
|
||||
|
||||
const git_oid *git_tree_entry_id(const git_tree_entry *entry)
|
||||
const git_oid *git_tree_entry_id(git_tree_entry *entry)
|
||||
{
|
||||
return &entry->oid;
|
||||
}
|
||||
|
||||
git_object *git_tree_entry_2object(const git_tree_entry *entry)
|
||||
git_object *git_tree_entry_2object(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)
|
||||
git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
|
||||
{
|
||||
const char *filename = (const char *)key;
|
||||
const git_tree_entry *entry = (const git_tree_entry *)array_member;
|
||||
|
||||
return strcmp(filename, entry->filename);
|
||||
return *(git_tree_entry **)bsearch(filename, tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_cmp);
|
||||
}
|
||||
|
||||
const 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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user