ext/rugged/vendor/libgit2-dist/src/tree.c in rugged-0.16.0 vs ext/rugged/vendor/libgit2-dist/src/tree.c in rugged-0.17.0b1

- old
+ new

@@ -1,7 +1,7 @@ /* - * Copyright (C) 2009-2011 the libgit2 contributors + * Copyright (C) 2009-2012 the libgit2 contributors * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ @@ -13,30 +13,28 @@ #define DEFAULT_TREE_SIZE 16 #define MAX_FILEMODE 0777777 #define MAX_FILEMODE_BYTES 6 -#define ENTRY_IS_TREE(e) ((e)->attr & 040000) - static int valid_attributes(const int attributes) { return attributes >= 0 && attributes <= MAX_FILEMODE; } static int valid_entry_name(const char *filename) { - return strlen(filename) > 0 && strchr(filename, '/') == NULL; + return *filename != '\0' && strchr(filename, '/') == NULL; } static int entry_sort_cmp(const void *a, const void *b) { const git_tree_entry *entry_a = (const git_tree_entry *)(a); const git_tree_entry *entry_b = (const git_tree_entry *)(b); - return git_futils_cmp_path( - entry_a->filename, entry_a->filename_len, ENTRY_IS_TREE(entry_a), - entry_b->filename, entry_b->filename_len, ENTRY_IS_TREE(entry_b)); + return git_path_cmp( + entry_a->filename, entry_a->filename_len, git_tree_entry__is_tree(entry_a), + entry_b->filename, entry_b->filename_len, git_tree_entry__is_tree(entry_b)); } struct tree_key_search { const char *filename; @@ -97,11 +95,11 @@ /* We found a common prefix. Look forward as long as * there are entries that share the common prefix */ for (i = homing; i < (int)entries->length; ++i) { entry = entries->contents[i]; - if (homing_search_cmp(&ksearch, entry) != 0) + if (homing_search_cmp(&ksearch, entry) < 0) break; if (strcmp(filename, entry->filename) == 0) return i; } @@ -109,11 +107,11 @@ /* If we haven't found our filename yet, look backwards * too as long as we have entries with the same prefix */ for (i = homing - 1; i >= 0; --i) { entry = entries->contents[i]; - if (homing_search_cmp(&ksearch, entry) != 0) + if (homing_search_cmp(&ksearch, entry) > 0) break; if (strcmp(filename, entry->filename) == 0) return i; } @@ -170,11 +168,14 @@ return GIT_OBJ_TREE; else return GIT_OBJ_BLOB; } -int git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry) +int git_tree_entry_to_object( + git_object **object_out, + git_repository *repo, + const git_tree_entry *entry) { assert(entry && object_out); return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY); } @@ -195,51 +196,75 @@ { assert(tree); return git_vector_get(&tree->entries, idx); } +int git_tree__prefix_position(git_tree *tree, const char *path) +{ + git_vector *entries = &tree->entries; + struct tree_key_search ksearch; + unsigned int at_pos; + + ksearch.filename = path; + ksearch.filename_len = strlen(path); + + /* Find tree entry with appropriate prefix */ + git_vector_bsearch3(&at_pos, entries, &homing_search_cmp, &ksearch); + + for (; at_pos < entries->length; ++at_pos) { + const git_tree_entry *entry = entries->contents[at_pos]; + if (homing_search_cmp(&ksearch, entry) < 0) + break; + } + + for (; at_pos > 0; --at_pos) { + const git_tree_entry *entry = entries->contents[at_pos - 1]; + if (homing_search_cmp(&ksearch, entry) > 0) + break; + } + + return at_pos; +} + unsigned int git_tree_entrycount(git_tree *tree) { assert(tree); return tree->entries.length; } +static int tree_error(const char *str) +{ + giterr_set(GITERR_TREE, "%s", str); + return -1; +} + static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end) { - int error = GIT_SUCCESS; + if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0) + return -1; - if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS) - return GIT_ENOMEM; - while (buffer < buffer_end) { git_tree_entry *entry; int tmp; entry = git__calloc(1, sizeof(git_tree_entry)); - if (entry == NULL) { - error = GIT_ENOMEM; - break; - } + GITERR_CHECK_ALLOC(entry); - if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS) - return GIT_ENOMEM; + if (git_vector_insert(&tree->entries, entry) < 0) + return -1; - if (git__strtol32(&tmp, buffer, &buffer, 8) < GIT_SUCCESS || + if (git__strtol32(&tmp, buffer, &buffer, 8) < 0 || !buffer || !valid_attributes(tmp)) - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Can't parse attributes"); + return tree_error("Failed to parse tree. Can't parse attributes"); entry->attr = tmp; - if (*buffer++ != ' ') { - error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted"); - break; - } + if (*buffer++ != ' ') + return tree_error("Failed to parse tree. Object is corrupted"); - if (memchr(buffer, 0, buffer_end - buffer) == NULL) { - error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted"); - break; - } + if (memchr(buffer, 0, buffer_end - buffer) == NULL) + return tree_error("Failed to parse tree. Object is corrupted"); entry->filename = git__strdup(buffer); entry->filename_len = strlen(buffer); while (buffer < buffer_end && *buffer != 0) @@ -249,11 +274,11 @@ git_oid_fromraw(&entry->oid, (const unsigned char *)buffer); buffer += GIT_OID_RAWSZ; } - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse buffer"); + return 0; } int git_tree__parse(git_tree *tree, git_odb_object *obj) { assert(tree); @@ -280,26 +305,23 @@ static int append_entry(git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes) { git_tree_entry *entry; - if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL) - return GIT_ENOMEM; + entry = git__calloc(1, sizeof(git_tree_entry)); + GITERR_CHECK_ALLOC(entry); - memset(entry, 0x0, sizeof(git_tree_entry)); entry->filename = git__strdup(filename); entry->filename_len = strlen(entry->filename); - bld->entry_count++; - git_oid_cpy(&entry->oid, id); entry->attr = attributes; if (git_vector_insert(&bld->entries, entry) < 0) - return GIT_ENOMEM; + return -1; - return GIT_SUCCESS; + return 0; } static int write_tree( git_oid *oid, git_repository *repo, @@ -320,11 +342,11 @@ return find_next_dir(dirname, index, start); } error = git_treebuilder_create(&bld, NULL); if (bld == NULL) { - return GIT_ENOMEM; + return -1; } /* * This loop is unfortunate, but necessary. The index doesn't have * any directores, so we need to handle that manually, and we @@ -356,19 +378,17 @@ git_oid sub_oid; int written; char *subdir, *last_comp; subdir = git__strndup(entry->path, next_slash - entry->path); - if (subdir == NULL) { - error = GIT_ENOMEM; - goto cleanup; - } + GITERR_CHECK_ALLOC(subdir); /* Write out the subtree */ written = write_tree(&sub_oid, repo, index, subdir, i); if (written < 0) { - error = git__rethrow(written, "Failed to write subtree %s", subdir); + tree_error("Failed to write subtree"); + goto on_error; } else { i = written - 1; /* -1 because of the loop increment */ } /* @@ -383,55 +403,53 @@ } else { last_comp = subdir; } error = append_entry(bld, last_comp, &sub_oid, S_IFDIR); git__free(subdir); - if (error < GIT_SUCCESS) { - error = git__rethrow(error, "Failed to insert dir"); - goto cleanup; + if (error < 0) { + tree_error("Failed to insert dir"); + goto on_error; } } else { error = append_entry(bld, filename, &entry->oid, entry->mode); - if (error < GIT_SUCCESS) { - error = git__rethrow(error, "Failed to insert file"); + if (error < 0) { + tree_error("Failed to insert file"); + goto on_error; } } } - error = git_treebuilder_write(oid, repo, bld); - if (error < GIT_SUCCESS) - error = git__rethrow(error, "Failed to write tree to db"); + if (git_treebuilder_write(oid, repo, bld) < 0) + goto on_error; - cleanup: git_treebuilder_free(bld); + return i; - if (error < GIT_SUCCESS) - return error; - else - return i; +on_error: + git_treebuilder_free(bld); + return -1; } int git_tree_create_fromindex(git_oid *oid, git_index *index) { + int ret; git_repository *repo; - int error; repo = (git_repository *)GIT_REFCOUNT_OWNER(index); if (repo == NULL) - return git__throw(GIT_EBAREINDEX, - "Failed to create tree. " - "The index file is not backed up by an existing repository"); + return tree_error("Failed to create tree. " + "The index file is not backed up by an existing repository"); if (index->tree != NULL && index->tree->entries >= 0) { git_oid_cpy(oid, &index->tree->oid); - return GIT_SUCCESS; + return 0; } /* The tree cache didn't help us */ - error = write_tree(oid, repo, index, "", 0); - return (error < GIT_SUCCESS) ? git__rethrow(error, "Failed to create tree") : GIT_SUCCESS; + ret = write_tree(oid, repo, index, "", 0); + return ret < 0 ? ret : 0; } static void sort_entries(git_treebuilder *bld) { git_vector_sort(&bld->entries); @@ -443,80 +461,74 @@ unsigned int i, source_entries = DEFAULT_TREE_SIZE; assert(builder_p); bld = git__calloc(1, sizeof(git_treebuilder)); - if (bld == NULL) - return GIT_ENOMEM; + GITERR_CHECK_ALLOC(bld); if (source != NULL) source_entries = source->entries.length; - if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < GIT_SUCCESS) { - git__free(bld); - return GIT_ENOMEM; - } + if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < 0) + goto on_error; if (source != NULL) { for (i = 0; i < source->entries.length; ++i) { git_tree_entry *entry_src = source->entries.contents[i]; - if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0) { - git_treebuilder_free(bld); - return GIT_ENOMEM; - } + if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0) + goto on_error; } } *builder_p = bld; - return GIT_SUCCESS; + return 0; + +on_error: + git_treebuilder_free(bld); + return -1; } int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes) { git_tree_entry *entry; int pos; assert(bld && id && filename); if (!valid_attributes(attributes)) - return git__throw(GIT_ERROR, "Failed to insert entry. Invalid attributes"); + return tree_error("Failed to insert entry. Invalid attributes"); if (!valid_entry_name(filename)) - return git__throw(GIT_ERROR, "Failed to insert entry. Invalid name for a tree entry"); + return tree_error("Failed to insert entry. Invalid name for a tree entry"); pos = tree_key_search(&bld->entries, filename); if (pos >= 0) { entry = git_vector_get(&bld->entries, pos); - if (entry->removed) { + if (entry->removed) entry->removed = 0; - bld->entry_count++; - } } else { - if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL) - return GIT_ENOMEM; + entry = git__calloc(1, sizeof(git_tree_entry)); + GITERR_CHECK_ALLOC(entry); - memset(entry, 0x0, sizeof(git_tree_entry)); entry->filename = git__strdup(filename); entry->filename_len = strlen(entry->filename); - - bld->entry_count++; } git_oid_cpy(&entry->oid, id); entry->attr = attributes; if (pos == GIT_ENOTFOUND) { if (git_vector_insert(&bld->entries, entry) < 0) - return GIT_ENOMEM; + return -1; } if (entry_out != NULL) *entry_out = entry; - return GIT_SUCCESS; + return 0; } static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename) { int idx; @@ -543,21 +555,19 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename) { git_tree_entry *remove_ptr = treebuilder_get(bld, filename); if (remove_ptr == NULL || remove_ptr->removed) - return git__throw(GIT_ENOTFOUND, "Failed to remove entry. File isn't in the tree"); + return tree_error("Failed to remove entry. File isn't in the tree"); remove_ptr->removed = 1; - bld->entry_count--; - return GIT_SUCCESS; + return 0; } int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld) { unsigned int i; - int error; git_buf tree = GIT_BUF_INIT; git_odb *odb; assert(bld); @@ -575,25 +585,26 @@ 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)) { - git_buf_free(&tree); - return git__throw(GIT_ENOMEM, "Not enough memory to build the tree data"); - } + if (git_buf_oom(&tree)) + goto on_error; - error = git_repository_odb__weakptr(&odb, repo); - if (error < GIT_SUCCESS) { - git_buf_free(&tree); - return error; - } + if (git_repository_odb__weakptr(&odb, repo) < 0) + goto on_error; - error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE); + + if (git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE) < 0) + goto on_error; + git_buf_free(&tree); + return 0; - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write tree"); +on_error: + git_buf_free(&tree); + return -1; } void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload) { unsigned int i; @@ -629,57 +640,62 @@ } static int tree_frompath( git_tree **parent_out, git_tree *root, - const char *treeentry_path, - int offset) + git_buf *treeentry_path, + size_t offset) { char *slash_pos = NULL; const git_tree_entry* entry; - int error = GIT_SUCCESS; + int error = 0; git_tree *subtree; - if (!*(treeentry_path + offset)) - return git__rethrow(GIT_EINVALIDPATH, - "Invalid relative path to a tree entry '%s'.", treeentry_path); + if (!*(treeentry_path->ptr + offset)) { + giterr_set(GITERR_INVALID, + "Invalid relative path to a tree entry '%s'.", treeentry_path->ptr); + return -1; + } - slash_pos = (char *)strchr(treeentry_path + offset, '/'); + slash_pos = (char *)strchr(treeentry_path->ptr + offset, '/'); if (slash_pos == NULL) return git_tree_lookup( parent_out, root->object.repo, git_object_id((const git_object *)root) ); - if (slash_pos == treeentry_path + offset) - return git__rethrow(GIT_EINVALIDPATH, - "Invalid relative path to a tree entry '%s'.", treeentry_path); + if (slash_pos == treeentry_path->ptr + offset) { + giterr_set(GITERR_INVALID, + "Invalid relative path to a tree entry '%s'.", treeentry_path->ptr); + return -1; + } *slash_pos = '\0'; - entry = git_tree_entry_byname(root, treeentry_path + offset); + entry = git_tree_entry_byname(root, treeentry_path->ptr + offset); if (slash_pos != NULL) *slash_pos = '/'; - if (entry == NULL) - return git__rethrow(GIT_ENOTFOUND, + if (entry == NULL) { + giterr_set(GITERR_TREE, "No tree entry can be found from " - "the given tree and relative path '%s'.", treeentry_path); + "the given tree and relative path '%s'.", treeentry_path->ptr); + return GIT_ENOTFOUND; + } - error = git_tree_lookup(&subtree, root->object.repo, &entry->oid); - if (error < GIT_SUCCESS) + if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0) return error; error = tree_frompath( parent_out, subtree, treeentry_path, - slash_pos - treeentry_path + 1 + (slash_pos - treeentry_path->ptr) + 1 ); git_tree_free(subtree); return error; } @@ -687,72 +703,83 @@ int git_tree_get_subtree( git_tree **subtree, git_tree *root, const char *subtree_path) { - char buffer[GIT_PATH_MAX]; + int error; + git_buf buffer = GIT_BUF_INIT; assert(subtree && root && subtree_path); - strncpy(buffer, subtree_path, GIT_PATH_MAX); - return tree_frompath(subtree, root, buffer, 0); + if ((error = git_buf_sets(&buffer, subtree_path)) == 0) + error = tree_frompath(subtree, root, &buffer, 0); + + git_buf_free(&buffer); + + return error; } static int tree_walk_post( git_tree *tree, git_treewalk_cb callback, - char *root, - size_t root_len, + git_buf *path, void *payload) { - int error; + int error = 0; unsigned int i; for (i = 0; i < tree->entries.length; ++i) { git_tree_entry *entry = tree->entries.contents[i]; - root[root_len] = '\0'; - - if (callback(root, entry, payload) < 0) + if (callback(path->ptr, entry, payload) < 0) continue; - if (ENTRY_IS_TREE(entry)) { + if (git_tree_entry__is_tree(entry)) { git_tree *subtree; + size_t path_len = git_buf_len(path); if ((error = git_tree_lookup( &subtree, tree->object.repo, &entry->oid)) < 0) - return error; + break; - strcpy(root + root_len, entry->filename); - root[root_len + entry->filename_len] = '/'; + /* append the next entry to the path */ + git_buf_puts(path, entry->filename); + git_buf_putc(path, '/'); - tree_walk_post(subtree, - callback, root, - root_len + entry->filename_len + 1, - payload - ); + if (git_buf_oom(path)) + return -1; + if (tree_walk_post(subtree, callback, path, payload) < 0) + return -1; + + git_buf_truncate(path, path_len); git_tree_free(subtree); } } - return GIT_SUCCESS; + return 0; } int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload) { - char root_path[GIT_PATH_MAX]; + int error = 0; + git_buf root_path = GIT_BUF_INIT; - root_path[0] = '\0'; switch (mode) { case GIT_TREEWALK_POST: - return tree_walk_post(tree, callback, root_path, 0, payload); + error = tree_walk_post(tree, callback, &root_path, payload); + break; case GIT_TREEWALK_PRE: - return git__throw(GIT_ENOTIMPLEMENTED, - "Preorder tree walking is still not implemented"); + tree_error("Preorder tree walking is still not implemented"); + return -1; default: - return git__throw(GIT_EINVALIDARGS, - "Invalid walking mode for tree walk"); + giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk"); + return -1; } + + git_buf_free(&root_path); + + return error; } +