/* * call-seq: * Tree::Builder.new(repository, [tree]) * * Create a new Rugged::Tree::Builder instance to write a tree to * the given +repository+. * * If an optional +tree+ is given, the returned Tree::Builder will be * initialized with the entry of +tree+. Otherwise, the Tree::Builder * will be empty and has to be filled manually. */ static VALUE rb_git_treebuilder_new(int argc, VALUE *argv, VALUE klass) { git_treebuilder *builder; git_repository *repo; git_tree *tree = NULL; VALUE rb_object, rb_builder, rb_repo; int error; if (rb_scan_args(argc, argv, "11", &rb_repo, &rb_object) == 2) { if (!rb_obj_is_kind_of(rb_object, rb_cRuggedTree)) rb_raise(rb_eTypeError, "A Rugged::Tree instance is required"); Data_Get_Struct(rb_object, git_tree, tree); } rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_treebuilder_new(&builder, repo, tree); rugged_exception_check(error); rb_builder = Data_Wrap_Struct(klass, NULL, &rb_git_treebuilder_free, builder); rugged_set_owner(rb_builder, rb_repo); return rb_builder; }
static int tree_write( git_tree **out, git_repository *repo, git_tree *source_tree, const git_oid *object_oid, const char *treeentry_name, unsigned int attributes) { int error; git_treebuilder *tb = NULL; const git_tree_entry *entry; git_oid tree_oid; if ((error = git_treebuilder_new(&tb, repo, source_tree)) < 0) goto cleanup; if (object_oid) { if ((error = git_treebuilder_insert( &entry, tb, treeentry_name, object_oid, attributes)) < 0) goto cleanup; } else { if ((error = git_treebuilder_remove(tb, treeentry_name)) < 0) goto cleanup; } if ((error = git_treebuilder_write(&tree_oid, tb)) < 0) goto cleanup; error = git_tree_lookup(out, repo, &tree_oid); cleanup: git_treebuilder_free(tb); return error; }
static void test_inserting_submodule(void) { git_treebuilder *bld; git_oid sm_id; cl_git_pass(git_oid_fromstr(&sm_id, "da39a3ee5e6b4b0d3255bfef95601890afd80709")); cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL)); cl_git_pass(git_treebuilder_insert(NULL, bld, "sm", &sm_id, GIT_FILEMODE_COMMIT)); git_treebuilder_free(bld); }
void test_object_tree_write__invalid_null_oid(void) { git_treebuilder *bld; git_oid null_oid = {{0}}; cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL)); cl_git_fail(git_treebuilder_insert(NULL, bld, "null_oid_file", &null_oid, GIT_FILEMODE_BLOB)); cl_assert(giterr_last() && strstr(giterr_last()->message, "null OID") != NULL); git_treebuilder_free(bld); }
void test_object_tree_write__protect_filesystems(void) { git_treebuilder *builder; git_oid bid; cl_git_pass(git_oid_fromstr(&bid, "fa49b077972391ad58037050f2a75f74e3671e92")); /* Ensure that (by default) we can write objects with funny names on * platforms that are not affected. */ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); #ifndef GIT_WIN32 cl_git_pass(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB)); cl_git_pass(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB)); #endif #ifndef __APPLE__ cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB)); cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB)); #endif git_treebuilder_free(builder); /* Now turn on core.protectHFS and core.protectNTFS and validate that these * paths are rejected. */ cl_repo_set_bool(g_repo, "core.protectHFS", true); cl_repo_set_bool(g_repo, "core.protectNTFS", true); cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); cl_git_fail(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB)); cl_git_fail(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB)); cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB)); cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB)); git_treebuilder_free(builder); }
PyObject * Repository_TreeBuilder(Repository *self, PyObject *args) { TreeBuilder *builder; git_treebuilder *bld; PyObject *py_src = NULL; git_oid oid; git_tree *tree = NULL; git_tree *must_free = NULL; int err; if (!PyArg_ParseTuple(args, "|O", &py_src)) return NULL; if (py_src) { if (PyObject_TypeCheck(py_src, &TreeType)) { Tree *py_tree = (Tree *)py_src; if (py_tree->repo->repo != self->repo) { /* return Error_set(GIT_EINVALIDARGS); */ return Error_set(GIT_ERROR); } tree = py_tree->tree; } else { err = py_oid_to_git_oid_expand(self->repo, py_src, &oid); if (err < 0) return NULL; err = git_tree_lookup(&tree, self->repo, &oid); if (err < 0) return Error_set(err); must_free = tree; } } err = git_treebuilder_new(&bld, self->repo, tree); if (must_free != NULL) git_tree_free(must_free); if (err < 0) return Error_set(err); builder = PyObject_New(TreeBuilder, &TreeBuilderType); if (builder) { builder->repo = self; builder->bld = bld; Py_INCREF(self); } return (PyObject*)builder; }
void test_object_tree_write__subtree(void) { /* write a hierarchical tree from a memory */ git_treebuilder *builder; git_tree *tree; git_oid id, bid, subtree_id, id2, id3; git_oid id_hiearar; git_oid_fromstr(&id, first_tree); git_oid_fromstr(&id2, second_tree); git_oid_fromstr(&id3, third_tree); git_oid_fromstr(&bid, blob_oid); /* create subtree */ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); cl_git_pass(git_treebuilder_insert( NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); /* -V536 */ cl_git_pass(git_treebuilder_write(&subtree_id, builder)); git_treebuilder_free(builder); /* create parent tree */ cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); cl_git_pass(git_treebuilder_new(&builder, g_repo, tree)); cl_git_pass(git_treebuilder_insert( NULL, builder, "new", &subtree_id, GIT_FILEMODE_TREE)); /* -V536 */ cl_git_pass(git_treebuilder_write(&id_hiearar, builder)); git_treebuilder_free(builder); git_tree_free(tree); cl_assert(git_oid_cmp(&id_hiearar, &id3) == 0); /* check data is correct */ cl_git_pass(git_tree_lookup(&tree, g_repo, &id_hiearar)); cl_assert(2 == git_tree_entrycount(tree)); git_tree_free(tree); }
void test_object_tree_write__filtering(void) { git_treebuilder *builder; int i; git_oid entry_oid, tree_oid; git_tree *tree; git_oid_fromstr(&entry_oid, blob_oid); cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); for (i = 0; _entries[i].filename; ++i) cl_git_pass(git_treebuilder_insert(NULL, builder, _entries[i].filename, &entry_oid, _entries[i].attr)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); cl_assert(git_treebuilder_get(builder, "apple") != NULL); cl_assert(git_treebuilder_get(builder, "aardvark") != NULL); cl_assert(git_treebuilder_get(builder, "last") != NULL); git_treebuilder_filter(builder, treebuilder_filter_prefixed, "apple"); cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder)); cl_assert(git_treebuilder_get(builder, "apple") == NULL); cl_assert(git_treebuilder_get(builder, "aardvark") != NULL); cl_assert(git_treebuilder_get(builder, "last") != NULL); git_treebuilder_filter(builder, treebuilder_filter_prefixed, "a"); cl_assert_equal_i(2, (int)git_treebuilder_entrycount(builder)); cl_assert(git_treebuilder_get(builder, "aardvark") == NULL); cl_assert(git_treebuilder_get(builder, "last") != NULL); cl_git_pass(git_treebuilder_write(&tree_oid, builder)); git_treebuilder_free(builder); cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid)); cl_assert_equal_i(2, (int)git_tree_entrycount(tree)); git_tree_free(tree); }
/* treebuilder stuff */ int luagi_tree_builder_new( lua_State *L ) { git_repository **repo = checkrepo( L, 1 ); git_tree *tree = NULL; if( lua_isuserdata( L, 2) ) { tree = *checktree_at( L, 2 ); } git_treebuilder** builder = lua_newuserdata( L, sizeof( git_treebuilder* ) ); int ret = git_treebuilder_new( builder, *repo, tree ); if( ret ) { return ltk_push_git_error( L ); } ltk_setmetatable( L, LUAGI_TREE_BUILDER_FUNCS ); return 1; }
static void test_invalid_objects(bool should_allow_invalid) { git_treebuilder *builder; git_oid valid_blob_id, invalid_blob_id, valid_tree_id, invalid_tree_id; #define assert_allowed(expr) \ clar__assert(!(expr) == should_allow_invalid, __FILE__, __LINE__, \ (should_allow_invalid ? \ "Expected function call to succeed: " #expr : \ "Expected function call to fail: " #expr), \ NULL, 1) cl_git_pass(git_oid_fromstr(&valid_blob_id, blob_oid)); cl_git_pass(git_oid_fromstr(&invalid_blob_id, "1234567890123456789012345678901234567890")); cl_git_pass(git_oid_fromstr(&valid_tree_id, first_tree)); cl_git_pass(git_oid_fromstr(&invalid_tree_id, "0000000000111111111122222222223333333333")); cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); /* test valid blobs and trees (these should always pass) */ cl_git_pass(git_treebuilder_insert(NULL, builder, "file.txt", &valid_blob_id, GIT_FILEMODE_BLOB)); cl_git_pass(git_treebuilder_insert(NULL, builder, "folder", &valid_tree_id, GIT_FILEMODE_TREE)); /* replace valid files and folders with invalid ones */ assert_allowed(git_treebuilder_insert(NULL, builder, "file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB)); assert_allowed(git_treebuilder_insert(NULL, builder, "folder", &invalid_blob_id, GIT_FILEMODE_BLOB)); /* insert new invalid files and folders */ assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB)); assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_folder", &invalid_blob_id, GIT_FILEMODE_BLOB)); /* insert valid blobs as trees and trees as blobs */ assert_allowed(git_treebuilder_insert(NULL, builder, "file_as_folder", &valid_blob_id, GIT_FILEMODE_TREE)); assert_allowed(git_treebuilder_insert(NULL, builder, "folder_as_file.txt", &valid_tree_id, GIT_FILEMODE_BLOB)); #undef assert_allowed git_treebuilder_free(builder); }
void test_object_tree_write__from_memory(void) { /* write a tree from a memory */ git_treebuilder *builder; git_tree *tree; git_oid id, bid, rid, id2; git_oid_fromstr(&id, first_tree); git_oid_fromstr(&id2, second_tree); git_oid_fromstr(&bid, blob_oid); /* create a second tree from first tree using `git_treebuilder_insert` * on REPOSITORY_FOLDER. */ cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); cl_git_pass(git_treebuilder_new(&builder, g_repo, tree)); cl_git_fail(git_treebuilder_insert(NULL, builder, "", &bid, GIT_FILEMODE_BLOB)); cl_git_fail(git_treebuilder_insert(NULL, builder, "/", &bid, GIT_FILEMODE_BLOB)); cl_git_fail(git_treebuilder_insert(NULL, builder, ".git", &bid, GIT_FILEMODE_BLOB)); cl_git_fail(git_treebuilder_insert(NULL, builder, "..", &bid, GIT_FILEMODE_BLOB)); cl_git_fail(git_treebuilder_insert(NULL, builder, ".", &bid, GIT_FILEMODE_BLOB)); cl_git_fail(git_treebuilder_insert(NULL, builder, "folder/new.txt", &bid, GIT_FILEMODE_BLOB)); cl_git_pass(git_treebuilder_insert( NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); cl_git_pass(git_treebuilder_write(&rid, builder)); cl_assert(git_oid_cmp(&rid, &id2) == 0); git_treebuilder_free(builder); git_tree_free(tree); }
/* "b=name,t=name", blob_id, tree_id */ static void build_test_tree( git_oid *out, git_repository *repo, const char *fmt, ...) { git_oid *id; git_treebuilder *builder; const char *scan = fmt, *next; char type, delimiter; git_filemode_t mode = GIT_FILEMODE_BLOB; git_buf name = GIT_BUF_INIT; va_list arglist; cl_git_pass(git_treebuilder_new(&builder, repo, NULL)); /* start builder */ va_start(arglist, fmt); while (*scan) { switch (type = *scan++) { case 't': case 'T': mode = GIT_FILEMODE_TREE; break; case 'b': case 'B': mode = GIT_FILEMODE_BLOB; break; default: cl_assert(type == 't' || type == 'T' || type == 'b' || type == 'B'); } delimiter = *scan++; /* read and skip delimiter */ for (next = scan; *next && *next != delimiter; ++next) /* seek end */; cl_git_pass(git_buf_set(&name, scan, (size_t)(next - scan))); for (scan = next; *scan && (*scan == delimiter || *scan == ','); ++scan) /* skip delimiter and optional comma */; id = va_arg(arglist, git_oid *); cl_git_pass(git_treebuilder_insert(NULL, builder, name.ptr, id, mode)); } va_end(arglist); cl_git_pass(git_treebuilder_write(out, builder)); git_treebuilder_free(builder); git_buf_free(&name); }
/** * Reads the tree object out of a commit object, or returns an empty tree * if the commit id is zero. */ static int sync_get_tree(git_oid *out, git_repository *repo, const git_oid *commit_id) { int e = 0; git_treebuilder *tb = NULL; git_commit *commit = NULL; if (git_oid_iszero(commit_id)) { git_check(git_treebuilder_new(&tb, repo, NULL)); git_check(git_treebuilder_write(out, tb)); } else { git_check(git_commit_lookup(&commit, repo, commit_id)); git_oid_cpy(out, git_commit_tree_id(commit)); } exit: if (tb) git_treebuilder_free(tb); if (commit) git_commit_free(commit); return e; }
void test_object_tree_write__cruel_paths(void) { static const char *the_paths[] = { "C:\\", " : * ? \" \n < > |", "a\\b", "\\\\b\a", ":\\", "COM1", "foo.aux", REP1024("1234"), /* 4096 char string */ REP1024("12345678"), /* 8192 char string */ "\xC5\xAA\x6E\xC4\xAD\x63\xC5\x8D\x64\x65\xCC\xBD", /* Ūnĭcōde̽ */ NULL }; git_treebuilder *builder; git_tree *tree; git_oid id, bid, subid; const char **scan; int count = 0, i, j; git_tree_entry *te; git_oid_fromstr(&bid, blob_oid); /* create tree */ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); for (scan = the_paths; *scan; ++scan) { cl_git_pass(git_treebuilder_insert( NULL, builder, *scan, &bid, GIT_FILEMODE_BLOB)); count++; } cl_git_pass(git_treebuilder_write(&id, builder)); git_treebuilder_free(builder); /* check data is correct */ cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); cl_assert_equal_i(count, git_tree_entrycount(tree)); for (scan = the_paths; *scan; ++scan) { const git_tree_entry *cte = git_tree_entry_byname(tree, *scan); cl_assert(cte != NULL); cl_assert_equal_s(*scan, git_tree_entry_name(cte)); } for (scan = the_paths; *scan; ++scan) { cl_git_pass(git_tree_entry_bypath(&te, tree, *scan)); cl_assert_equal_s(*scan, git_tree_entry_name(te)); git_tree_entry_free(te); } git_tree_free(tree); /* let's try longer paths */ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); for (scan = the_paths; *scan; ++scan) { cl_git_pass(git_treebuilder_insert( NULL, builder, *scan, &id, GIT_FILEMODE_TREE)); } cl_git_pass(git_treebuilder_write(&subid, builder)); git_treebuilder_free(builder); /* check data is correct */ cl_git_pass(git_tree_lookup(&tree, g_repo, &subid)); cl_assert_equal_i(count, git_tree_entrycount(tree)); for (i = 0; i < count; ++i) { for (j = 0; j < count; ++j) { git_buf b = GIT_BUF_INIT; cl_git_pass(git_buf_joinpath(&b, the_paths[i], the_paths[j])); cl_git_pass(git_tree_entry_bypath(&te, tree, b.ptr)); cl_assert_equal_s(the_paths[j], git_tree_entry_name(te)); git_tree_entry_free(te); git_buf_free(&b); } } git_tree_free(tree); }
int configctl_git_commit(char *path) { int rc; int git_status = -1; git_oid oid_blob; git_oid oid_tree; git_oid oid_commit; git_blob *blob; git_tree *tree_cmt; git_treebuilder *tree_bld; char *file; file = get_file(path); #if 0 // TODO: check if file is changed __debug("%s", file); git_diff_stats *stats; git_diff *diff; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; opts.pathspec.strings = &file; opts.pathspec.count = 1; rc = git_diff_index_to_workdir(&diff, repo, NULL, &opts); if(rc) goto error; int diff_num = git_diff_num_deltas(diff); __debug("%d", diff_num); git_diff_get_stats(&stats, diff); int x = git_diff_stats_files_changed(stats); __debug("%d", x); git_diff_free(diff); #endif rc = git_add(file); if (rc) goto error; rc = git_blob_create_fromworkdir(&oid_blob, repo, file); if (rc) goto error; rc = git_blob_lookup(&blob, repo, &oid_blob); if (rc) goto error; rc = git_treebuilder_new(&tree_bld, repo, NULL ); if (0 == rc) { rc = git_treebuilder_insert(NULL, tree_bld, file, &oid_blob, GIT_FILEMODE_BLOB); if (!rc) { rc = git_treebuilder_write(&oid_tree, tree_bld); if (!rc) { rc = git_tree_lookup(&tree_cmt, repo, &oid_tree); if (0 == rc) { git_commit *commit; commit = get_last_commit(); git_signature_now(&sign, sign_name, sign_email); rc = git_commit_create(&oid_commit, repo, "HEAD", sign, sign, NULL, commit_message, tree_cmt, 1, (const struct git_commit **) &commit); if (!rc) { git_status = 0; __debug("successful git commit"); } git_tree_free( tree_cmt ); git_commit_free(commit); git_signature_free(sign); } } } git_treebuilder_free(tree_bld); } git_blob_free( blob ); error: return git_status; }
/* * And the Lord said: Is this tree properly sorted? */ void test_object_tree_write__sorted_subtrees(void) { git_treebuilder *builder; git_tree *tree; unsigned int i; int position_c = -1, position_cake = -1, position_config = -1; struct { unsigned int attr; const char *filename; } entries[] = { { GIT_FILEMODE_BLOB, ".gitattributes" }, { GIT_FILEMODE_BLOB, ".gitignore" }, { GIT_FILEMODE_BLOB, ".htaccess" }, { GIT_FILEMODE_BLOB, "Capfile" }, { GIT_FILEMODE_BLOB, "Makefile"}, { GIT_FILEMODE_BLOB, "README"}, { GIT_FILEMODE_TREE, "app"}, { GIT_FILEMODE_TREE, "cake"}, { GIT_FILEMODE_TREE, "config"}, { GIT_FILEMODE_BLOB, "c"}, { GIT_FILEMODE_BLOB, "git_test.txt"}, { GIT_FILEMODE_BLOB, "htaccess.htaccess"}, { GIT_FILEMODE_BLOB, "index.php"}, { GIT_FILEMODE_TREE, "plugins"}, { GIT_FILEMODE_TREE, "schemas"}, { GIT_FILEMODE_TREE, "ssl-certs"}, { GIT_FILEMODE_TREE, "vendors"} }; git_oid bid, tid, tree_oid; cl_git_pass(git_oid_fromstr(&bid, blob_oid)); cl_git_pass(git_oid_fromstr(&tid, first_tree)); cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); for (i = 0; i < ARRAY_SIZE(entries); ++i) { git_oid *id = entries[i].attr == GIT_FILEMODE_TREE ? &tid : &bid; cl_git_pass(git_treebuilder_insert(NULL, builder, entries[i].filename, id, entries[i].attr)); } cl_git_pass(git_treebuilder_write(&tree_oid, builder)); cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid)); for (i = 0; i < git_tree_entrycount(tree); i++) { const git_tree_entry *entry = git_tree_entry_byindex(tree, i); if (strcmp(entry->filename, "c") == 0) position_c = i; if (strcmp(entry->filename, "cake") == 0) position_cake = i; if (strcmp(entry->filename, "config") == 0) position_config = i; } git_tree_free(tree); cl_assert(position_c != -1); cl_assert(position_cake != -1); cl_assert(position_config != -1); cl_assert(position_c < position_cake); cl_assert(position_cake < position_config); git_treebuilder_free(builder); }
void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) { git_treebuilder *builder; int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i; git_oid entry_oid, tree_oid; git_tree *tree; cl_git_pass(git_oid_fromstr(&entry_oid, blob_oid)); cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder)); for (i = 0; _entries[i].filename; ++i) cl_git_pass(git_treebuilder_insert(NULL, builder, _entries[i].filename, &entry_oid, _entries[i].attr)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); cl_git_pass(git_treebuilder_remove(builder, "apple")); cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder)); cl_git_pass(git_treebuilder_remove(builder, "apple_after")); cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder)); cl_git_pass(git_treebuilder_insert( NULL, builder, "before_last", &entry_oid, GIT_FILEMODE_BLOB)); cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder)); /* reinsert apple_after */ cl_git_pass(git_treebuilder_insert( NULL, builder, "apple_after", &entry_oid, GIT_FILEMODE_BLOB)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); cl_git_pass(git_treebuilder_remove(builder, "last")); cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder)); /* reinsert last */ cl_git_pass(git_treebuilder_insert( NULL, builder, "last", &entry_oid, GIT_FILEMODE_BLOB)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); cl_git_pass(git_treebuilder_insert( NULL, builder, "apple_extra", &entry_oid, GIT_FILEMODE_BLOB)); cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder)); cl_git_pass(git_treebuilder_write(&tree_oid, builder)); git_treebuilder_free(builder); cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid)); cl_assert_equal_i(7, (int)git_tree_entrycount(tree)); cl_assert(git_tree_entry_byname(tree, ".first") != NULL); cl_assert(git_tree_entry_byname(tree, "apple") == NULL); cl_assert(git_tree_entry_byname(tree, "apple_after") != NULL); cl_assert(git_tree_entry_byname(tree, "apple_extra") != NULL); cl_assert(git_tree_entry_byname(tree, "last") != NULL); aardvark_i = apple_i = apple_after_i = apple_extra_i = last_i = -1; for (i = 0; i < 7; ++i) { const git_tree_entry *entry = git_tree_entry_byindex(tree, i); if (!strcmp(entry->filename, "aardvark")) aardvark_i = i; else if (!strcmp(entry->filename, "apple")) apple_i = i; else if (!strcmp(entry->filename, "apple_after")) apple_after_i = i; else if (!strcmp(entry->filename, "apple_extra")) apple_extra_i = i; else if (!strcmp(entry->filename, "last")) last_i = i; } cl_assert_equal_i(-1, apple_i); cl_assert_equal_i(6, last_i); cl_assert(aardvark_i < apple_after_i); cl_assert(apple_after_i < apple_extra_i); git_tree_free(tree); }
/** * Merges two tree objects, producing a third tree. * The base tree allows the algorithm to distinguish between adds and deletes. * The algorithm always prefers the item from tree 1 when there is a conflict. */ static int sync_merge_trees(git_oid *out, git_repository *repo, const git_oid *base_id, const git_oid *id1, const git_oid *id2) { int e = 0; git_tree *base_tree = NULL; git_tree *tree1 = NULL; git_tree *tree2 = NULL; git_treebuilder *tb = NULL; size_t size1, size2; size_t i1 = 0; size_t i2 = 0; const git_tree_entry *e1 = NULL; const git_tree_entry *e2 = NULL; enum { ONLY1 = 1, ONLY2 = 2, BOTH = 3 } state = BOTH; git_check(git_tree_lookup(&base_tree, repo, base_id)); git_check(git_tree_lookup(&tree1, repo, id1)); git_check(git_tree_lookup(&tree2, repo, id2)); git_check(git_treebuilder_new(&tb, repo, NULL)); size1 = git_tree_entrycount(tree1); size2 = git_tree_entrycount(tree2); while (1) { // Advance to next file: if (state == ONLY1 || state == BOTH) { e1 = i1 < size1 ? git_tree_entry_byindex(tree1, i1++) : NULL; } if (state == ONLY2 || state == BOTH) { e2 = i2 < size2 ? git_tree_entry_byindex(tree2, i2++) : NULL; } // Determine state: if (e1 && e2) { int s = strcmp(git_tree_entry_name(e1), git_tree_entry_name(e2)); state = s < 0 ? ONLY1 : s > 0 ? ONLY2 : BOTH; } else if (e1 && !e2) { state = ONLY1; } else if (!e1 && e2) { state = ONLY2; } else { break; } // Grab the entry in question: const git_tree_entry *entry = (state == ONLY1 || state == BOTH) ? e1 : e2; const git_tree_entry *base_entry = git_tree_entry_byname(base_tree, git_tree_entry_name(entry)); // Decide what to do with the entry: if (state == BOTH && base_entry && GIT_OBJ_TREE == git_tree_entry_type(e1) && GIT_OBJ_TREE == git_tree_entry_type(e2) && GIT_OBJ_TREE == git_tree_entry_type(base_entry)) { // Merge sub-trees: git_oid new_tree; git_check(sync_merge_trees(&new_tree, repo, git_tree_entry_id(base_entry), git_tree_entry_id(e1), git_tree_entry_id(e2))); git_check(git_treebuilder_insert(NULL, tb, git_tree_entry_name(e1), &new_tree, git_tree_entry_filemode(e1))); } else if (state == BOTH && base_entry) { if (git_oid_cmp(git_tree_entry_id(base_entry), git_tree_entry_id(e1))) { // Entry `e1` has changes, so use that: git_check(git_treebuilder_insert(NULL, tb, git_tree_entry_name(e1), git_tree_entry_id(e1), git_tree_entry_filemode(e1))); } else { // Entry `e1` has no changes, so use `e2`: git_check(git_treebuilder_insert(NULL, tb, git_tree_entry_name(e2), git_tree_entry_id(e2), git_tree_entry_filemode(e2))); } } else if (state == BOTH || !base_entry) { // Entry was added, or already present: git_check(git_treebuilder_insert(NULL, tb, git_tree_entry_name(entry), git_tree_entry_id(entry), git_tree_entry_filemode(entry))); } // Otherwise, the entry was deleted. } // Write tree: git_check(git_treebuilder_write(out, tb)); exit: if (base_tree) git_tree_free(base_tree); if (tree1) git_tree_free(tree1); if (tree2) git_tree_free(tree2); if (tb) git_treebuilder_free(tb); return e; }