Commit 4a619797 by Carlos Martín Nieto Committed by Carlos Martín Nieto

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.
parent 3f3f6225
...@@ -219,89 +219,101 @@ int git_tree__parse(git_tree *tree, git_odb_object *obj) ...@@ -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);
}
static int write_index(git_oid *oid, git_index *index, const char *base, int baselen, int entry_no, int maxentries) error = git_treebuilder_create(&bld, NULL);
{ if (bld == NULL) {
size_t size, offset;
char *buffer;
int nr, error;
/* Guess at some random initial size */
size = maxentries * 40;
buffer = git__malloc(size);
if (buffer == NULL)
return GIT_ENOMEM; return GIT_ENOMEM;
}
offset = 0; /*
* This loop is unfortunate, but necessary. The index doesn't have
for (nr = entry_no; nr < maxentries; ++nr) { * any directores, so we need to handle that manually, and we
git_index_entry *entry = git_index_get(index, nr); * need to keep track of the current position.
*/
const char *pathname = entry->path, *filename, *dirname; for (i = start; i < entries; ++i) {
int pathlen = strlen(pathname), entrylen; git_index_entry *entry = git_index_get(index, i);
char *filename, *next_slash;
unsigned int write_mode;
git_oid subtree_oid; /*
git_oid *write_oid; * If we've left our (sub)tree, exit the loop and return. The
* first check is an early out (and security for the
/* Did we hit the end of the directory? Return how many we wrote */ * third). The second check is a simple prefix comparison. The
if (baselen >= pathlen || memcmp(base, pathname, baselen) != 0) * third check catches situations where there is a directory
* win32/sys and a file win32mmap.c. Without it, the following
* code believes there is a file win32/mmap.c
*/
if (strlen(entry->path) < dirname_len ||
memcmp(entry->path, dirname, dirname_len) ||
(dirname_len > 0 && entry->path[dirname_len] != '/')) {
break; break;
}
/* Do we have _further_ subdirectories? */ filename = entry->path + dirname_len;
filename = pathname + baselen; if (*filename == '/')
dirname = strchr(filename, '/'); filename++;
next_slash = strchr(filename, '/');
write_oid = &entry->oid; if (next_slash) {
write_mode = entry->mode; git_oid sub_oid;
int written;
if (dirname) { char *subdir, *last_comp;
int subdir_written;
subdir = git__strndup(entry->path, next_slash - entry->path);
#if 0 if (subdir == NULL) {
if (entry->mode != S_IFDIR) { error = GIT_ENOMEM;
free(buffer); goto cleanup;
return GIT_EOBJCORRUPTED;
} }
#endif
subdir_written = write_index(&subtree_oid, index, pathname, dirname - pathname + 1, nr, maxentries);
if (subdir_written < GIT_SUCCESS) { /* Write out the subtree */
free(buffer); written = write_tree(&sub_oid, index, subdir, i);
return subdir_written; if (written < 0) {
error = git__rethrow(written, "Failed to write subtree %s", subdir);
} else {
i = written - 1; /* -1 because of the loop increment */
} }
nr = subdir_written - 1; /*
* We need to figure out what we want toinsert
/* Now we need to write out the directory entry into this tree.. */ * into this tree. If we're traversing
pathlen = dirname - pathname; * deps/zlib/, then we only want to write
write_oid = &subtree_oid; * 'zlib' into the tree.
write_mode = S_IFDIR; */
} last_comp = strrchr(subdir, '/');
if (last_comp) {
entrylen = pathlen - baselen; last_comp++; /* Get rid of the '/' */
if (offset + entrylen + 32 > size) { } else {
size = alloc_nr(offset + entrylen + 32); last_comp = subdir;
buffer = git__realloc(buffer, size); }
error = git_treebuilder_insert(NULL, bld, last_comp, &sub_oid, S_IFDIR);
if (buffer == NULL) free(subdir);
return GIT_ENOMEM; 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");
}
} }
offset += write_index_entry(buffer + offset, write_mode, filename, entrylen, write_oid);
} }
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) ...@@ -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;
} }
......
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