diff options
author | Michael Tesch <mitesch@microsoft.com> | 2016-08-09 12:23:19 -0400 |
---|---|---|
committer | Michael Tesch <mitesch@microsoft.com> | 2016-12-12 10:46:05 -0500 |
commit | 87aaefe20b2e2fad8f0b8b236d1c23ce9ec5340f (patch) | |
tree | cdb39442af47ecfd98c09342d365ac16bf6f7641 | |
parent | 97e57e8770132d61ff2c36bee2de2c7ac5c9d609 (diff) | |
download | libgit2-87aaefe20b2e2fad8f0b8b236d1c23ce9ec5340f.tar.gz |
write_tree: use shared buffer for writing trees
The function to write trees allocates a new buffer for each tree.
This causes problems with performance when performing a lot
of actions involving writing trees, e.g. when doing many merges.
Fix the issue by instead handing in a shared buffer, which is then
re-used across the calls without having to re-allocate between
calls.
-rw-r--r-- | include/git2/tree.h | 13 | ||||
-rw-r--r-- | src/tree.c | 45 |
2 files changed, 43 insertions, 15 deletions
diff --git a/include/git2/tree.h b/include/git2/tree.h index 2e4735c4b..a38215f9f 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -375,6 +375,19 @@ GIT_EXTERN(void) git_treebuilder_filter( GIT_EXTERN(int) git_treebuilder_write( git_oid *id, git_treebuilder *bld); +/** + * Write the contents of the tree builder as a tree object + * using a shared git_buf. + * + * @see git_treebuilder_write + * + * @param id Pointer to store the OID of the newly written tree + * @param bld Tree builder to write + * @param tree Shared buffer for writing the tree. Will be grown as necessary. + * @return 0 or an error code + */ +GIT_EXTERN(int) git_treebuilder_write_with_buffer( + git_oid *oid, git_treebuilder *bld, git_buf *tree); /** Callback for the tree traversal method */ typedef int (*git_treewalk_cb)( diff --git a/src/tree.c b/src/tree.c index 5db2446bf..0a155a9e4 100644 --- a/src/tree.c +++ b/src/tree.c @@ -515,7 +515,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 +569,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 +601,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,6 +617,7 @@ 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); @@ -641,7 +643,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); @@ -797,19 +800,36 @@ 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; 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 (tree->asize == 0 && + (error = git_buf_grow(tree, entrycount * 72)) < 0) + return error; + git_strmap_foreach_value(bld->map, entry, { if (git_vector_insert(&entries, entry) < 0) return -1; @@ -817,26 +837,21 @@ int git_treebuilder_write(git_oid *oid, git_treebuilder *bld) 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); - 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; } - if (!error && !(error = git_repository_odb__weakptr(&odb, bld->repo))) - error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE); + error = git_odb_write(oid, odb, tree->ptr, tree->size, GIT_OBJ_TREE); - git_buf_free(&tree); git_vector_free(&entries); return error; |