diff --git a/include/git2/checkout.h b/include/git2/checkout.h index dfc7e0580..d3e971b43 100644 --- a/include/git2/checkout.h +++ b/include/git2/checkout.h @@ -131,6 +131,9 @@ typedef enum { /** Don't refresh index/config/etc before doing checkout */ GIT_CHECKOUT_NO_REFRESH = (1u << 9), + /** Treat pathspec as simple list of exact match file paths */ + GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13), + /** * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED */ @@ -222,7 +225,8 @@ typedef struct git_checkout_opts { void *progress_payload; /** When not zeroed out, array of fnmatch patterns specifying which - * paths should be taken into account, otherwise all files. + * paths should be taken into account, otherwise all files. Use + * GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH to treat as simple list. */ git_strarray paths; diff --git a/src/checkout.c b/src/checkout.c index da22df680..4d6f99463 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -222,7 +222,9 @@ static int checkout_action_wd_only( git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE; if (!git_pathspec_match_path( - pathspec, wd->path, false, workdir->ignore_case)) + pathspec, wd->path, + (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0, + workdir->ignore_case)) return 0; /* check if item is tracked in the index but not in the checkout diff */ @@ -1209,6 +1211,8 @@ int git_checkout_iterator( GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_INCLUDE_TYPECHANGE_TREES | GIT_DIFF_SKIP_BINARY_CHECK; + if (data.opts.checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) + diff_opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH; if (data.opts.paths.count > 0) diff_opts.pathspec = data.opts.paths; diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index 30887588f..013c79b57 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -273,3 +273,86 @@ void test_checkout_tree__can_update_only(void) git_object_free(obj); } + +void test_checkout_tree__can_checkout_with_pattern(void) +{ + char *entries[] = { "[l-z]*.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, + "8496071c1b46c854b31185ea97743be6a8774479")); + + 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))); + + git_object_free(g_object); + g_object = NULL; + + 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")); + + /* now to a narrow patterned checkout */ + + g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + g_opts.paths.strings = entries; + g_opts.paths.count = 1; + + 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_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" }; + + /* 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, + "8496071c1b46c854b31185ea97743be6a8774479")); + + 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))); + + git_object_free(g_object); + g_object = NULL; + + cl_assert(!git_path_isfile("testrepo/branch_file.txt")); + + /* now to a narrow patterned checkout, but disable pattern */ + + g_opts.checkout_strategy = + GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH; + g_opts.paths.strings = entries; + g_opts.paths.count = 1; + + 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_assert(!git_path_isfile("testrepo/branch_file.txt")); + + /* let's try that again, but allow the pattern match */ + + g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); + + cl_assert(git_path_isfile("testrepo/branch_file.txt")); +}