]> git.proxmox.com Git - libgit2.git/blame - tests/checkout/typechange.c
Update CMakeLists.txt
[libgit2.git] / tests / checkout / typechange.c
CommitLineData
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
7static git_repository *g_repo = NULL;
8
70681ff7
JH
9/*
10From the test repo used for this test:
11--------------------------------------
12
13This is a test repo for libgit2 where tree entries have type changes
14
15The key types that could be found in tree entries are:
16
171 - GIT_FILEMODE_NEW = 0000000
182 - GIT_FILEMODE_TREE = 0040000
193 - GIT_FILEMODE_BLOB = 0100644
204 - GIT_FILEMODE_BLOB_EXECUTABLE = 0100755
215 - GIT_FILEMODE_LINK = 0120000
226 - GIT_FILEMODE_COMMIT = 0160000
23
24I will try to have every type of transition somewhere in the history
25of this repo.
26
27Commits
28-------
29Initial commit - a(1) b(1) c(1) d(1) e(1)
30Create content - a(1->2) b(1->3) c(1->4) d(1->5) e(1->6)
31Changes #1 - a(2->3) b(3->4) c(4->5) d(5->6) e(6->2)
32Changes #2 - a(3->5) b(4->6) c(5->2) d(6->3) e(2->4)
33Changes #3 - a(5->3) b(6->4) c(2->5) d(3->6) e(4->2)
34Changes #4 - a(3->2) b(4->3) c(5->4) d(6->5) e(2->6)
35Changes #5 - a(2->1) b(3->1) c(4->1) d(5->1) e(6->1)
36
37*/
38
0d64bef9
RB
39static const char *g_typechange_oids[] = {
40 "79b9f23e85f55ea36a472a902e875bc1121a94cb",
41 "9bdb75b73836a99e3dbeea640a81de81031fdc29",
42 "0e7ed140b514b8cae23254cb8656fe1674403aff",
43 "9d0235c7a7edc0889a18f97a42ee6db9fe688447",
44 "9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a",
45 "1b63caae4a5ca96f78e8dfefc376c6a39a142475",
46 "6eae26c90e8ccc4d16208972119c40635489c6f0",
47 NULL
48};
49
50static bool g_typechange_empty[] = {
51 true, false, false, false, false, false, true, true
52};
53
70681ff7
JH
54static const int g_typechange_expected_conflicts[] = {
55 1, 2, 3, 3, 2, 3, 2
56};
57
58static const int g_typechange_expected_untracked[] = {
59 6, 4, 3, 2, 3, 2, 5
60};
61
0d64bef9
RB
62void test_checkout_typechange__initialize(void)
63{
64 g_repo = cl_git_sandbox_init("typechanges");
65
66 cl_fixture_sandbox("submod2_target");
67 p_rename("submod2_target/.gitted", "submod2_target/.git");
68}
69
70void test_checkout_typechange__cleanup(void)
71{
72 cl_git_sandbox_cleanup();
73 cl_fixture_cleanup("submod2_target");
74}
75
c50c58de
RB
76static void assert_file_exists(const char *path)
77{
78 cl_assert_(git_path_isfile(path), path);
79}
80
81static void assert_dir_exists(const char *path)
82{
83 cl_assert_(git_path_isdir(path), path);
84}
85
86static void assert_workdir_matches_tree(
87 git_repository *repo, const git_oid *id, const char *root, bool recurse)
88{
89 git_object *obj;
90 git_tree *tree;
91 size_t i, max_i;
92 git_buf path = GIT_BUF_INIT;
93
94 if (!root)
95 root = git_repository_workdir(repo);
96 cl_assert(root);
97
98 cl_git_pass(git_object_lookup(&obj, repo, id, GIT_OBJ_ANY));
99 cl_git_pass(git_object_peel((git_object **)&tree, obj, GIT_OBJ_TREE));
100 git_object_free(obj);
101
102 max_i = git_tree_entrycount(tree);
103
104 for (i = 0; i < max_i; ++i) {
105 const git_tree_entry *te = git_tree_entry_byindex(tree, i);
106 cl_assert(te);
107
108 cl_git_pass(git_buf_joinpath(&path, root, git_tree_entry_name(te)));
109
110 switch (git_tree_entry_type(te)) {
111 case GIT_OBJ_COMMIT:
112 assert_dir_exists(path.ptr);
113 break;
114 case GIT_OBJ_TREE:
115 assert_dir_exists(path.ptr);
116 if (recurse)
117 assert_workdir_matches_tree(
118 repo, git_tree_entry_id(te), path.ptr, true);
119 break;
120 case GIT_OBJ_BLOB:
121 switch (git_tree_entry_filemode(te)) {
122 case GIT_FILEMODE_BLOB:
123 case GIT_FILEMODE_BLOB_EXECUTABLE:
124 assert_file_exists(path.ptr);
125 /* because of cross-platform, don't confirm exec bit yet */
126 break;
127 case GIT_FILEMODE_LINK:
128 cl_assert_(git_path_exists(path.ptr), path.ptr);
129 /* because of cross-platform, don't confirm link yet */
130 break;
131 default:
132 cl_assert(false); /* really?! */
133 }
134 break;
135 default:
136 cl_assert(false); /* really?!! */
137 }
138 }
139
140 git_tree_free(tree);
141 git_buf_free(&path);
142}
143
144void test_checkout_typechange__checkout_typechanges_safe(void)
0d64bef9
RB
145{
146 int i;
147 git_object *obj;
6affd71f 148 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
0d64bef9 149
0d64bef9
RB
150 for (i = 0; g_typechange_oids[i] != NULL; ++i) {
151 cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
7e5c8a5b 152
70681ff7 153 opts.checkout_strategy = !i ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE;
0d64bef9 154
30a46ab1 155 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
0d64bef9 156
ad9a921b 157 cl_git_pass(
4e498646 158 git_repository_set_head_detached(g_repo, git_object_id(obj)));
ad9a921b 159
c50c58de
RB
160 assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true);
161
0d64bef9
RB
162 git_object_free(obj);
163
164 if (!g_typechange_empty[i]) {
165 cl_assert(git_path_isdir("typechanges"));
166 cl_assert(git_path_exists("typechanges/a"));
167 cl_assert(git_path_exists("typechanges/b"));
168 cl_assert(git_path_exists("typechanges/c"));
169 cl_assert(git_path_exists("typechanges/d"));
170 cl_assert(git_path_exists("typechanges/e"));
171 } else {
172 cl_assert(git_path_isdir("typechanges"));
173 cl_assert(!git_path_exists("typechanges/a"));
174 cl_assert(!git_path_exists("typechanges/b"));
175 cl_assert(!git_path_exists("typechanges/c"));
176 cl_assert(!git_path_exists("typechanges/d"));
177 cl_assert(!git_path_exists("typechanges/e"));
178 }
179 }
180}
c50c58de
RB
181
182typedef struct {
183 int conflicts;
184 int dirty;
185 int updates;
186 int untracked;
187 int ignored;
188} notify_counts;
189
190static int notify_counter(
191 git_checkout_notify_t why,
192 const char *path,
193 const git_diff_file *baseline,
194 const git_diff_file *target,
195 const git_diff_file *workdir,
196 void *payload)
197{
198 notify_counts *cts = payload;
199
200 GIT_UNUSED(path);
201 GIT_UNUSED(baseline);
202 GIT_UNUSED(target);
203 GIT_UNUSED(workdir);
204
205 switch (why) {
206 case GIT_CHECKOUT_NOTIFY_CONFLICT: cts->conflicts++; break;
207 case GIT_CHECKOUT_NOTIFY_DIRTY: cts->dirty++; break;
208 case GIT_CHECKOUT_NOTIFY_UPDATED: cts->updates++; break;
209 case GIT_CHECKOUT_NOTIFY_UNTRACKED: cts->untracked++; break;
210 case GIT_CHECKOUT_NOTIFY_IGNORED: cts->ignored++; break;
211 default: break;
212 }
213
214 return 0;
215}
216
217static void force_create_file(const char *file)
218{
219 int error = git_futils_rmdir_r(file, NULL,
220 GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS);
221 cl_assert(!error || error == GIT_ENOTFOUND);
222 cl_git_pass(git_futils_mkpath2file(file, 0777));
8023b83a 223 cl_git_rewritefile(file, "yowza!!");
c50c58de
RB
224}
225
70681ff7
JH
226static int make_submodule_dirty(git_submodule *sm, const char *name, void *payload)
227{
228 git_buf submodulepath = GIT_BUF_INIT;
229 git_buf dirtypath = GIT_BUF_INIT;
230 git_repository *submodule_repo;
231
232 /* remove submodule directory in preparation for init and repo_init */
233 cl_git_pass(git_buf_joinpath(
234 &submodulepath,
235 git_repository_workdir(g_repo),
236 git_submodule_path(sm)
237 ));
238 git_futils_rmdir_r(git_buf_cstr(&submodulepath), NULL, GIT_RMDIR_REMOVE_FILES);
239
240 /* initialize submodule and its repository */
241 cl_git_pass(git_submodule_init(sm, 1));
242 cl_git_pass(git_submodule_repo_init(&submodule_repo, sm, 0));
243
244 /* create a file in the submodule workdir to make it dirty */
245 cl_git_pass(
246 git_buf_joinpath(&dirtypath, git_repository_workdir(submodule_repo), "dirty"));
247 force_create_file(git_buf_cstr(&dirtypath));
248
249 git_buf_free(&dirtypath);
250 git_buf_free(&submodulepath);
251
252 return 0;
253}
254
c50c58de
RB
255void test_checkout_typechange__checkout_with_conflicts(void)
256{
257 int i;
258 git_object *obj;
6affd71f 259 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
c50c58de
RB
260 notify_counts cts = {0};
261
262 opts.notify_flags =
263 GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_UNTRACKED;
264 opts.notify_cb = notify_counter;
265 opts.notify_payload = &cts;
266
267 for (i = 0; g_typechange_oids[i] != NULL; ++i) {
268 cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
269
270 force_create_file("typechanges/a/blocker");
271 force_create_file("typechanges/b");
272 force_create_file("typechanges/c/sub/sub/file");
273 git_futils_rmdir_r("typechanges/d", NULL, GIT_RMDIR_REMOVE_FILES);
274 p_mkdir("typechanges/d", 0777); /* intentionally empty dir */
275 force_create_file("typechanges/untracked");
70681ff7 276 cl_git_pass(git_submodule_foreach(g_repo, make_submodule_dirty, NULL));
c50c58de 277
496b76d4 278 opts.checkout_strategy = GIT_CHECKOUT_SAFE;
c50c58de
RB
279 memset(&cts, 0, sizeof(cts));
280
281 cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
70681ff7
JH
282 cl_assert_equal_i(cts.conflicts, g_typechange_expected_conflicts[i]);
283 cl_assert_equal_i(cts.untracked, g_typechange_expected_untracked[i]);
284 cl_assert_equal_i(cts.dirty, 0);
285 cl_assert_equal_i(cts.updates, 0);
286 cl_assert_equal_i(cts.ignored, 0);
c50c58de
RB
287
288 opts.checkout_strategy =
289 GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
290 memset(&cts, 0, sizeof(cts));
291
292 cl_assert(git_path_exists("typechanges/untracked"));
293
294 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
295 cl_assert_equal_i(0, cts.conflicts);
296
297 cl_assert(!git_path_exists("typechanges/untracked"));
298
299 cl_git_pass(
4e498646 300 git_repository_set_head_detached(g_repo, git_object_id(obj)));
c50c58de
RB
301
302 assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true);
303
304 git_object_free(obj);
305 }
306}