1 #include "clar_libgit2.h"
4 #include "git2/reflog.h"
7 static const char *new_ref
= "refs/heads/test-reflog";
8 static const char *current_master_tip
= "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
9 #define commit_msg "commit: bla bla"
11 static git_repository
*g_repo
;
15 static void assert_signature(const git_signature
*expected
, const git_signature
*actual
)
18 cl_assert_equal_s(expected
->name
, actual
->name
);
19 cl_assert_equal_s(expected
->email
, actual
->email
);
20 cl_assert(expected
->when
.offset
== actual
->when
.offset
);
21 cl_assert(expected
->when
.time
== actual
->when
.time
);
25 /* Fixture setup and teardown */
26 void test_refs_reflog_reflog__initialize(void)
28 g_repo
= cl_git_sandbox_init("testrepo.git");
31 void test_refs_reflog_reflog__cleanup(void)
33 cl_git_sandbox_cleanup();
36 static void assert_appends(const git_signature
*committer
, const git_oid
*oid
)
38 git_repository
*repo2
;
39 git_reference
*lookedup_ref
;
41 const git_reflog_entry
*entry
;
43 /* Reopen a new instance of the repository */
44 cl_git_pass(git_repository_open(&repo2
, "testrepo.git"));
46 /* Lookup the previously created branch */
47 cl_git_pass(git_reference_lookup(&lookedup_ref
, repo2
, new_ref
));
49 /* Read and parse the reflog for this branch */
50 cl_git_pass(git_reflog_read(&reflog
, repo2
, new_ref
));
51 cl_assert_equal_i(3, (int)git_reflog_entrycount(reflog
));
53 /* The first one was the creation of the branch */
54 entry
= git_reflog_entry_byindex(reflog
, 2);
55 cl_assert(git_oid_streq(&entry
->oid_old
, GIT_OID_HEX_ZERO
) == 0);
57 entry
= git_reflog_entry_byindex(reflog
, 1);
58 assert_signature(committer
, entry
->committer
);
59 cl_assert(git_oid_cmp(oid
, &entry
->oid_old
) == 0);
60 cl_assert(git_oid_cmp(oid
, &entry
->oid_cur
) == 0);
61 cl_assert(entry
->msg
== NULL
);
63 entry
= git_reflog_entry_byindex(reflog
, 0);
64 assert_signature(committer
, entry
->committer
);
65 cl_assert(git_oid_cmp(oid
, &entry
->oid_cur
) == 0);
66 cl_assert_equal_s(commit_msg
, entry
->msg
);
68 git_reflog_free(reflog
);
69 git_repository_free(repo2
);
71 git_reference_free(lookedup_ref
);
74 void test_refs_reflog_reflog__append_then_read(void)
76 /* write a reflog for a given reference and ensure it can be read back */
79 git_signature
*committer
;
82 /* Create a new branch pointing at the HEAD */
83 git_oid_fromstr(&oid
, current_master_tip
);
84 cl_git_pass(git_reference_create(&ref
, g_repo
, new_ref
, &oid
, 0, NULL
));
85 git_reference_free(ref
);
87 cl_git_pass(git_signature_now(&committer
, "foo", "foo@bar"));
89 cl_git_pass(git_reflog_read(&reflog
, g_repo
, new_ref
));
90 cl_git_pass(git_reflog_append(reflog
, &oid
, committer
, NULL
));
91 cl_git_pass(git_reflog_append(reflog
, &oid
, committer
, commit_msg
"\n"));
92 cl_git_pass(git_reflog_write(reflog
));
94 assert_appends(committer
, &oid
);
96 git_reflog_free(reflog
);
97 git_signature_free(committer
);
100 void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
102 git_reference
*master
, *new_master
;
103 git_str master_log_path
= GIT_STR_INIT
, moved_log_path
= GIT_STR_INIT
;
105 git_str_joinpath(&master_log_path
, git_repository_path(g_repo
), GIT_REFLOG_DIR
);
106 git_str_puts(&moved_log_path
, git_str_cstr(&master_log_path
));
107 git_str_joinpath(&master_log_path
, git_str_cstr(&master_log_path
), "refs/heads/master");
108 git_str_joinpath(&moved_log_path
, git_str_cstr(&moved_log_path
), "refs/moved");
110 cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&master_log_path
)));
111 cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&moved_log_path
)));
113 cl_git_pass(git_reference_lookup(&master
, g_repo
, "refs/heads/master"));
114 cl_git_pass(git_reference_rename(&new_master
, master
, "refs/moved", 0, NULL
));
115 git_reference_free(master
);
117 cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&master_log_path
)));
118 cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&moved_log_path
)));
120 git_reference_free(new_master
);
121 git_str_dispose(&moved_log_path
);
122 git_str_dispose(&master_log_path
);
125 void test_refs_reflog_reflog__deleting_the_reference_deletes_the_reflog(void)
127 git_reference
*master
;
128 git_str master_log_path
= GIT_STR_INIT
;
130 git_str_joinpath(&master_log_path
, git_repository_path(g_repo
), GIT_REFLOG_DIR
);
131 git_str_joinpath(&master_log_path
, git_str_cstr(&master_log_path
), "refs/heads/master");
133 cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&master_log_path
)));
135 cl_git_pass(git_reference_lookup(&master
, g_repo
, "refs/heads/master"));
136 cl_git_pass(git_reference_delete(master
));
137 git_reference_free(master
);
139 cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&master_log_path
)));
140 git_str_dispose(&master_log_path
);
143 void test_refs_reflog_reflog__removes_empty_reflog_dir(void)
146 git_str log_path
= GIT_STR_INIT
;
149 /* Create a new branch pointing at the HEAD */
150 git_oid_fromstr(&id
, current_master_tip
);
151 cl_git_pass(git_reference_create(&ref
, g_repo
, "refs/heads/new-dir/new-head", &id
, 0, NULL
));
153 git_str_joinpath(&log_path
, git_repository_path(g_repo
), GIT_REFLOG_DIR
);
154 git_str_joinpath(&log_path
, git_str_cstr(&log_path
), "refs/heads/new-dir/new-head");
156 cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&log_path
)));
158 cl_git_pass(git_reference_delete(ref
));
159 git_reference_free(ref
);
161 /* new ref creation should succeed since new-dir is empty */
162 git_oid_fromstr(&id
, current_master_tip
);
163 cl_git_pass(git_reference_create(&ref
, g_repo
, "refs/heads/new-dir", &id
, 0, NULL
));
164 git_reference_free(ref
);
166 git_str_dispose(&log_path
);
169 void test_refs_reflog_reflog__fails_gracefully_on_nonempty_reflog_dir(void)
172 git_str log_path
= GIT_STR_INIT
;
175 /* Create a new branch pointing at the HEAD */
176 git_oid_fromstr(&id
, current_master_tip
);
177 cl_git_pass(git_reference_create(&ref
, g_repo
, "refs/heads/new-dir/new-head", &id
, 0, NULL
));
178 git_reference_free(ref
);
180 git_str_joinpath(&log_path
, git_repository_path(g_repo
), GIT_REFLOG_DIR
);
181 git_str_joinpath(&log_path
, git_str_cstr(&log_path
), "refs/heads/new-dir/new-head");
183 cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&log_path
)));
185 /* delete the ref manually, leave the reflog */
186 cl_must_pass(p_unlink("testrepo.git/refs/heads/new-dir/new-head"));
188 /* new ref creation should fail since new-dir contains reflogs still */
189 git_oid_fromstr(&id
, current_master_tip
);
190 cl_git_fail_with(GIT_EDIRECTORY
, git_reference_create(&ref
, g_repo
, "refs/heads/new-dir", &id
, 0, NULL
));
191 git_reference_free(ref
);
193 git_str_dispose(&log_path
);
196 static void assert_has_reflog(bool expected_result
, const char *name
)
198 cl_assert_equal_i(expected_result
, git_reference_has_log(g_repo
, name
));
201 void test_refs_reflog_reflog__reference_has_reflog(void)
203 assert_has_reflog(true, "HEAD");
204 assert_has_reflog(true, "refs/heads/master");
205 assert_has_reflog(false, "refs/heads/subtrees");
208 void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_returns_an_empty_one(void)
211 const char *refname
= "refs/heads/subtrees";
212 git_str subtrees_log_path
= GIT_STR_INIT
;
214 git_str_join_n(&subtrees_log_path
, '/', 3, git_repository_path(g_repo
), GIT_REFLOG_DIR
, refname
);
215 cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&subtrees_log_path
)));
217 cl_git_pass(git_reflog_read(&reflog
, g_repo
, refname
));
219 cl_assert_equal_i(0, (int)git_reflog_entrycount(reflog
));
221 git_reflog_free(reflog
);
222 git_str_dispose(&subtrees_log_path
);
225 void test_refs_reflog_reflog__reading_a_reflog_with_invalid_format_succeeds(void)
228 const char *refname
= "refs/heads/newline";
229 const char *refmessage
=
230 "Reflog*message with a newline and enough content after it to pass the GIT_REFLOG_SIZE_MIN check inside reflog_parse.";
231 const git_reflog_entry
*entry
;
234 git_str logpath
= GIT_STR_INIT
, logcontents
= GIT_STR_INIT
;
237 /* Create a new branch. */
238 cl_git_pass(git_oid_fromstr(&id
, current_master_tip
));
239 cl_git_pass(git_reference_create(&ref
, g_repo
, refname
, &id
, 1, refmessage
));
242 * Corrupt the branch reflog by introducing a newline inside the reflog message.
243 * We do this by replacing '*' with '\n'
245 cl_git_pass(git_str_join_n(&logpath
, '/', 3, git_repository_path(g_repo
), GIT_REFLOG_DIR
, refname
));
246 cl_git_pass(git_futils_readbuffer(&logcontents
, git_str_cstr(&logpath
)));
247 cl_assert((star
= strchr(git_str_cstr(&logcontents
), '*')) != NULL
);
249 cl_git_rewritefile(git_str_cstr(&logpath
), git_str_cstr(&logcontents
));
252 * Confirm that the file was rewritten successfully
253 * and now contains a '\n' in the expected location
255 cl_git_pass(git_futils_readbuffer(&logcontents
, git_str_cstr(&logpath
)));
256 cl_assert(strstr(git_str_cstr(&logcontents
), "Reflog\nmessage") != NULL
);
258 cl_git_pass(git_reflog_read(&reflog
, g_repo
, refname
));
259 cl_assert(entry
= git_reflog_entry_byindex(reflog
, 0));
260 cl_assert_equal_s(git_reflog_entry_message(entry
), "Reflog");
262 git_reference_free(ref
);
263 git_reflog_free(reflog
);
264 git_str_dispose(&logpath
);
265 git_str_dispose(&logcontents
);
268 void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void)
270 git_reference
*master
, *new_master
;
271 git_str master_log_path
= GIT_STR_INIT
, moved_log_path
= GIT_STR_INIT
;
274 cl_git_pass(git_reference_lookup(&master
, g_repo
, "refs/heads/master"));
275 cl_git_pass(git_reflog_read(&reflog
, g_repo
, "refs/heads/master"));
277 cl_git_pass(git_reflog_write(reflog
));
279 cl_git_pass(git_reference_rename(&new_master
, master
, "refs/moved", 0, NULL
));
280 git_reference_free(master
);
282 cl_git_fail(git_reflog_write(reflog
));
284 git_reflog_free(reflog
);
285 git_reference_free(new_master
);
286 git_str_dispose(&moved_log_path
);
287 git_str_dispose(&master_log_path
);
290 void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC(void)
292 cl_assert_equal_i(GIT_EINVALIDSPEC
,
293 git_reflog_rename(g_repo
, "refs/heads/master", "refs/heads/Inv@{id"));
296 void test_refs_reflog_reflog__write_only_std_locations(void)
301 git_oid_fromstr(&id
, current_master_tip
);
303 cl_git_pass(git_reference_create(&ref
, g_repo
, "refs/heads/foo", &id
, 1, NULL
));
304 git_reference_free(ref
);
305 cl_git_pass(git_reference_create(&ref
, g_repo
, "refs/tags/foo", &id
, 1, NULL
));
306 git_reference_free(ref
);
307 cl_git_pass(git_reference_create(&ref
, g_repo
, "refs/notes/foo", &id
, 1, NULL
));
308 git_reference_free(ref
);
310 assert_has_reflog(true, "refs/heads/foo");
311 assert_has_reflog(false, "refs/tags/foo");
312 assert_has_reflog(true, "refs/notes/foo");
316 void test_refs_reflog_reflog__write_when_explicitly_active(void)
321 git_oid_fromstr(&id
, current_master_tip
);
322 git_reference_ensure_log(g_repo
, "refs/tags/foo");
324 cl_git_pass(git_reference_create(&ref
, g_repo
, "refs/tags/foo", &id
, 1, NULL
));
325 git_reference_free(ref
);
326 assert_has_reflog(true, "refs/tags/foo");
329 void test_refs_reflog_reflog__append_to_HEAD_when_changing_current_branch(void)
331 size_t nlogs
, nlogs_after
;
336 cl_git_pass(git_reflog_read(&log
, g_repo
, "HEAD"));
337 nlogs
= git_reflog_entrycount(log
);
338 git_reflog_free(log
);
341 git_oid_fromstr(&id
, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
342 cl_git_pass(git_reference_create(&ref
, g_repo
, "refs/heads/master", &id
, 1, NULL
));
343 git_reference_free(ref
);
345 cl_git_pass(git_reflog_read(&log
, g_repo
, "HEAD"));
346 nlogs_after
= git_reflog_entrycount(log
);
347 git_reflog_free(log
);
349 cl_assert_equal_i(nlogs_after
, nlogs
+ 1);
352 void test_refs_reflog_reflog__do_not_append_when_no_update(void)
354 size_t nlogs
, nlogs_after
;
355 git_reference
*ref
, *ref2
;
358 cl_git_pass(git_reflog_read(&log
, g_repo
, "HEAD"));
359 nlogs
= git_reflog_entrycount(log
);
360 git_reflog_free(log
);
362 cl_git_pass(git_reference_lookup(&ref
, g_repo
, "refs/heads/master"));
363 cl_git_pass(git_reference_create(&ref2
, g_repo
, "refs/heads/master",
364 git_reference_target(ref
), 1, NULL
));
366 git_reference_free(ref
);
367 git_reference_free(ref2
);
369 cl_git_pass(git_reflog_read(&log
, g_repo
, "HEAD"));
370 nlogs_after
= git_reflog_entrycount(log
);
371 git_reflog_free(log
);
373 cl_assert_equal_i(nlogs_after
, nlogs
);
376 static void assert_no_reflog_update(void)
378 size_t nlogs
, nlogs_after
;
379 size_t nlogs_master
, nlogs_master_after
;
384 cl_git_pass(git_reflog_read(&log
, g_repo
, "HEAD"));
385 nlogs
= git_reflog_entrycount(log
);
386 git_reflog_free(log
);
388 cl_git_pass(git_reflog_read(&log
, g_repo
, "refs/heads/master"));
389 nlogs_master
= git_reflog_entrycount(log
);
390 git_reflog_free(log
);
393 git_oid_fromstr(&id
, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
394 cl_git_pass(git_reference_create(&ref
, g_repo
, "refs/heads/master", &id
, 1, NULL
));
395 git_reference_free(ref
);
397 cl_git_pass(git_reflog_read(&log
, g_repo
, "HEAD"));
398 nlogs_after
= git_reflog_entrycount(log
);
399 git_reflog_free(log
);
401 cl_assert_equal_i(nlogs_after
, nlogs
);
403 cl_git_pass(git_reflog_read(&log
, g_repo
, "refs/heads/master"));
404 nlogs_master_after
= git_reflog_entrycount(log
);
405 git_reflog_free(log
);
407 cl_assert_equal_i(nlogs_after
, nlogs
);
408 cl_assert_equal_i(nlogs_master_after
, nlogs_master
);
412 void test_refs_reflog_reflog__logallrefupdates_bare_set_false(void)
416 cl_git_pass(git_repository_config(&config
, g_repo
));
417 cl_git_pass(git_config_set_bool(config
, "core.logallrefupdates", false));
418 git_config_free(config
);
420 assert_no_reflog_update();
423 void test_refs_reflog_reflog__logallrefupdates_bare_set_always(void)
430 cl_git_pass(git_repository_config(&config
, g_repo
));
431 cl_git_pass(git_config_set_string(config
, "core.logallrefupdates", "always"));
432 git_config_free(config
);
434 git_oid_fromstr(&id
, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
435 cl_git_pass(git_reference_create(&ref
, g_repo
, "refs/bork", &id
, 1, "message"));
437 cl_git_pass(git_reflog_read(&log
, g_repo
, "refs/bork"));
438 cl_assert_equal_i(1, git_reflog_entrycount(log
));
439 cl_assert_equal_s("message", git_reflog_entry_byindex(log
, 0)->msg
);
441 git_reflog_free(log
);
442 git_reference_free(ref
);
445 void test_refs_reflog_reflog__logallrefupdates_bare_unset(void)
449 cl_git_pass(git_repository_config(&config
, g_repo
));
450 cl_git_pass(git_config_delete_entry(config
, "core.logallrefupdates"));
451 git_config_free(config
);
453 assert_no_reflog_update();
456 void test_refs_reflog_reflog__logallrefupdates_nonbare_set_false(void)
460 cl_git_sandbox_cleanup();
461 g_repo
= cl_git_sandbox_init("testrepo");
464 cl_git_pass(git_repository_config(&config
, g_repo
));
465 cl_git_pass(git_config_set_bool(config
, "core.logallrefupdates", false));
466 git_config_free(config
);
468 assert_no_reflog_update();