mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-05 12:52:58 +00:00
tree: use git_treebuilder to write the index as a tree
There is no point in reinventing the wheel when using the treebuilder is much more straightforward and makes the code more readable. There is no optimisation, and the performance is no worse than when writing the tree object ourselves.
This commit is contained in:
parent
3f3f6225f8
commit
4a619797ec
151
src/tree.c
151
src/tree.c
@ -219,89 +219,101 @@ int git_tree__parse(git_tree *tree, git_odb_object *obj)
|
|||||||
return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len);
|
return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_index_entry(char *buffer, int mode, const char *path, size_t path_len, const git_oid *oid)
|
static int write_tree(git_oid *oid, git_index *index, const char *dirname, unsigned int start)
|
||||||
{
|
{
|
||||||
int written;
|
git_treebuilder *bld = NULL;
|
||||||
written = sprintf(buffer, "%o %.*s%c", mode, (int)path_len, path, 0);
|
unsigned int i, entries = git_index_entrycount(index);
|
||||||
memcpy(buffer + written, &oid->id, GIT_OID_RAWSZ);
|
int error;
|
||||||
return written + GIT_OID_RAWSZ;
|
size_t dirname_len = strlen(dirname);
|
||||||
|
|
||||||
|
error = git_treebuilder_create(&bld, NULL);
|
||||||
|
if (bld == NULL) {
|
||||||
|
return GIT_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_index(git_oid *oid, git_index *index, const char *base, int baselen, int entry_no, int maxentries)
|
/*
|
||||||
{
|
* This loop is unfortunate, but necessary. The index doesn't have
|
||||||
size_t size, offset;
|
* any directores, so we need to handle that manually, and we
|
||||||
char *buffer;
|
* need to keep track of the current position.
|
||||||
int nr, error;
|
*/
|
||||||
|
for (i = start; i < entries; ++i) {
|
||||||
|
git_index_entry *entry = git_index_get(index, i);
|
||||||
|
char *filename, *next_slash;
|
||||||
|
|
||||||
/* Guess at some random initial size */
|
/*
|
||||||
size = maxentries * 40;
|
* If we've left our (sub)tree, exit the loop and return. The
|
||||||
buffer = git__malloc(size);
|
* first check is an early out (and security for the
|
||||||
if (buffer == NULL)
|
* third). The second check is a simple prefix comparison. The
|
||||||
return GIT_ENOMEM;
|
* third check catches situations where there is a directory
|
||||||
|
* win32/sys and a file win32mmap.c. Without it, the following
|
||||||
offset = 0;
|
* code believes there is a file win32/mmap.c
|
||||||
|
*/
|
||||||
for (nr = entry_no; nr < maxentries; ++nr) {
|
if (strlen(entry->path) < dirname_len ||
|
||||||
git_index_entry *entry = git_index_get(index, nr);
|
memcmp(entry->path, dirname, dirname_len) ||
|
||||||
|
(dirname_len > 0 && entry->path[dirname_len] != '/')) {
|
||||||
const char *pathname = entry->path, *filename, *dirname;
|
|
||||||
int pathlen = strlen(pathname), entrylen;
|
|
||||||
|
|
||||||
unsigned int write_mode;
|
|
||||||
git_oid subtree_oid;
|
|
||||||
git_oid *write_oid;
|
|
||||||
|
|
||||||
/* Did we hit the end of the directory? Return how many we wrote */
|
|
||||||
if (baselen >= pathlen || memcmp(base, pathname, baselen) != 0)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Do we have _further_ subdirectories? */
|
|
||||||
filename = pathname + baselen;
|
|
||||||
dirname = strchr(filename, '/');
|
|
||||||
|
|
||||||
write_oid = &entry->oid;
|
|
||||||
write_mode = entry->mode;
|
|
||||||
|
|
||||||
if (dirname) {
|
|
||||||
int subdir_written;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (entry->mode != S_IFDIR) {
|
|
||||||
free(buffer);
|
|
||||||
return GIT_EOBJCORRUPTED;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
subdir_written = write_index(&subtree_oid, index, pathname, dirname - pathname + 1, nr, maxentries);
|
|
||||||
|
|
||||||
if (subdir_written < GIT_SUCCESS) {
|
|
||||||
free(buffer);
|
|
||||||
return subdir_written;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nr = subdir_written - 1;
|
filename = entry->path + dirname_len;
|
||||||
|
if (*filename == '/')
|
||||||
|
filename++;
|
||||||
|
next_slash = strchr(filename, '/');
|
||||||
|
if (next_slash) {
|
||||||
|
git_oid sub_oid;
|
||||||
|
int written;
|
||||||
|
char *subdir, *last_comp;
|
||||||
|
|
||||||
/* Now we need to write out the directory entry into this tree.. */
|
subdir = git__strndup(entry->path, next_slash - entry->path);
|
||||||
pathlen = dirname - pathname;
|
if (subdir == NULL) {
|
||||||
write_oid = &subtree_oid;
|
error = GIT_ENOMEM;
|
||||||
write_mode = S_IFDIR;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
entrylen = pathlen - baselen;
|
/* Write out the subtree */
|
||||||
if (offset + entrylen + 32 > size) {
|
written = write_tree(&sub_oid, index, subdir, i);
|
||||||
size = alloc_nr(offset + entrylen + 32);
|
if (written < 0) {
|
||||||
buffer = git__realloc(buffer, size);
|
error = git__rethrow(written, "Failed to write subtree %s", subdir);
|
||||||
|
} else {
|
||||||
if (buffer == NULL)
|
i = written - 1; /* -1 because of the loop increment */
|
||||||
return GIT_ENOMEM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += write_index_entry(buffer + offset, write_mode, filename, entrylen, write_oid);
|
/*
|
||||||
|
* We need to figure out what we want toinsert
|
||||||
|
* into this tree. If we're traversing
|
||||||
|
* deps/zlib/, then we only want to write
|
||||||
|
* 'zlib' into the tree.
|
||||||
|
*/
|
||||||
|
last_comp = strrchr(subdir, '/');
|
||||||
|
if (last_comp) {
|
||||||
|
last_comp++; /* Get rid of the '/' */
|
||||||
|
} else {
|
||||||
|
last_comp = subdir;
|
||||||
|
}
|
||||||
|
error = git_treebuilder_insert(NULL, bld, last_comp, &sub_oid, S_IFDIR);
|
||||||
|
free(subdir);
|
||||||
|
if (error < GIT_SUCCESS) {
|
||||||
|
error = git__rethrow(error, "Failed to insert dir");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error = git_treebuilder_insert(NULL, bld, filename, &entry->oid, entry->mode);
|
||||||
|
if (error < GIT_SUCCESS) {
|
||||||
|
error = git__rethrow(error, "Failed to insert file");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error = git_odb_write(oid, index->repository->db, buffer, offset, GIT_OBJ_TREE);
|
error = git_treebuilder_write(oid, index->repository, bld);
|
||||||
free(buffer);
|
if (error < GIT_SUCCESS)
|
||||||
|
error = git__rethrow(error, "Failed to write tree to db");
|
||||||
|
|
||||||
return (error == GIT_SUCCESS) ? nr : git__rethrow(error, "Failed to write index");
|
cleanup:
|
||||||
|
git_treebuilder_free(bld);
|
||||||
|
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
return error;
|
||||||
|
else
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_tree_create_fromindex(git_oid *oid, git_index *index)
|
int git_tree_create_fromindex(git_oid *oid, git_index *index)
|
||||||
@ -311,7 +323,8 @@ int git_tree_create_fromindex(git_oid *oid, git_index *index)
|
|||||||
if (index->repository == NULL)
|
if (index->repository == NULL)
|
||||||
return git__throw(GIT_EBAREINDEX, "Failed to create tree. The index file is not backed up by an existing repository");
|
return git__throw(GIT_EBAREINDEX, "Failed to create tree. The index file is not backed up by an existing repository");
|
||||||
|
|
||||||
error = write_index(oid, index, "", 0, 0, git_index_entrycount(index));
|
/* The tree cache didn't help us */
|
||||||
|
error = write_tree(oid, index, "", 0);
|
||||||
return (error < GIT_SUCCESS) ? git__rethrow(error, "Failed to create tree") : GIT_SUCCESS;
|
return (error < GIT_SUCCESS) ? git__rethrow(error, "Failed to create tree") : GIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user