Add GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH This adds an option to checkout a la the diff option to turn off fnmatch evaluation for pathspec entries. This can be useful to make sure your "pattern" in really interpretted as an exact file match only.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index dfc7e05..d3e971b 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 da22df6..4d6f994 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 3088758..013c79b 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"));
+}