Commit ae996e02 by Vicent Martí

Merge pull request #394 from carlosmn/tree-fromindex

Use git_treebuilder to write the index as a tree
parents 6640266e 4a619797
...@@ -201,89 +201,101 @@ int git_tree__parse(git_tree *tree, git_odb_object *obj) ...@@ -201,89 +201,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)
...@@ -293,7 +305,8 @@ int git_tree_create_fromindex(git_oid *oid, git_index *index) ...@@ -293,7 +305,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