]> git.proxmox.com Git - libgit2.git/blobdiff - tests/checkout/tree.c
New upstream version 1.3.0+dfsg.1
[libgit2.git] / tests / checkout / tree.c
index 0fabadc8d8e6901870f35ed5038df14be4d27a2c..3241a3eb7672dbfe294ca0fc870833a95b63d3db 100644 (file)
@@ -4,18 +4,28 @@
 #include "git2/checkout.h"
 #include "repository.h"
 #include "buffer.h"
-#include "fileops.h"
+#include "futils.h"
 
 static git_repository *g_repo;
 static git_checkout_options g_opts;
 static git_object *g_object;
 
+static void assert_status_entrycount(git_repository *repo, size_t count)
+{
+       git_status_list *status;
+
+       cl_git_pass(git_status_list_new(&status, repo, NULL));
+       cl_assert_equal_i(count, git_status_list_entrycount(status));
+
+       git_status_list_free(status);
+}
+
 void test_checkout_tree__initialize(void)
 {
        g_repo = cl_git_sandbox_init("testrepo");
 
        GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTIONS_VERSION);
-       g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+       g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
 }
 
 void test_checkout_tree__cleanup(void)
@@ -63,7 +73,7 @@ void test_checkout_tree__can_checkout_and_remove_directory(void)
        cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
        cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
 
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
 
        cl_assert_equal_i(true, git_path_isdir("./testrepo/ab/"));
        cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
@@ -78,7 +88,7 @@ void test_checkout_tree__can_checkout_and_remove_directory(void)
        cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
        cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
 
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master", NULL, NULL));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
 
        /* This directory should no longer exist */
        cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
@@ -160,10 +170,10 @@ void test_checkout_tree__can_switch_branches(void)
        opts.checkout_strategy = GIT_CHECKOUT_FORCE;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
 
        cl_assert(git_path_isfile("testrepo/README"));
        cl_assert(git_path_isfile("testrepo/branch_file.txt"));
@@ -180,10 +190,10 @@ void test_checkout_tree__can_switch_branches(void)
        opts.checkout_strategy = GIT_CHECKOUT_SAFE;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
 
        cl_assert(git_path_isfile("testrepo/README"));
        cl_assert(git_path_isfile("testrepo/branch_file.txt"));
@@ -250,10 +260,10 @@ static int checkout_tree_with_blob_ignored_in_workdir(int strategy, bool isdir)
        opts.checkout_strategy = GIT_CHECKOUT_FORCE;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
 
        cl_assert(git_path_isfile("testrepo/README"));
        cl_assert(git_path_isfile("testrepo/branch_file.txt"));
@@ -290,7 +300,7 @@ static int checkout_tree_with_blob_ignored_in_workdir(int strategy, bool isdir)
        cl_assert_equal_i(1, ignored);
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        error = git_checkout_tree(g_repo, obj, &opts);
 
@@ -306,14 +316,14 @@ void test_checkout_tree__conflict_on_ignored_when_not_overwriting(void)
        cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir(
                GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, false));
 
-       cl_assert_equal_i(GIT_EMERGECONFLICT, error);
+       cl_assert_equal_i(GIT_ECONFLICT, error);
 }
 
 void test_checkout_tree__can_overwrite_ignored_by_default(void)
 {
        cl_git_pass(checkout_tree_with_blob_ignored_in_workdir(GIT_CHECKOUT_SAFE, false));
 
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
 
        cl_assert(git_path_isfile("testrepo/ab/4.txt"));
 
@@ -327,14 +337,14 @@ void test_checkout_tree__conflict_on_ignored_folder_when_not_overwriting(void)
        cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir(
                GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, true));
 
-       cl_assert_equal_i(GIT_EMERGECONFLICT, error);
+       cl_assert_equal_i(GIT_ECONFLICT, error);
 }
 
 void test_checkout_tree__can_overwrite_ignored_folder_by_default(void)
 {
        cl_git_pass(checkout_tree_with_blob_ignored_in_workdir(GIT_CHECKOUT_SAFE, true));
 
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
 
        cl_assert(git_path_isfile("testrepo/ab/4.txt"));
 
@@ -364,10 +374,10 @@ void test_checkout_tree__can_update_only(void)
        opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
 
        assert_on_branch(g_repo, "dir");
 
@@ -396,7 +406,7 @@ void test_checkout_tree__can_checkout_with_pattern(void)
 
        cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
        cl_git_pass(
-               git_repository_set_head_detached(g_repo, git_object_id(g_object), NULL, NULL));
+               git_repository_set_head_detached(g_repo, git_object_id(g_object)));
 
        git_object_free(g_object);
        g_object = NULL;
@@ -408,7 +418,7 @@ void test_checkout_tree__can_checkout_with_pattern(void)
 
        /* now to a narrow patterned checkout */
 
-       g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+       g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
        g_opts.paths.strings = entries;
        g_opts.paths.count = 1;
 
@@ -422,6 +432,41 @@ void test_checkout_tree__can_checkout_with_pattern(void)
        cl_assert(git_path_exists("testrepo/new.txt"));
 }
 
+void test_checkout_tree__pathlist_checkout_ignores_non_matches(void)
+{
+       char *entries[] = { "branch_file.txt", "link_to_new.txt" };
+
+       /* reset to beginning of history (i.e. just a README file) */
+
+       g_opts.checkout_strategy =
+               GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+       cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
+
+       cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
+
+       cl_assert(git_path_exists("testrepo/README"));
+       cl_assert(git_path_exists("testrepo/branch_file.txt"));
+       cl_assert(git_path_exists("testrepo/link_to_new.txt"));
+       cl_assert(git_path_exists("testrepo/new.txt"));
+
+       git_object_free(g_object);
+       cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+       g_opts.checkout_strategy =
+               GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
+       g_opts.paths.strings = entries;
+       g_opts.paths.count = 2;
+
+       cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+       cl_assert(git_path_exists("testrepo/README"));
+       cl_assert(!git_path_exists("testrepo/branch_file.txt"));
+       cl_assert(!git_path_exists("testrepo/link_to_new.txt"));
+       cl_assert(git_path_exists("testrepo/new.txt"));
+}
+
 void test_checkout_tree__can_disable_pattern_match(void)
 {
        char *entries[] = { "b*.txt" };
@@ -435,7 +480,7 @@ void test_checkout_tree__can_disable_pattern_match(void)
 
        cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
        cl_git_pass(
-               git_repository_set_head_detached(g_repo, git_object_id(g_object), NULL, NULL));
+               git_repository_set_head_detached(g_repo, git_object_id(g_object)));
 
        git_object_free(g_object);
        g_object = NULL;
@@ -445,7 +490,8 @@ void test_checkout_tree__can_disable_pattern_match(void)
        /* now to a narrow patterned checkout, but disable pattern */
 
        g_opts.checkout_strategy =
-               GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
+               GIT_CHECKOUT_SAFE |
+               GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
        g_opts.paths.strings = entries;
        g_opts.paths.count = 1;
 
@@ -457,7 +503,7 @@ void test_checkout_tree__can_disable_pattern_match(void)
 
        /* let's try that again, but allow the pattern match */
 
-       g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+       g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
 
        cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
 
@@ -480,11 +526,11 @@ void assert_conflict(
        /* Create a branch pointing at the parent */
        cl_git_pass(git_revparse_single(&g_object, g_repo, parent_sha));
        cl_git_pass(git_branch_create(&branch, g_repo,
-               "potential_conflict", (git_commit *)g_object, 0, NULL, NULL));
+               "potential_conflict", (git_commit *)g_object, 0));
 
        /* Make HEAD point to this branch */
        cl_git_pass(git_reference_symbolic_create(
-               &head, g_repo, "HEAD", git_reference_name(branch), 1, NULL, NULL));
+               &head, g_repo, "HEAD", git_reference_name(branch), 1, NULL));
        git_reference_free(head);
        git_reference_free(branch);
 
@@ -495,8 +541,9 @@ void assert_conflict(
        /* Hack-ishy workaound to ensure *all* the index entries
         * match the content of the tree
         */
-       cl_git_pass(git_object_peel(&hack_tree, g_object, GIT_OBJ_TREE));
+       cl_git_pass(git_object_peel(&hack_tree, g_object, GIT_OBJECT_TREE));
        cl_git_pass(git_index_read_tree(index, (git_tree *)hack_tree));
+       cl_git_pass(git_index_write(index));
        git_object_free(hack_tree);
        git_object_free(g_object);
        g_object = NULL;
@@ -504,14 +551,14 @@ void assert_conflict(
        /* Create a conflicting file */
        cl_git_pass(git_buf_joinpath(&file_path, "./testrepo", entry_path));
        cl_git_mkfile(git_buf_cstr(&file_path), new_content);
-       git_buf_free(&file_path);
+       git_buf_dispose(&file_path);
 
        /* Trying to checkout the original commit */
        cl_git_pass(git_revparse_single(&g_object, g_repo, commit_sha));
 
        g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
        cl_assert_equal_i(
-               GIT_EMERGECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
+               GIT_ECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
 
        /* Stage the conflicting change */
        cl_git_pass(git_index_add_bypath(index, entry_path));
@@ -519,10 +566,10 @@ void assert_conflict(
        git_index_free(index);
 
        cl_assert_equal_i(
-               GIT_EMERGECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
+               GIT_ECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
 }
 
-void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECONFLICT(void)
+void test_checkout_tree__checking_out_a_conflicting_type_change_returns_ECONFLICT(void)
 {
        /*
         * 099faba adds a symlink named 'link_to_new.txt'
@@ -532,7 +579,7 @@ void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECO
        assert_conflict("link_to_new.txt", "old.txt", "a65fedf", "099faba");
 }
 
-void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECONFLICT_2(void)
+void test_checkout_tree__checking_out_a_conflicting_type_change_returns_ECONFLICT_2(void)
 {
        /*
         * cf80f8d adds a directory named 'a/'
@@ -542,7 +589,7 @@ void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECO
        assert_conflict("a", "hello\n", "a4a7dce", "cf80f8d");
 }
 
-void test_checkout_tree__checking_out_a_conflicting_content_change_returns_EMERGECONFLICT(void)
+void test_checkout_tree__checking_out_a_conflicting_content_change_returns_ECONFLICT(void)
 {
        /*
         * c47800c adds a symlink named 'branch_file.txt'
@@ -571,7 +618,7 @@ void test_checkout_tree__donot_update_deleted_file_by_default(void)
 
        cl_git_pass(git_oid_fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
        cl_git_pass(git_commit_lookup(&old_commit, g_repo, &old_id));
-       cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD, NULL, NULL, NULL));
+       cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD, NULL));
 
        cl_git_pass(p_unlink("testrepo/branch_file.txt"));
        cl_git_pass(git_index_remove_bypath(index ,"branch_file.txt"));
@@ -629,7 +676,7 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void)
        assert_on_branch(g_repo, "master");
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        ca.filename = "new.txt";
        ca.error = -5555;
@@ -645,7 +692,14 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void)
        cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), -5555);
 
        cl_assert(!git_path_exists("testrepo/new.txt"));
-       cl_assert_equal_i(4, ca.count);
+
+       /* on case-insensitive FS = a/b.txt, branch_file.txt, new.txt */
+       /* on case-sensitive FS   = README, then above */
+
+       if (git_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */
+               cl_assert_equal_i(3, ca.count);
+       else
+               cl_assert_equal_i(4, ca.count);
 
        /* and again with a different stopping point and return code */
        ca.filename = "README";
@@ -655,7 +709,11 @@ void test_checkout_tree__can_cancel_checkout_from_notify(void)
        cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), 123);
 
        cl_assert(!git_path_exists("testrepo/new.txt"));
-       cl_assert_equal_i(1, ca.count);
+
+       if (git_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */
+               cl_assert_equal_i(4, ca.count);
+       else
+               cl_assert_equal_i(1, ca.count);
 
        git_object_free(obj);
 }
@@ -676,13 +734,15 @@ void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void)
        cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
 
        cl_git_pass(git_checkout_tree(g_repo, (git_object *)commit, &opts));
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master", NULL, NULL));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
 
        cl_git_pass(p_mkdir("./testrepo/this-is-dir", 0777));
        cl_git_mkfile("./testrepo/this-is-dir/contained_file", "content\n");
 
        cl_git_pass(git_index_add_bypath(index, "this-is-dir/contained_file"));
-       git_index_write_tree(&tree_id, index);
+       cl_git_pass(git_index_write(index));
+
+       cl_git_pass(git_index_write_tree(&tree_id, index));
        cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
 
        cl_git_pass(p_unlink("./testrepo/this-is-dir/contained_file"));
@@ -736,7 +796,7 @@ void test_checkout_tree__can_write_to_empty_dirs(void)
        opts.checkout_strategy = GIT_CHECKOUT_FORCE;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
 
@@ -755,7 +815,7 @@ void test_checkout_tree__fails_when_dir_in_use(void)
        opts.checkout_strategy = GIT_CHECKOUT_FORCE;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
 
@@ -766,7 +826,7 @@ void test_checkout_tree__fails_when_dir_in_use(void)
        cl_git_pass(p_chdir("testrepo/a"));
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
 
@@ -789,7 +849,7 @@ void test_checkout_tree__can_continue_when_dir_in_use(void)
                GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
 
@@ -800,7 +860,7 @@ void test_checkout_tree__can_continue_when_dir_in_use(void)
        cl_git_pass(p_chdir("testrepo/a"));
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
 
@@ -824,14 +884,15 @@ void test_checkout_tree__target_directory_from_bare(void)
        g_repo = cl_git_sandbox_init("testrepo.git");
        cl_assert(git_repository_is_bare(g_repo));
 
-       opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+       opts.checkout_strategy = GIT_CHECKOUT_SAFE |
+               GIT_CHECKOUT_RECREATE_MISSING;
 
        opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
        opts.notify_cb = checkout_count_callback;
        opts.notify_payload = &cts;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
-       cl_git_pass(git_object_lookup(&g_object, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&g_object, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_fail(git_checkout_tree(g_repo, g_object, &opts));
 
@@ -854,9 +915,9 @@ void test_checkout_tree__target_directory_from_bare(void)
 
 void test_checkout_tree__extremely_long_file_name(void)
 {
-       // A utf-8 string with 83 characters, but 249 bytes.
+       /* A utf-8 string with 83 characters, but 249 bytes. */
        const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97";
-       char path[1024];
+       char path[1024] = {0};
 
        g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
        cl_git_pass(git_revparse_single(&g_object, g_repo, "long-file-name"));
@@ -880,20 +941,20 @@ static void create_conflict(const char *path)
 
        memset(&entry, 0x0, sizeof(git_index_entry));
        entry.mode = 0100644;
-       entry.flags = 1 << GIT_IDXENTRY_STAGESHIFT;
+       GIT_INDEX_ENTRY_STAGE_SET(&entry, 1);
        git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
        entry.path = path;
        cl_git_pass(git_index_add(index, &entry));
 
-       entry.flags = 2 << GIT_IDXENTRY_STAGESHIFT;
+       GIT_INDEX_ENTRY_STAGE_SET(&entry, 2);
        git_oid_fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
        cl_git_pass(git_index_add(index, &entry));
 
-       entry.flags = 3 << GIT_IDXENTRY_STAGESHIFT;
+       GIT_INDEX_ENTRY_STAGE_SET(&entry, 3);
        git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
        cl_git_pass(git_index_add(index, &entry));
 
-       git_index_write(index);
+       cl_git_pass(git_index_write(index));
        git_index_free(index);
 }
 
@@ -906,7 +967,7 @@ void test_checkout_tree__fails_when_conflicts_exist_in_index(void)
        opts.checkout_strategy = GIT_CHECKOUT_SAFE;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        create_conflict("conflicts.txt");
 
@@ -923,28 +984,133 @@ void test_checkout_tree__filemode_preserved_in_index(void)
        git_index *index;
        const git_index_entry *entry;
 
+       opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
        cl_git_pass(git_repository_index(&index, g_repo));
 
+       /* test a freshly added executable */
        cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
        cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
 
-       opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
        cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
        cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0));
-       cl_assert_equal_i(0100755, entry->mode);
+       cl_assert(GIT_PERMS_IS_EXEC(entry->mode));
+
+       git_commit_free(commit);
+
+
+       /* Now start with a commit which has a text file */
+       cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+       cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+       cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+       cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
+       cl_assert(!GIT_PERMS_IS_EXEC(entry->mode));
+
+       git_commit_free(commit);
+
+
+       /* And then check out to a commit which converts the text file to an executable */
+       cl_git_pass(git_oid_fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e"));
+       cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+       cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+       cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
+       cl_assert(GIT_PERMS_IS_EXEC(entry->mode));
+
+       git_commit_free(commit);
+
+
+       /* Finally, check out the text file again and check that the exec bit is cleared */
+       cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+       cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+       cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+       cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
+       cl_assert(!GIT_PERMS_IS_EXEC(entry->mode));
 
        git_commit_free(commit);
+
+
        git_index_free(index);
 }
 
+mode_t read_filemode(const char *path)
+{
+       git_buf fullpath = GIT_BUF_INIT;
+       struct stat st;
+       mode_t result;
+
+       git_buf_joinpath(&fullpath, "testrepo", path);
+       cl_must_pass(p_stat(fullpath.ptr, &st));
+
+       result = GIT_PERMS_IS_EXEC(st.st_mode) ?
+               GIT_FILEMODE_BLOB_EXECUTABLE : GIT_FILEMODE_BLOB;
+
+       git_buf_dispose(&fullpath);
+
+       return result;
+}
+
+void test_checkout_tree__filemode_preserved_in_workdir(void)
+{
+#ifndef GIT_WIN32
+       git_oid executable_oid;
+       git_commit *commit;
+       git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+       opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+       /* test a freshly added executable */
+       cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
+       cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+       cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+       cl_assert(GIT_PERMS_IS_EXEC(read_filemode("executable.txt")));
+
+       git_commit_free(commit);
+
+
+       /* Now start with a commit which has a text file */
+       cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+       cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+       cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+       cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+       git_commit_free(commit);
+
+
+       /* And then check out to a commit which converts the text file to an executable */
+       cl_git_pass(git_oid_fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e"));
+       cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+       cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+       cl_assert(GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+       git_commit_free(commit);
+
+
+       /* Finally, check out the text file again and check that the exec bit is cleared */
+       cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+       cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+       cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+       cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+       git_commit_free(commit);
+#else
+       cl_skip();
+#endif
+}
+
 void test_checkout_tree__removes_conflicts(void)
 {
        git_oid commit_id;
        git_commit *commit;
        git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
        git_index *index;
-       
+
        cl_git_pass(git_oid_fromstr(&commit_id, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
        cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
 
@@ -961,7 +1127,7 @@ void test_checkout_tree__removes_conflicts(void)
        create_conflict("other.txt");
        cl_git_mkfile("testrepo/other.txt", "This is another conflict file.\n");
 
-       git_index_write(index);
+       cl_git_pass(git_index_write(index));
 
        cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
 
@@ -987,7 +1153,7 @@ void test_checkout_tree__removes_conflicts_only_by_pathscope(void)
        git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
        git_index *index;
        const char *path = "executable.txt";
-       
+
        cl_git_pass(git_oid_fromstr(&commit_id, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
        cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
 
@@ -1006,7 +1172,7 @@ void test_checkout_tree__removes_conflicts_only_by_pathscope(void)
        create_conflict("other.txt");
        cl_git_mkfile("testrepo/other.txt", "This is another conflict file.\n");
 
-       git_index_write(index);
+       cl_git_pass(git_index_write(index));
 
        cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
 
@@ -1047,7 +1213,7 @@ void test_checkout_tree__case_changing_rename(void)
        cl_git_pass(git_commit_lookup(&dir_commit, g_repo, &dir_commit_id));
 
        cl_git_pass(git_checkout_tree(g_repo, (git_object *)dir_commit, &opts));
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
 
        cl_assert(git_path_isfile("testrepo/README"));
        case_sensitive = !git_path_isfile("testrepo/readme");
@@ -1084,8 +1250,8 @@ void test_checkout_tree__case_changing_rename(void)
        cl_git_pass(git_commit_lookup(&master_commit, g_repo, &master_id));
 
        cl_git_pass(git_checkout_tree(g_repo, (git_object *)master_commit, &opts));
-       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master", NULL, NULL));
-       
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
+
        assert_on_branch(g_repo, "master");
 
        cl_assert(git_path_isfile("testrepo/README"));
@@ -1121,7 +1287,7 @@ void test_checkout_tree__can_collect_perfdata(void)
        opts.checkout_strategy = GIT_CHECKOUT_FORCE;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
 
@@ -1162,7 +1328,7 @@ void test_checkout_tree__caches_attributes_during_checkout(void)
        opts.paths.count = 2;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/ident"));
-       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
 
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
 
@@ -1180,8 +1346,8 @@ void test_checkout_tree__caches_attributes_during_checkout(void)
        cl_assert_equal_strn(ident1.ptr, "# $Id: ", 7);
        cl_assert_equal_strn(ident2.ptr, "# $Id: ", 7);
 
-       git_buf_free(&ident1);
-       git_buf_free(&ident2);
+       git_buf_dispose(&ident1);
+       git_buf_dispose(&ident2);
        git_object_free(obj);
 }
 
@@ -1197,9 +1363,9 @@ void test_checkout_tree__can_not_update_index(void)
                GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_UPDATE_INDEX;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
-       cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJECT_ANY));
 
-       cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts, NULL, NULL));
+       cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts));
 
        cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
 
@@ -1234,9 +1400,9 @@ void test_checkout_tree__can_update_but_not_write_index(void)
                GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_WRITE_INDEX;
 
        cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
-       cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJ_ANY));
+       cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJECT_ANY));
 
-       cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts, NULL, NULL));
+       cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts));
 
        cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
 
@@ -1264,3 +1430,255 @@ void test_checkout_tree__can_update_but_not_write_index(void)
        git_object_free(head);
        git_index_free(index);
 }
+
+/* Emulate checking out in a repo created by clone --no-checkout,
+ * which would not have written an index. */
+void test_checkout_tree__safe_proceeds_if_no_index(void)
+{
+       git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+       git_oid oid;
+       git_object *obj = NULL;
+
+       assert_on_branch(g_repo, "master");
+       cl_must_pass(p_unlink("testrepo/.git/index"));
+
+       /* do second checkout safe because we should be clean after first */
+       opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+       cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+       cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
+
+       cl_assert(git_path_isfile("testrepo/README"));
+       cl_assert(git_path_isfile("testrepo/branch_file.txt"));
+       cl_assert(git_path_isfile("testrepo/new.txt"));
+       cl_assert(git_path_isfile("testrepo/ab/4.txt"));
+       cl_assert(git_path_isfile("testrepo/ab/c/3.txt"));
+       cl_assert(git_path_isfile("testrepo/ab/de/2.txt"));
+       cl_assert(git_path_isfile("testrepo/ab/de/fgh/1.txt"));
+
+       cl_assert(!git_path_isdir("testrepo/a"));
+
+       assert_on_branch(g_repo, "subtrees");
+
+       git_object_free(obj);
+}
+
+static int checkout_conflict_count_cb(
+       git_checkout_notify_t why,
+       const char *path,
+       const git_diff_file *b,
+       const git_diff_file *t,
+       const git_diff_file *w,
+       void *payload)
+{
+       size_t *n = payload;
+
+       GIT_UNUSED(why);
+       GIT_UNUSED(path);
+       GIT_UNUSED(b);
+       GIT_UNUSED(t);
+       GIT_UNUSED(w);
+
+       (*n)++;
+
+       return 0;
+}
+
+/* A repo that has a HEAD (even a properly born HEAD that peels to
+ * a commit) but no index should be treated as if it's an empty baseline
+ */
+void test_checkout_tree__baseline_is_empty_when_no_index(void)
+{
+       git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+       git_reference *head;
+       git_object *obj;
+       size_t conflicts = 0;
+
+       assert_on_branch(g_repo, "master");
+
+       cl_git_pass(git_repository_head(&head, g_repo));
+       cl_git_pass(git_reference_peel(&obj, head, GIT_OBJECT_COMMIT));
+
+       cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
+
+       cl_must_pass(p_unlink("testrepo/.git/index"));
+
+       /* for a safe checkout, we should have checkout conflicts with
+        * the existing untracked files.
+        */
+       opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
+       opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
+       opts.notify_cb = checkout_conflict_count_cb;
+       opts.notify_payload = &conflicts;
+
+       cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, obj, &opts));
+       cl_assert_equal_i(4, conflicts);
+
+       /* but force should succeed and update the index */
+       opts.checkout_strategy |= GIT_CHECKOUT_FORCE;
+       cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+       assert_status_entrycount(g_repo, 0);
+
+       git_object_free(obj);
+       git_reference_free(head);
+}
+
+void test_checkout_tree__mode_change_is_force_updated(void)
+{
+       git_index *index;
+       git_reference *head;
+       git_object *obj;
+
+       if (!cl_is_chmod_supported())
+               clar__skip();
+
+       assert_on_branch(g_repo, "master");
+       cl_git_pass(git_repository_index(&index, g_repo));
+       cl_git_pass(git_repository_head(&head, g_repo));
+       cl_git_pass(git_reference_peel(&obj, head, GIT_OBJECT_COMMIT));
+
+       cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
+       assert_status_entrycount(g_repo, 0);
+
+       /* update the mode on-disk */
+       cl_must_pass(p_chmod("testrepo/README", 0755));
+
+       assert_status_entrycount(g_repo, 1);
+       cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
+       assert_status_entrycount(g_repo, 0);
+
+       /* update the mode on-disk and in the index */
+       cl_must_pass(p_chmod("testrepo/README", 0755));
+       cl_must_pass(git_index_add_bypath(index, "README"));
+
+       cl_git_pass(git_index_write(index));
+       assert_status_entrycount(g_repo, 1);
+
+       cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
+       cl_git_pass(git_index_write(index));
+
+       assert_status_entrycount(g_repo, 0);
+
+       git_object_free(obj);
+       git_reference_free(head);
+       git_index_free(index);
+}
+
+void test_checkout_tree__nullopts(void)
+{
+       cl_git_pass(git_checkout_tree(g_repo, NULL, NULL));
+}
+
+static void modify_index_ondisk(void)
+{
+       git_repository *other_repo;
+       git_index *other_index;
+       git_index_entry entry = {{0}};
+
+       cl_git_pass(git_repository_open(&other_repo, git_repository_workdir(g_repo)));
+       cl_git_pass(git_repository_index(&other_index, other_repo));
+
+       cl_git_pass(git_oid_fromstr(&entry.id, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
+       entry.mode = 0100644;
+       entry.path = "README";
+
+       cl_git_pass(git_index_add(other_index, &entry));
+       cl_git_pass(git_index_write(other_index));
+
+       git_index_free(other_index);
+       git_repository_free(other_repo);
+}
+
+static void modify_index_and_checkout_tree(git_checkout_options *opts)
+{
+       git_index *index;
+       git_reference *head;
+       git_object *obj;
+
+       /* External changes to the index are maintained by default */
+       cl_git_pass(git_repository_index(&index, g_repo));
+       cl_git_pass(git_repository_head(&head, g_repo));
+       cl_git_pass(git_reference_peel(&obj, head, GIT_OBJECT_COMMIT));
+
+       cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
+       assert_status_entrycount(g_repo, 0);
+
+       modify_index_ondisk();
+
+       /* The file in the index remains modified */
+       cl_git_pass(git_checkout_tree(g_repo, obj, opts));
+
+       git_object_free(obj);
+       git_reference_free(head);
+       git_index_free(index);
+}
+
+void test_checkout_tree__retains_external_index_changes(void)
+{
+       git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+       opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+       modify_index_and_checkout_tree(&opts);
+       assert_status_entrycount(g_repo, 1);
+}
+
+void test_checkout_tree__no_index_refresh(void)
+{
+       git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+       opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_NO_REFRESH;
+
+       modify_index_and_checkout_tree(&opts);
+       assert_status_entrycount(g_repo, 0);
+}
+
+void test_checkout_tree__dry_run(void)
+{
+       git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+       git_oid oid;
+       git_object *obj = NULL;
+       checkout_counts ct;
+
+       /* first let's get things into a known state - by checkout out the HEAD */
+
+       assert_on_branch(g_repo, "master");
+
+       opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+       cl_git_pass(git_checkout_head(g_repo, &opts));
+
+       cl_assert(!git_path_isdir("testrepo/a"));
+
+       check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
+
+       /* now checkout branch but with dry run enabled */
+
+       memset(&ct, 0, sizeof(ct));
+       opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DRY_RUN;
+       opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+       opts.notify_cb = checkout_count_callback;
+       opts.notify_payload = &ct;
+
+       cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+       cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+       cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+       cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
+
+       assert_on_branch(g_repo, "dir");
+
+       /* these normally would have been created and updated, but with
+        * DRY_RUN they will be unchanged.
+        */
+       cl_assert(!git_path_isdir("testrepo/a"));
+       check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
+
+       /* check that notify callback was invoked */
+       cl_assert_equal_i(ct.n_updates, 2);
+       
+       git_object_free(obj);
+}