]> git.proxmox.com Git - libgit2.git/blob - tests/refs/reflog/reflog.c
32ce7ffb7adb583f14d17e39ba0ba53f4509fb78
[libgit2.git] / tests / refs / reflog / reflog.c
1 #include "clar_libgit2.h"
2
3 #include "futils.h"
4 #include "git2/reflog.h"
5 #include "reflog.h"
6
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"
10
11 static git_repository *g_repo;
12
13
14 /* helpers */
15 static void assert_signature(const git_signature *expected, const git_signature *actual)
16 {
17 cl_assert(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);
22 }
23
24
25 /* Fixture setup and teardown */
26 void test_refs_reflog_reflog__initialize(void)
27 {
28 g_repo = cl_git_sandbox_init("testrepo.git");
29 }
30
31 void test_refs_reflog_reflog__cleanup(void)
32 {
33 cl_git_sandbox_cleanup();
34 }
35
36 static void assert_appends(const git_signature *committer, const git_oid *oid)
37 {
38 git_repository *repo2;
39 git_reference *lookedup_ref;
40 git_reflog *reflog;
41 const git_reflog_entry *entry;
42
43 /* Reopen a new instance of the repository */
44 cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
45
46 /* Lookup the previously created branch */
47 cl_git_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref));
48
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));
52
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);
56
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);
62
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);
67
68 git_reflog_free(reflog);
69 git_repository_free(repo2);
70
71 git_reference_free(lookedup_ref);
72 }
73
74 void test_refs_reflog_reflog__append_then_read(void)
75 {
76 /* write a reflog for a given reference and ensure it can be read back */
77 git_reference *ref;
78 git_oid oid;
79 git_signature *committer;
80 git_reflog *reflog;
81
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);
86
87 cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
88
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));
93
94 assert_appends(committer, &oid);
95
96 git_reflog_free(reflog);
97 git_signature_free(committer);
98 }
99
100 void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
101 {
102 git_reference *master, *new_master;
103 git_str master_log_path = GIT_STR_INIT, moved_log_path = GIT_STR_INIT;
104
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");
109
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)));
112
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);
116
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)));
119
120 git_reference_free(new_master);
121 git_str_dispose(&moved_log_path);
122 git_str_dispose(&master_log_path);
123 }
124
125 void test_refs_reflog_reflog__deleting_the_reference_deletes_the_reflog(void)
126 {
127 git_reference *master;
128 git_str master_log_path = GIT_STR_INIT;
129
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");
132
133 cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&master_log_path)));
134
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);
138
139 cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&master_log_path)));
140 git_str_dispose(&master_log_path);
141 }
142
143 void test_refs_reflog_reflog__removes_empty_reflog_dir(void)
144 {
145 git_reference *ref;
146 git_str log_path = GIT_STR_INIT;
147 git_oid id;
148
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));
152
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");
155
156 cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&log_path)));
157
158 cl_git_pass(git_reference_delete(ref));
159 git_reference_free(ref);
160
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);
165
166 git_str_dispose(&log_path);
167 }
168
169 void test_refs_reflog_reflog__fails_gracefully_on_nonempty_reflog_dir(void)
170 {
171 git_reference *ref;
172 git_str log_path = GIT_STR_INIT;
173 git_oid id;
174
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);
179
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");
182
183 cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&log_path)));
184
185 /* delete the ref manually, leave the reflog */
186 cl_must_pass(p_unlink("testrepo.git/refs/heads/new-dir/new-head"));
187
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);
192
193 git_str_dispose(&log_path);
194 }
195
196 static void assert_has_reflog(bool expected_result, const char *name)
197 {
198 cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name));
199 }
200
201 void test_refs_reflog_reflog__reference_has_reflog(void)
202 {
203 assert_has_reflog(true, "HEAD");
204 assert_has_reflog(true, "refs/heads/master");
205 assert_has_reflog(false, "refs/heads/subtrees");
206 }
207
208 void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_returns_an_empty_one(void)
209 {
210 git_reflog *reflog;
211 const char *refname = "refs/heads/subtrees";
212 git_str subtrees_log_path = GIT_STR_INIT;
213
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)));
216
217 cl_git_pass(git_reflog_read(&reflog, g_repo, refname));
218
219 cl_assert_equal_i(0, (int)git_reflog_entrycount(reflog));
220
221 git_reflog_free(reflog);
222 git_str_dispose(&subtrees_log_path);
223 }
224
225 void test_refs_reflog_reflog__reading_a_reflog_with_invalid_format_succeeds(void)
226 {
227 git_reflog *reflog;
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;
232 git_reference *ref;
233 git_oid id;
234 git_str logpath = GIT_STR_INIT, logcontents = GIT_STR_INIT;
235 char *star;
236
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));
240
241 /*
242 * Corrupt the branch reflog by introducing a newline inside the reflog message.
243 * We do this by replacing '*' with '\n'
244 */
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);
248 *star = '\n';
249 cl_git_rewritefile(git_str_cstr(&logpath), git_str_cstr(&logcontents));
250
251 /*
252 * Confirm that the file was rewritten successfully
253 * and now contains a '\n' in the expected location
254 */
255 cl_git_pass(git_futils_readbuffer(&logcontents, git_str_cstr(&logpath)));
256 cl_assert(strstr(git_str_cstr(&logcontents), "Reflog\nmessage") != NULL);
257
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");
261
262 git_reference_free(ref);
263 git_reflog_free(reflog);
264 git_str_dispose(&logpath);
265 git_str_dispose(&logcontents);
266 }
267
268 void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void)
269 {
270 git_reference *master, *new_master;
271 git_str master_log_path = GIT_STR_INIT, moved_log_path = GIT_STR_INIT;
272 git_reflog *reflog;
273
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"));
276
277 cl_git_pass(git_reflog_write(reflog));
278
279 cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0, NULL));
280 git_reference_free(master);
281
282 cl_git_fail(git_reflog_write(reflog));
283
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);
288 }
289
290 void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC(void)
291 {
292 cl_assert_equal_i(GIT_EINVALIDSPEC,
293 git_reflog_rename(g_repo, "refs/heads/master", "refs/heads/Inv@{id"));
294 }
295
296 void test_refs_reflog_reflog__write_only_std_locations(void)
297 {
298 git_reference *ref;
299 git_oid id;
300
301 git_oid_fromstr(&id, current_master_tip);
302
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);
309
310 assert_has_reflog(true, "refs/heads/foo");
311 assert_has_reflog(false, "refs/tags/foo");
312 assert_has_reflog(true, "refs/notes/foo");
313
314 }
315
316 void test_refs_reflog_reflog__write_when_explicitly_active(void)
317 {
318 git_reference *ref;
319 git_oid id;
320
321 git_oid_fromstr(&id, current_master_tip);
322 git_reference_ensure_log(g_repo, "refs/tags/foo");
323
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");
327 }
328
329 void test_refs_reflog_reflog__append_to_HEAD_when_changing_current_branch(void)
330 {
331 size_t nlogs, nlogs_after;
332 git_reference *ref;
333 git_reflog *log;
334 git_oid id;
335
336 cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
337 nlogs = git_reflog_entrycount(log);
338 git_reflog_free(log);
339
340 /* Move it back */
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);
344
345 cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
346 nlogs_after = git_reflog_entrycount(log);
347 git_reflog_free(log);
348
349 cl_assert_equal_i(nlogs_after, nlogs + 1);
350 }
351
352 void test_refs_reflog_reflog__do_not_append_when_no_update(void)
353 {
354 size_t nlogs, nlogs_after;
355 git_reference *ref, *ref2;
356 git_reflog *log;
357
358 cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
359 nlogs = git_reflog_entrycount(log);
360 git_reflog_free(log);
361
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));
365
366 git_reference_free(ref);
367 git_reference_free(ref2);
368
369 cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
370 nlogs_after = git_reflog_entrycount(log);
371 git_reflog_free(log);
372
373 cl_assert_equal_i(nlogs_after, nlogs);
374 }
375
376 static void assert_no_reflog_update(void)
377 {
378 size_t nlogs, nlogs_after;
379 size_t nlogs_master, nlogs_master_after;
380 git_reference *ref;
381 git_reflog *log;
382 git_oid id;
383
384 cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
385 nlogs = git_reflog_entrycount(log);
386 git_reflog_free(log);
387
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);
391
392 /* Move it back */
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);
396
397 cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
398 nlogs_after = git_reflog_entrycount(log);
399 git_reflog_free(log);
400
401 cl_assert_equal_i(nlogs_after, nlogs);
402
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);
406
407 cl_assert_equal_i(nlogs_after, nlogs);
408 cl_assert_equal_i(nlogs_master_after, nlogs_master);
409
410 }
411
412 void test_refs_reflog_reflog__logallrefupdates_bare_set_false(void)
413 {
414 git_config *config;
415
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);
419
420 assert_no_reflog_update();
421 }
422
423 void test_refs_reflog_reflog__logallrefupdates_bare_set_always(void)
424 {
425 git_config *config;
426 git_reference *ref;
427 git_reflog *log;
428 git_oid id;
429
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);
433
434 git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
435 cl_git_pass(git_reference_create(&ref, g_repo, "refs/bork", &id, 1, "message"));
436
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);
440
441 git_reflog_free(log);
442 git_reference_free(ref);
443 }
444
445 void test_refs_reflog_reflog__logallrefupdates_bare_unset(void)
446 {
447 git_config *config;
448
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);
452
453 assert_no_reflog_update();
454 }
455
456 void test_refs_reflog_reflog__logallrefupdates_nonbare_set_false(void)
457 {
458 git_config *config;
459
460 cl_git_sandbox_cleanup();
461 g_repo = cl_git_sandbox_init("testrepo");
462
463
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);
467
468 assert_no_reflog_update();
469 }