]>
Commit | Line | Data |
---|---|---|
0d64bef9 RB |
1 | #include "clar_libgit2.h" |
2 | #include "git2/checkout.h" | |
3 | #include "path.h" | |
4 | #include "posix.h" | |
c50c58de | 5 | #include "fileops.h" |
0d64bef9 RB |
6 | |
7 | static git_repository *g_repo = NULL; | |
8 | ||
9 | static const char *g_typechange_oids[] = { | |
10 | "79b9f23e85f55ea36a472a902e875bc1121a94cb", | |
11 | "9bdb75b73836a99e3dbeea640a81de81031fdc29", | |
12 | "0e7ed140b514b8cae23254cb8656fe1674403aff", | |
13 | "9d0235c7a7edc0889a18f97a42ee6db9fe688447", | |
14 | "9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a", | |
15 | "1b63caae4a5ca96f78e8dfefc376c6a39a142475", | |
16 | "6eae26c90e8ccc4d16208972119c40635489c6f0", | |
17 | NULL | |
18 | }; | |
19 | ||
20 | static bool g_typechange_empty[] = { | |
21 | true, false, false, false, false, false, true, true | |
22 | }; | |
23 | ||
24 | void test_checkout_typechange__initialize(void) | |
25 | { | |
26 | g_repo = cl_git_sandbox_init("typechanges"); | |
27 | ||
28 | cl_fixture_sandbox("submod2_target"); | |
29 | p_rename("submod2_target/.gitted", "submod2_target/.git"); | |
30 | } | |
31 | ||
32 | void test_checkout_typechange__cleanup(void) | |
33 | { | |
34 | cl_git_sandbox_cleanup(); | |
35 | cl_fixture_cleanup("submod2_target"); | |
36 | } | |
37 | ||
c50c58de RB |
38 | static void assert_file_exists(const char *path) |
39 | { | |
40 | cl_assert_(git_path_isfile(path), path); | |
41 | } | |
42 | ||
43 | static void assert_dir_exists(const char *path) | |
44 | { | |
45 | cl_assert_(git_path_isdir(path), path); | |
46 | } | |
47 | ||
48 | static void assert_workdir_matches_tree( | |
49 | git_repository *repo, const git_oid *id, const char *root, bool recurse) | |
50 | { | |
51 | git_object *obj; | |
52 | git_tree *tree; | |
53 | size_t i, max_i; | |
54 | git_buf path = GIT_BUF_INIT; | |
55 | ||
56 | if (!root) | |
57 | root = git_repository_workdir(repo); | |
58 | cl_assert(root); | |
59 | ||
60 | cl_git_pass(git_object_lookup(&obj, repo, id, GIT_OBJ_ANY)); | |
61 | cl_git_pass(git_object_peel((git_object **)&tree, obj, GIT_OBJ_TREE)); | |
62 | git_object_free(obj); | |
63 | ||
64 | max_i = git_tree_entrycount(tree); | |
65 | ||
66 | for (i = 0; i < max_i; ++i) { | |
67 | const git_tree_entry *te = git_tree_entry_byindex(tree, i); | |
68 | cl_assert(te); | |
69 | ||
70 | cl_git_pass(git_buf_joinpath(&path, root, git_tree_entry_name(te))); | |
71 | ||
72 | switch (git_tree_entry_type(te)) { | |
73 | case GIT_OBJ_COMMIT: | |
74 | assert_dir_exists(path.ptr); | |
75 | break; | |
76 | case GIT_OBJ_TREE: | |
77 | assert_dir_exists(path.ptr); | |
78 | if (recurse) | |
79 | assert_workdir_matches_tree( | |
80 | repo, git_tree_entry_id(te), path.ptr, true); | |
81 | break; | |
82 | case GIT_OBJ_BLOB: | |
83 | switch (git_tree_entry_filemode(te)) { | |
84 | case GIT_FILEMODE_BLOB: | |
85 | case GIT_FILEMODE_BLOB_EXECUTABLE: | |
86 | assert_file_exists(path.ptr); | |
87 | /* because of cross-platform, don't confirm exec bit yet */ | |
88 | break; | |
89 | case GIT_FILEMODE_LINK: | |
90 | cl_assert_(git_path_exists(path.ptr), path.ptr); | |
91 | /* because of cross-platform, don't confirm link yet */ | |
92 | break; | |
93 | default: | |
94 | cl_assert(false); /* really?! */ | |
95 | } | |
96 | break; | |
97 | default: | |
98 | cl_assert(false); /* really?!! */ | |
99 | } | |
100 | } | |
101 | ||
102 | git_tree_free(tree); | |
103 | git_buf_free(&path); | |
104 | } | |
105 | ||
106 | void test_checkout_typechange__checkout_typechanges_safe(void) | |
0d64bef9 RB |
107 | { |
108 | int i; | |
109 | git_object *obj; | |
6affd71f | 110 | git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; |
0d64bef9 | 111 | |
0d64bef9 RB |
112 | for (i = 0; g_typechange_oids[i] != NULL; ++i) { |
113 | cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i])); | |
7e5c8a5b | 114 | |
c50c58de RB |
115 | opts.checkout_strategy = GIT_CHECKOUT_FORCE; |
116 | ||
117 | /* There are bugs in some submodule->tree changes that prevent | |
118 | * SAFE from passing here, even though the following should work: | |
119 | */ | |
120 | /* !i ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE; */ | |
0d64bef9 | 121 | |
30a46ab1 | 122 | cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); |
0d64bef9 | 123 | |
ad9a921b | 124 | cl_git_pass( |
659cf202 | 125 | git_repository_set_head_detached(g_repo, git_object_id(obj), NULL)); |
ad9a921b | 126 | |
c50c58de RB |
127 | assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true); |
128 | ||
0d64bef9 RB |
129 | git_object_free(obj); |
130 | ||
131 | if (!g_typechange_empty[i]) { | |
132 | cl_assert(git_path_isdir("typechanges")); | |
133 | cl_assert(git_path_exists("typechanges/a")); | |
134 | cl_assert(git_path_exists("typechanges/b")); | |
135 | cl_assert(git_path_exists("typechanges/c")); | |
136 | cl_assert(git_path_exists("typechanges/d")); | |
137 | cl_assert(git_path_exists("typechanges/e")); | |
138 | } else { | |
139 | cl_assert(git_path_isdir("typechanges")); | |
140 | cl_assert(!git_path_exists("typechanges/a")); | |
141 | cl_assert(!git_path_exists("typechanges/b")); | |
142 | cl_assert(!git_path_exists("typechanges/c")); | |
143 | cl_assert(!git_path_exists("typechanges/d")); | |
144 | cl_assert(!git_path_exists("typechanges/e")); | |
145 | } | |
146 | } | |
147 | } | |
c50c58de RB |
148 | |
149 | typedef struct { | |
150 | int conflicts; | |
151 | int dirty; | |
152 | int updates; | |
153 | int untracked; | |
154 | int ignored; | |
155 | } notify_counts; | |
156 | ||
157 | static int notify_counter( | |
158 | git_checkout_notify_t why, | |
159 | const char *path, | |
160 | const git_diff_file *baseline, | |
161 | const git_diff_file *target, | |
162 | const git_diff_file *workdir, | |
163 | void *payload) | |
164 | { | |
165 | notify_counts *cts = payload; | |
166 | ||
167 | GIT_UNUSED(path); | |
168 | GIT_UNUSED(baseline); | |
169 | GIT_UNUSED(target); | |
170 | GIT_UNUSED(workdir); | |
171 | ||
172 | switch (why) { | |
173 | case GIT_CHECKOUT_NOTIFY_CONFLICT: cts->conflicts++; break; | |
174 | case GIT_CHECKOUT_NOTIFY_DIRTY: cts->dirty++; break; | |
175 | case GIT_CHECKOUT_NOTIFY_UPDATED: cts->updates++; break; | |
176 | case GIT_CHECKOUT_NOTIFY_UNTRACKED: cts->untracked++; break; | |
177 | case GIT_CHECKOUT_NOTIFY_IGNORED: cts->ignored++; break; | |
178 | default: break; | |
179 | } | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
184 | static void force_create_file(const char *file) | |
185 | { | |
186 | int error = git_futils_rmdir_r(file, NULL, | |
187 | GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS); | |
188 | cl_assert(!error || error == GIT_ENOTFOUND); | |
189 | cl_git_pass(git_futils_mkpath2file(file, 0777)); | |
8023b83a | 190 | cl_git_rewritefile(file, "yowza!!"); |
c50c58de RB |
191 | } |
192 | ||
193 | void test_checkout_typechange__checkout_with_conflicts(void) | |
194 | { | |
195 | int i; | |
196 | git_object *obj; | |
6affd71f | 197 | git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; |
c50c58de RB |
198 | notify_counts cts = {0}; |
199 | ||
200 | opts.notify_flags = | |
201 | GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_UNTRACKED; | |
202 | opts.notify_cb = notify_counter; | |
203 | opts.notify_payload = &cts; | |
204 | ||
205 | for (i = 0; g_typechange_oids[i] != NULL; ++i) { | |
206 | cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i])); | |
207 | ||
208 | force_create_file("typechanges/a/blocker"); | |
209 | force_create_file("typechanges/b"); | |
210 | force_create_file("typechanges/c/sub/sub/file"); | |
211 | git_futils_rmdir_r("typechanges/d", NULL, GIT_RMDIR_REMOVE_FILES); | |
212 | p_mkdir("typechanges/d", 0777); /* intentionally empty dir */ | |
213 | force_create_file("typechanges/untracked"); | |
214 | ||
496b76d4 | 215 | opts.checkout_strategy = GIT_CHECKOUT_SAFE; |
c50c58de RB |
216 | memset(&cts, 0, sizeof(cts)); |
217 | ||
218 | cl_git_fail(git_checkout_tree(g_repo, obj, &opts)); | |
219 | cl_assert(cts.conflicts > 0); | |
220 | cl_assert(cts.untracked > 0); | |
221 | ||
222 | opts.checkout_strategy = | |
223 | GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED; | |
224 | memset(&cts, 0, sizeof(cts)); | |
225 | ||
226 | cl_assert(git_path_exists("typechanges/untracked")); | |
227 | ||
228 | cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); | |
229 | cl_assert_equal_i(0, cts.conflicts); | |
230 | ||
231 | cl_assert(!git_path_exists("typechanges/untracked")); | |
232 | ||
233 | cl_git_pass( | |
659cf202 | 234 | git_repository_set_head_detached(g_repo, git_object_id(obj), NULL)); |
c50c58de RB |
235 | |
236 | assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true); | |
237 | ||
238 | git_object_free(obj); | |
239 | } | |
240 | } |