1 #include "clar_libgit2.h"
2 #include "diff_generate.h"
3 #include "git2/checkout.h"
8 static git_repository
*g_repo
= NULL
;
11 From the test repo used for this test:
12 --------------------------------------
14 This is a test repo for libgit2 where tree entries have type changes
16 The key types that could be found in tree entries are:
18 1 - GIT_FILEMODE_NEW = 0000000
19 2 - GIT_FILEMODE_TREE = 0040000
20 3 - GIT_FILEMODE_BLOB = 0100644
21 4 - GIT_FILEMODE_BLOB_EXECUTABLE = 0100755
22 5 - GIT_FILEMODE_LINK = 0120000
23 6 - GIT_FILEMODE_COMMIT = 0160000
25 I will try to have every type of transition somewhere in the history
30 Initial commit - a(1) b(1) c(1) d(1) e(1)
31 Create content - a(1->2) b(1->3) c(1->4) d(1->5) e(1->6)
32 Changes #1 - a(2->3) b(3->4) c(4->5) d(5->6) e(6->2)
33 Changes #2 - a(3->5) b(4->6) c(5->2) d(6->3) e(2->4)
34 Changes #3 - a(5->3) b(6->4) c(2->5) d(3->6) e(4->2)
35 Changes #4 - a(3->2) b(4->3) c(5->4) d(6->5) e(2->6)
36 Changes #5 - a(2->1) b(3->1) c(4->1) d(5->1) e(6->1)
40 static const char *g_typechange_oids
[] = {
41 "79b9f23e85f55ea36a472a902e875bc1121a94cb",
42 "9bdb75b73836a99e3dbeea640a81de81031fdc29",
43 "0e7ed140b514b8cae23254cb8656fe1674403aff",
44 "9d0235c7a7edc0889a18f97a42ee6db9fe688447",
45 "9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a",
46 "1b63caae4a5ca96f78e8dfefc376c6a39a142475",
47 "6eae26c90e8ccc4d16208972119c40635489c6f0",
51 static bool g_typechange_empty
[] = {
52 true, false, false, false, false, false, true, true
55 static const int g_typechange_expected_conflicts
[] = {
59 static const int g_typechange_expected_untracked
[] = {
63 void test_checkout_typechange__initialize(void)
65 g_repo
= cl_git_sandbox_init("typechanges");
67 cl_fixture_sandbox("submod2_target");
68 p_rename("submod2_target/.gitted", "submod2_target/.git");
71 void test_checkout_typechange__cleanup(void)
73 cl_git_sandbox_cleanup();
74 cl_fixture_cleanup("submod2_target");
77 static void assert_file_exists(const char *path
)
79 cl_assert_(git_path_isfile(path
), path
);
82 static void assert_dir_exists(const char *path
)
84 cl_assert_(git_path_isdir(path
), path
);
87 static void assert_workdir_matches_tree(
88 git_repository
*repo
, const git_oid
*id
, const char *root
, bool recurse
)
93 git_buf path
= GIT_BUF_INIT
;
96 root
= git_repository_workdir(repo
);
99 cl_git_pass(git_object_lookup(&obj
, repo
, id
, GIT_OBJECT_ANY
));
100 cl_git_pass(git_object_peel((git_object
**)&tree
, obj
, GIT_OBJECT_TREE
));
101 git_object_free(obj
);
103 max_i
= git_tree_entrycount(tree
);
105 for (i
= 0; i
< max_i
; ++i
) {
106 const git_tree_entry
*te
= git_tree_entry_byindex(tree
, i
);
109 cl_git_pass(git_buf_joinpath(&path
, root
, git_tree_entry_name(te
)));
111 switch (git_tree_entry_type(te
)) {
112 case GIT_OBJECT_COMMIT
:
113 assert_dir_exists(path
.ptr
);
115 case GIT_OBJECT_TREE
:
116 assert_dir_exists(path
.ptr
);
118 assert_workdir_matches_tree(
119 repo
, git_tree_entry_id(te
), path
.ptr
, true);
121 case GIT_OBJECT_BLOB
:
122 switch (git_tree_entry_filemode(te
)) {
123 case GIT_FILEMODE_BLOB
:
124 case GIT_FILEMODE_BLOB_EXECUTABLE
:
125 assert_file_exists(path
.ptr
);
126 /* because of cross-platform, don't confirm exec bit yet */
128 case GIT_FILEMODE_LINK
:
129 cl_assert_(git_path_exists(path
.ptr
), path
.ptr
);
130 /* because of cross-platform, don't confirm link yet */
133 cl_assert(false); /* really?! */
137 cl_assert(false); /* really?!! */
142 git_buf_dispose(&path
);
145 void test_checkout_typechange__checkout_typechanges_safe(void)
149 git_checkout_options opts
= GIT_CHECKOUT_OPTIONS_INIT
;
151 for (i
= 0; g_typechange_oids
[i
] != NULL
; ++i
) {
152 cl_git_pass(git_revparse_single(&obj
, g_repo
, g_typechange_oids
[i
]));
154 opts
.checkout_strategy
= !i
? GIT_CHECKOUT_FORCE
: GIT_CHECKOUT_SAFE
;
156 cl_git_pass(git_checkout_tree(g_repo
, obj
, &opts
));
159 git_repository_set_head_detached(g_repo
, git_object_id(obj
)));
161 assert_workdir_matches_tree(g_repo
, git_object_id(obj
), NULL
, true);
163 git_object_free(obj
);
165 if (!g_typechange_empty
[i
]) {
166 cl_assert(git_path_isdir("typechanges"));
167 cl_assert(git_path_exists("typechanges/a"));
168 cl_assert(git_path_exists("typechanges/b"));
169 cl_assert(git_path_exists("typechanges/c"));
170 cl_assert(git_path_exists("typechanges/d"));
171 cl_assert(git_path_exists("typechanges/e"));
173 cl_assert(git_path_isdir("typechanges"));
174 cl_assert(!git_path_exists("typechanges/a"));
175 cl_assert(!git_path_exists("typechanges/b"));
176 cl_assert(!git_path_exists("typechanges/c"));
177 cl_assert(!git_path_exists("typechanges/d"));
178 cl_assert(!git_path_exists("typechanges/e"));
191 static int notify_counter(
192 git_checkout_notify_t why
,
194 const git_diff_file
*baseline
,
195 const git_diff_file
*target
,
196 const git_diff_file
*workdir
,
199 notify_counts
*cts
= payload
;
202 GIT_UNUSED(baseline
);
207 case GIT_CHECKOUT_NOTIFY_CONFLICT
: cts
->conflicts
++; break;
208 case GIT_CHECKOUT_NOTIFY_DIRTY
: cts
->dirty
++; break;
209 case GIT_CHECKOUT_NOTIFY_UPDATED
: cts
->updates
++; break;
210 case GIT_CHECKOUT_NOTIFY_UNTRACKED
: cts
->untracked
++; break;
211 case GIT_CHECKOUT_NOTIFY_IGNORED
: cts
->ignored
++; break;
218 static void force_create_file(const char *file
)
220 int error
= git_futils_rmdir_r(file
, NULL
,
221 GIT_RMDIR_REMOVE_FILES
| GIT_RMDIR_REMOVE_BLOCKERS
);
222 cl_assert(!error
|| error
== GIT_ENOTFOUND
);
223 cl_git_pass(git_futils_mkpath2file(file
, 0777));
224 cl_git_rewritefile(file
, "yowza!!");
227 static int make_submodule_dirty(git_submodule
*sm
, const char *name
, void *payload
)
229 git_buf submodulepath
= GIT_BUF_INIT
;
230 git_buf dirtypath
= GIT_BUF_INIT
;
231 git_repository
*submodule_repo
;
236 /* remove submodule directory in preparation for init and repo_init */
237 cl_git_pass(git_buf_joinpath(
239 git_repository_workdir(g_repo
),
240 git_submodule_path(sm
)
242 git_futils_rmdir_r(git_buf_cstr(&submodulepath
), NULL
, GIT_RMDIR_REMOVE_FILES
);
244 /* initialize submodule's repository */
245 cl_git_pass(git_submodule_repo_init(&submodule_repo
, sm
, 0));
247 /* create a file in the submodule workdir to make it dirty */
249 git_buf_joinpath(&dirtypath
, git_repository_workdir(submodule_repo
), "dirty"));
250 force_create_file(git_buf_cstr(&dirtypath
));
252 git_buf_dispose(&dirtypath
);
253 git_buf_dispose(&submodulepath
);
254 git_repository_free(submodule_repo
);
259 void test_checkout_typechange__checkout_with_conflicts(void)
263 git_checkout_options opts
= GIT_CHECKOUT_OPTIONS_INIT
;
264 notify_counts cts
= {0};
267 GIT_CHECKOUT_NOTIFY_CONFLICT
| GIT_CHECKOUT_NOTIFY_UNTRACKED
;
268 opts
.notify_cb
= notify_counter
;
269 opts
.notify_payload
= &cts
;
271 for (i
= 0; g_typechange_oids
[i
] != NULL
; ++i
) {
272 cl_git_pass(git_revparse_single(&obj
, g_repo
, g_typechange_oids
[i
]));
274 force_create_file("typechanges/a/blocker");
275 force_create_file("typechanges/b");
276 force_create_file("typechanges/c/sub/sub/file");
277 git_futils_rmdir_r("typechanges/d", NULL
, GIT_RMDIR_REMOVE_FILES
);
278 p_mkdir("typechanges/d", 0777); /* intentionally empty dir */
279 force_create_file("typechanges/untracked");
280 cl_git_pass(git_submodule_foreach(g_repo
, make_submodule_dirty
, NULL
));
282 opts
.checkout_strategy
= GIT_CHECKOUT_SAFE
;
283 memset(&cts
, 0, sizeof(cts
));
285 cl_git_fail(git_checkout_tree(g_repo
, obj
, &opts
));
286 cl_assert_equal_i(cts
.conflicts
, g_typechange_expected_conflicts
[i
]);
287 cl_assert_equal_i(cts
.untracked
, g_typechange_expected_untracked
[i
]);
288 cl_assert_equal_i(cts
.dirty
, 0);
289 cl_assert_equal_i(cts
.updates
, 0);
290 cl_assert_equal_i(cts
.ignored
, 0);
292 opts
.checkout_strategy
=
293 GIT_CHECKOUT_FORCE
| GIT_CHECKOUT_REMOVE_UNTRACKED
;
294 memset(&cts
, 0, sizeof(cts
));
296 cl_assert(git_path_exists("typechanges/untracked"));
298 cl_git_pass(git_checkout_tree(g_repo
, obj
, &opts
));
299 cl_assert_equal_i(0, cts
.conflicts
);
301 cl_assert(!git_path_exists("typechanges/untracked"));
304 git_repository_set_head_detached(g_repo
, git_object_id(obj
)));
306 assert_workdir_matches_tree(g_repo
, git_object_id(obj
), NULL
, true);
308 git_object_free(obj
);
312 void test_checkout_typechange__status_char(void)
318 const git_diff_delta
*delta
;
319 git_diff_options diffopts
= GIT_DIFF_OPTIONS_INIT
;
320 char expected
[8] = {'M', 'M', 'R', 'T', 'D', 'R', 'A', 'R'};
322 git_oid_fromstr(&oid
, "9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a");
323 cl_git_pass(git_commit_lookup(&commit
, g_repo
, &oid
));
324 diffopts
.flags
|= GIT_DIFF_INCLUDE_TYPECHANGE
;
325 cl_git_pass(git_diff__commit(&diff
, g_repo
, commit
, &diffopts
));
326 cl_git_pass(git_diff_find_similar(diff
, NULL
));
328 for (i
= 0; i
< git_diff_num_deltas(diff
); i
++) {
329 delta
= git_diff_get_delta(diff
, i
);
330 cl_assert_equal_i(expected
[i
], git_diff_status_char(delta
->status
));
334 git_commit_free(commit
);