summaryrefslogtreecommitdiff
path: root/src/tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tree.c')
-rw-r--r--src/tree.c101
1 files changed, 62 insertions, 39 deletions
diff --git a/src/tree.c b/src/tree.c
index 5db2446bf..fcee7f3b3 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -5,9 +5,9 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
-#include "commit.h"
#include "tree.h"
+
+#include "commit.h"
#include "git2/repository.h"
#include "git2/object.h"
#include "fileops.h"
@@ -20,8 +20,6 @@
#define TREE_ENTRY_CHECK_NAMELEN(n) \
if (n > UINT16_MAX) { giterr_set(GITERR_INVALID, "tree entry path too long"); }
-GIT__USE_STRMAP
-
static bool valid_filemode(const int filemode)
{
return (filemode == GIT_FILEMODE_TREE
@@ -447,7 +445,12 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj)
if ((nul = memchr(buffer, 0, buffer_end - buffer)) == NULL)
return tree_error("Failed to parse tree. Object is corrupted", NULL);
- filename_len = nul - buffer;
+ if ((filename_len = nul - buffer) == 0)
+ return tree_error("Failed to parse tree. Can't parse filename", NULL);
+
+ if ((buffer_end - (nul + 1)) < GIT_OID_RAWSZ)
+ return tree_error("Failed to parse tree. Can't parse OID", NULL);
+
/* Allocate the entry */
{
entry = git_array_alloc(tree->entries);
@@ -500,7 +503,7 @@ static int append_entry(
entry->attr = (uint16_t)filemode;
- git_strmap_insert(bld->map, entry->filename, entry, error);
+ git_strmap_insert(bld->map, entry->filename, entry, &error);
if (error < 0) {
git_tree_entry_free(entry);
giterr_set(GITERR_TREE, "failed to append entry %s to the tree builder", filename);
@@ -515,7 +518,8 @@ static int write_tree(
git_repository *repo,
git_index *index,
const char *dirname,
- size_t start)
+ size_t start,
+ git_buf *shared_buf)
{
git_treebuilder *bld = NULL;
size_t i, entries = git_index_entrycount(index);
@@ -568,7 +572,7 @@ static int write_tree(
GITERR_CHECK_ALLOC(subdir);
/* Write out the subtree */
- written = write_tree(&sub_oid, repo, index, subdir, i);
+ written = write_tree(&sub_oid, repo, index, subdir, i, shared_buf);
if (written < 0) {
git__free(subdir);
goto on_error;
@@ -600,7 +604,7 @@ static int write_tree(
}
}
- if (git_treebuilder_write(oid, bld) < 0)
+ if (git_treebuilder_write_with_buffer(oid, bld, shared_buf) < 0)
goto on_error;
git_treebuilder_free(bld);
@@ -616,13 +620,14 @@ int git_tree__write_index(
{
int ret;
git_tree *tree;
+ git_buf shared_buf = GIT_BUF_INIT;
bool old_ignore_case = false;
assert(oid && index && repo);
if (git_index_has_conflicts(index)) {
giterr_set(GITERR_INDEX,
- "Cannot create a tree from a not fully merged index.");
+ "cannot create a tree from a not fully merged index.");
return GIT_EUNMERGED;
}
@@ -641,7 +646,8 @@ int git_tree__write_index(
git_index__set_ignore_case(index, false);
}
- ret = write_tree(oid, repo, index, "", 0);
+ ret = write_tree(oid, repo, index, "", 0, &shared_buf);
+ git_buf_free(&shared_buf);
if (old_ignore_case)
git_index__set_ignore_case(index, true);
@@ -746,7 +752,7 @@ int git_treebuilder_insert(
entry = alloc_entry(filename, strlen(filename), id);
GITERR_CHECK_ALLOC(entry);
- git_strmap_insert(bld->map, entry->filename, entry, error);
+ git_strmap_insert(bld->map, entry->filename, entry, &error);
if (error < 0) {
git_tree_entry_free(entry);
@@ -797,46 +803,60 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
{
+ int error;
+ git_buf buffer = GIT_BUF_INIT;
+
+ error = git_treebuilder_write_with_buffer(oid, bld, &buffer);
+
+ git_buf_free(&buffer);
+ return error;
+}
+
+int git_treebuilder_write_with_buffer(git_oid *oid, git_treebuilder *bld, git_buf *tree)
+{
int error = 0;
size_t i, entrycount;
- git_buf tree = GIT_BUF_INIT;
git_odb *odb;
git_tree_entry *entry;
- git_vector entries;
+ git_vector entries = GIT_VECTOR_INIT;
assert(bld);
+ assert(tree);
+
+ git_buf_clear(tree);
entrycount = git_strmap_num_entries(bld->map);
- if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0)
- return -1;
+ if ((error = git_vector_init(&entries, entrycount, entry_sort_cmp)) < 0)
+ goto out;
+
+ if (tree->asize == 0 &&
+ (error = git_buf_grow(tree, entrycount * 72)) < 0)
+ goto out;
git_strmap_foreach_value(bld->map, entry, {
- if (git_vector_insert(&entries, entry) < 0)
- return -1;
+ if ((error = git_vector_insert(&entries, entry)) < 0)
+ goto out;
});
git_vector_sort(&entries);
- /* Grow the buffer beforehand to an estimated size */
- error = git_buf_grow(&tree, entrycount * 72);
-
for (i = 0; i < entries.length && !error; ++i) {
- git_tree_entry *entry = git_vector_get(&entries, i);
+ entry = git_vector_get(&entries, i);
- git_buf_printf(&tree, "%o ", entry->attr);
- git_buf_put(&tree, entry->filename, entry->filename_len + 1);
- git_buf_put(&tree, (char *)entry->oid->id, GIT_OID_RAWSZ);
+ git_buf_printf(tree, "%o ", entry->attr);
+ git_buf_put(tree, entry->filename, entry->filename_len + 1);
+ git_buf_put(tree, (char *)entry->oid->id, GIT_OID_RAWSZ);
- if (git_buf_oom(&tree))
+ if (git_buf_oom(tree)) {
error = -1;
+ goto out;
+ }
}
+ if ((error = git_repository_odb__weakptr(&odb, bld->repo)) == 0)
+ error = git_odb_write(oid, odb, tree->ptr, tree->size, GIT_OBJ_TREE);
- if (!error &&
- !(error = git_repository_odb__weakptr(&odb, bld->repo)))
- error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
-
- git_buf_free(&tree);
+out:
git_vector_free(&entries);
return error;
@@ -904,7 +924,7 @@ int git_tree_entry_bypath(
filename_len = subpath_len(path);
if (filename_len == 0) {
- giterr_set(GITERR_TREE, "Invalid tree path given");
+ giterr_set(GITERR_TREE, "invalid tree path given");
return GIT_ENOTFOUND;
}
@@ -912,7 +932,7 @@ int git_tree_entry_bypath(
if (entry == NULL) {
giterr_set(GITERR_TREE,
- "the path '%.*s' does not exist in the given tree", filename_len, path);
+ "the path '%.*s' does not exist in the given tree", (int) filename_len, path);
return GIT_ENOTFOUND;
}
@@ -922,7 +942,7 @@ int git_tree_entry_bypath(
* then this entry *must* be a tree */
if (!git_tree_entry__is_tree(entry)) {
giterr_set(GITERR_TREE,
- "the path '%.*s' exists but is not a tree", filename_len, path);
+ "the path '%.*s' exists but is not a tree", (int) filename_len, path);
return GIT_ENOTFOUND;
}
@@ -1022,7 +1042,7 @@ int git_tree_walk(
git_buf root_path = GIT_BUF_INIT;
if (mode != GIT_TREEWALK_POST && mode != GIT_TREEWALK_PRE) {
- giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");
+ giterr_set(GITERR_INVALID, "invalid walking mode for tree walk");
return -1;
}
@@ -1159,8 +1179,8 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli
goto cleanup;
for (i = 0; i < nupdates; i++) {
- const git_tree_update *last_update = i == 0 ? NULL : &updates[i-1];
- const git_tree_update *update = &updates[i];
+ const git_tree_update *last_update = i == 0 ? NULL : git_vector_get(&entries, i-1);
+ const git_tree_update *update = git_vector_get(&entries, i);
size_t common_prefix = 0, steps_up, j;
const char *path;
@@ -1195,6 +1215,9 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli
last = git_array_last(stack);
entry = last->tree ? git_tree_entry_byname(last->tree, component.ptr) : NULL;
+ if (!entry)
+ entry = treebuilder_get(last->bld, component.ptr);
+
if (entry && git_tree_entry_type(entry) != GIT_OBJ_TREE) {
giterr_set(GITERR_TREE, "D/F conflict when updating tree");
error = -1;
@@ -1229,7 +1252,7 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli
const git_tree_entry *e = git_treebuilder_get(last->bld, basename);
if (e && git_tree_entry_type(e) != git_object__type_from_filemode(update->filemode)) {
git__free(basename);
- giterr_set(GITERR_TREE, "Cannot replace '%s' with '%s' at '%s'",
+ giterr_set(GITERR_TREE, "cannot replace '%s' with '%s' at '%s'",
git_object_type2string(git_tree_entry_type(e)),
git_object_type2string(git_object__type_from_filemode(update->filemode)),
update->path);
@@ -1249,7 +1272,7 @@ int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseli
break;
}
default:
- giterr_set(GITERR_TREE, "unkown action for update");
+ giterr_set(GITERR_TREE, "unknown action for update");
error = -1;
goto cleanup;
}