1 #include "clar_libgit2.h"
2 
3 #include "futils.h"
4 #include "git2/reflog.h"
5 #include "reflog.h"
6 #include "refs.h"
7 #include "reflog_helpers.h"
8 
9 static const char *g_email = "foo@example.com";
10 static git_repository *g_repo;
11 
12 /* Fixture setup and teardown */
test_refs_reflog_messages__initialize(void)13 void test_refs_reflog_messages__initialize(void)
14 {
15 	g_repo = cl_git_sandbox_init("testrepo.git");
16 	cl_git_pass(git_repository_set_ident(g_repo, "Foo Bar", g_email));
17 }
18 
test_refs_reflog_messages__cleanup(void)19 void test_refs_reflog_messages__cleanup(void)
20 {
21 	cl_git_sandbox_cleanup();
22 }
23 
test_refs_reflog_messages__setting_head_updates_reflog(void)24 void test_refs_reflog_messages__setting_head_updates_reflog(void)
25 {
26 	git_object *tag;
27 	git_annotated_commit *annotated;
28 
29 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); /* 4 */
30 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/unborn"));
31 	cl_git_pass(git_revparse_single(&tag, g_repo, "tags/test"));
32 	cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(tag))); /* 3 */
33 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));        /* 2 */
34 	cl_git_pass(git_repository_set_head(g_repo, "refs/tags/test"));            /* 1 */
35 	cl_git_pass(git_repository_set_head(g_repo, "refs/remotes/test/master"));  /* 0 */
36 
37 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 4,
38 		NULL, "refs/heads/haacked",
39 		"foo@example.com",
40 		"checkout: moving from master to haacked");
41 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 3,
42 		NULL, "tags/test^{commit}",
43 		"foo@example.com",
44 		"checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d");
45 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 2,
46 		"tags/test^{commit}", "refs/heads/haacked",
47 		"foo@example.com",
48 		"checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked");
49 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 1,
50 		"refs/heads/haacked", "tags/test^{commit}",
51 		"foo@example.com",
52 		"checkout: moving from haacked to test");
53 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
54 		"tags/test^{commit}", "refs/remotes/test/master",
55 		"foo@example.com",
56 		"checkout: moving from e90810b8df3e80c413d903f631643c716887138d to test/master");
57 
58 	cl_git_pass(git_annotated_commit_from_revspec(&annotated, g_repo, "haacked~0"));
59 	cl_git_pass(git_repository_set_head_detached_from_annotated(g_repo, annotated));
60 
61 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
62 		NULL, "refs/heads/haacked",
63 		"foo@example.com",
64 		"checkout: moving from be3563ae3f795b2b4353bcce3a527ad0a4f7f644 to haacked~0");
65 
66 	git_annotated_commit_free(annotated);
67 	git_object_free(tag);
68 }
69 
test_refs_reflog_messages__setting_head_to_same_target_ignores_reflog(void)70 void test_refs_reflog_messages__setting_head_to_same_target_ignores_reflog(void)
71 {
72 	size_t nentries, nentries_after;
73 
74 	nentries = reflog_entrycount(g_repo, GIT_HEAD_FILE);
75 
76 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
77 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
78 
79 	nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE);
80 
81 	cl_assert_equal_i(nentries + 1, nentries_after);
82 }
83 
test_refs_reflog_messages__detaching_writes_reflog(void)84 void test_refs_reflog_messages__detaching_writes_reflog(void)
85 {
86 	git_oid id;
87 	const char *msg;
88 
89 	msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d";
90 	git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d");
91 	cl_git_pass(git_repository_set_head_detached(g_repo, &id));
92 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
93 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
94 		"e90810b8df3e80c413d903f631643c716887138d",
95 		NULL, msg);
96 
97 	msg = "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked";
98 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
99 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
100 		"e90810b8df3e80c413d903f631643c716887138d",
101 		"258f0e2a959a364e40ed6603d5d44fbb24765b10",
102 		NULL, msg);
103 }
104 
test_refs_reflog_messages__orphan_branch_does_not_count(void)105 void test_refs_reflog_messages__orphan_branch_does_not_count(void)
106 {
107 	git_oid id;
108 	const char *msg;
109 
110 	/* Have something known */
111 	msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d";
112 	git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d");
113 	cl_git_pass(git_repository_set_head_detached(g_repo, &id));
114 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
115 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
116 		"e90810b8df3e80c413d903f631643c716887138d",
117 		NULL, msg);
118 
119 	/* Switching to an orphan branch does not write to the reflog */
120 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/orphan"));
121 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
122 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
123 		"e90810b8df3e80c413d903f631643c716887138d",
124 		NULL, msg);
125 
126 	/* And coming back, we set the source to zero */
127 	msg = "checkout: moving from orphan to haacked";
128 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
129 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
130 		"0000000000000000000000000000000000000000",
131 		"258f0e2a959a364e40ed6603d5d44fbb24765b10",
132 		NULL, msg);
133 }
134 
test_refs_reflog_messages__branch_birth(void)135 void test_refs_reflog_messages__branch_birth(void)
136 {
137 	git_signature *sig;
138 	git_oid id;
139 	git_tree *tree;
140 	git_reference *ref;
141 	const char *msg;
142 	size_t nentries, nentries_after;
143 
144 	nentries = reflog_entrycount(g_repo, GIT_HEAD_FILE);
145 
146 	cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
147 
148 	cl_git_pass(git_repository_head(&ref, g_repo));
149 	cl_git_pass(git_reference_peel((git_object **) &tree, ref, GIT_OBJECT_TREE));
150 
151 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/orphan"));
152 
153 	nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE);
154 
155 	cl_assert_equal_i(nentries, nentries_after);
156 
157 	msg = "message 2";
158 	cl_git_pass(git_commit_create(&id, g_repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
159 
160 	cl_assert_equal_i(1, reflog_entrycount(g_repo, "refs/heads/orphan"));
161 
162 	nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE);
163 
164 	cl_assert_equal_i(nentries + 1, nentries_after);
165 
166 	git_signature_free(sig);
167 	git_tree_free(tree);
168 	git_reference_free(ref);
169 }
170 
test_refs_reflog_messages__commit_on_symbolic_ref_updates_head_reflog(void)171 void test_refs_reflog_messages__commit_on_symbolic_ref_updates_head_reflog(void)
172 {
173 	git_signature *sig;
174 	git_oid id;
175 	git_tree *tree;
176 	git_reference *ref1, *ref2;
177 	const char *msg;
178 	size_t nentries_head, nentries_master;
179 
180 	nentries_head = reflog_entrycount(g_repo, GIT_HEAD_FILE);
181 
182 	cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
183 
184 	cl_git_pass(git_repository_head(&ref1, g_repo));
185 	cl_git_pass(git_reference_peel((git_object **) &tree, ref1, GIT_OBJECT_TREE));
186 
187 	nentries_master = reflog_entrycount(g_repo, "refs/heads/master");
188 
189 	msg = "message 1";
190 	cl_git_pass(git_reference_symbolic_create(&ref2, g_repo, "refs/heads/master", "refs/heads/foo", 1, msg));
191 
192 	cl_assert_equal_i(0, reflog_entrycount(g_repo, "refs/heads/foo"));
193 	cl_assert_equal_i(nentries_head, reflog_entrycount(g_repo, GIT_HEAD_FILE));
194 	cl_assert_equal_i(nentries_master, reflog_entrycount(g_repo, "refs/heads/master"));
195 
196 	msg = "message 2";
197 	cl_git_pass(git_commit_create(&id, g_repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
198 
199 	cl_assert_equal_i(1, reflog_entrycount(g_repo, "refs/heads/foo"));
200 	cl_assert_equal_i(nentries_head + 1, reflog_entrycount(g_repo, GIT_HEAD_FILE));
201 	cl_assert_equal_i(nentries_master, reflog_entrycount(g_repo, "refs/heads/master"));
202 
203 	git_signature_free(sig);
204 	git_reference_free(ref1);
205 	git_reference_free(ref2);
206 	git_tree_free(tree);
207 }
208 
test_refs_reflog_messages__show_merge_for_merge_commits(void)209 void test_refs_reflog_messages__show_merge_for_merge_commits(void)
210 {
211 	git_oid b1_oid;
212 	git_oid b2_oid;
213 	git_oid merge_commit_oid;
214 	git_commit *b1_commit;
215 	git_commit *b2_commit;
216 	git_signature *s;
217 	git_commit *parent_commits[2];
218 	git_tree *tree;
219 
220 	cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
221 
222 	cl_git_pass(git_reference_name_to_id(&b1_oid, g_repo, "HEAD"));
223 	cl_git_pass(git_reference_name_to_id(&b2_oid, g_repo, "refs/heads/test"));
224 
225 	cl_git_pass(git_commit_lookup(&b1_commit, g_repo, &b1_oid));
226 	cl_git_pass(git_commit_lookup(&b2_commit, g_repo, &b2_oid));
227 
228 	parent_commits[0] = b1_commit;
229 	parent_commits[1] = b2_commit;
230 
231 	cl_git_pass(git_commit_tree(&tree, b1_commit));
232 
233 	cl_git_pass(git_commit_create(&merge_commit_oid,
234 		g_repo, "HEAD", s, s, NULL,
235 		"Merge commit", tree,
236 		2, (const struct git_commit **) parent_commits));
237 
238 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
239 		NULL,
240 		git_oid_tostr_s(&merge_commit_oid),
241 		NULL, "commit (merge): Merge commit");
242 
243 	git_tree_free(tree);
244 	git_commit_free(b1_commit);
245 	git_commit_free(b2_commit);
246 	git_signature_free(s);
247 }
248 
test_refs_reflog_messages__creating_a_direct_reference(void)249 void test_refs_reflog_messages__creating_a_direct_reference(void)
250 {
251 	git_reference *reference;
252 	git_oid id;
253 	git_reflog *reflog;
254 	const git_reflog_entry *entry;
255 
256 	const char *name = "refs/heads/new-head";
257 	const char *message = "You've been logged, mate!";
258 
259 	cl_git_pass(git_reference_name_to_id(&id, g_repo, "HEAD"));
260 
261 	cl_git_pass(git_reference_create(&reference, g_repo, name, &id, 0, message));
262 
263 	cl_git_pass(git_reflog_read(&reflog, g_repo, name));
264 	cl_assert_equal_sz(1, git_reflog_entrycount(reflog));
265 
266 	entry = git_reflog_entry_byindex(reflog, 0);
267 	cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
268 	cl_assert_equal_oid(&id, &entry->oid_cur);
269 	cl_assert_equal_s(message, entry->msg);
270 
271 	git_reflog_free(reflog);
272 	git_reference_free(reference);
273 }
274 
test_refs_reflog_messages__newline_gets_replaced(void)275 void test_refs_reflog_messages__newline_gets_replaced(void)
276 {
277 	const git_reflog_entry *entry;
278 	git_signature *signature;
279 	git_reflog *reflog;
280 	git_oid oid;
281 
282 	cl_git_pass(git_signature_now(&signature, "me", "foo@example.com"));
283 	cl_git_pass(git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
284 
285 	cl_git_pass(git_reflog_read(&reflog, g_repo, "HEAD"));
286 	cl_assert_equal_sz(7, git_reflog_entrycount(reflog));
287 	cl_git_pass(git_reflog_append(reflog, &oid, signature, "inner\nnewline"));
288 	cl_assert_equal_sz(8, git_reflog_entrycount(reflog));
289 
290 	cl_assert(entry = git_reflog_entry_byindex(reflog, 0));
291 	cl_assert_equal_s(git_reflog_entry_message(entry), "inner newline");
292 
293 	git_signature_free(signature);
294 	git_reflog_free(reflog);
295 }
296 
test_refs_reflog_messages__renaming_ref(void)297 void test_refs_reflog_messages__renaming_ref(void)
298 {
299 	git_reference *ref, *new_ref;
300 
301 	cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
302 	cl_git_pass(git_reference_rename(&new_ref, ref, "refs/heads/renamed", false,
303 									 "message"));
304 
305 	cl_reflog_check_entry(g_repo, git_reference_name(new_ref), 0,
306 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
307 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
308 		"foo@example.com", "message");
309 
310 	git_reference_free(ref);
311 	git_reference_free(new_ref);
312 }
313 
test_refs_reflog_messages__updating_a_direct_reference(void)314 void test_refs_reflog_messages__updating_a_direct_reference(void)
315 {
316 	git_reference *ref, *ref_out, *target_ref;
317 	git_oid target_id;
318 	const char *message = "You've been logged, mate!";
319 
320 	git_reference_name_to_id(&target_id, g_repo, "refs/heads/haacked");
321 	cl_git_pass(git_reference_lookup(&target_ref, g_repo, "refs/heads/haacked"));
322 
323 	cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
324 
325 	cl_git_pass(git_reference_set_target(&ref_out, ref, &target_id, message));
326 
327 	cl_reflog_check_entry(g_repo, "refs/heads/master", 0,
328 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
329 		"258f0e2a959a364e40ed6603d5d44fbb24765b10",
330 		NULL, message);
331 
332 	git_reference_free(target_ref);
333 	git_reference_free(ref);
334 	git_reference_free(ref_out);
335 }
336 
337 #define NEW_BRANCH_NAME "new-branch-on-the-block"
338 
test_refs_reflog_messages__creating_branches_default_messages(void)339 void test_refs_reflog_messages__creating_branches_default_messages(void)
340 {
341 	git_buf buf = GIT_BUF_INIT;
342 	git_annotated_commit *annotated;
343 	git_object *obj;
344 	git_commit *target;
345 	git_reference *branch1, *branch2;
346 
347 	cl_git_pass(git_revparse_single(&obj, g_repo, "e90810b8df3"));
348 	cl_git_pass(git_commit_lookup(&target, g_repo, git_object_id(obj)));
349 	git_object_free(obj);
350 
351 	cl_git_pass(git_branch_create(&branch1, g_repo, NEW_BRANCH_NAME, target, false));
352 
353 	cl_git_pass(git_buf_printf(&buf, "branch: Created from %s", git_oid_tostr_s(git_commit_id(target))));
354 	cl_reflog_check_entry(g_repo, "refs/heads/" NEW_BRANCH_NAME, 0,
355 		GIT_OID_HEX_ZERO,
356 		git_oid_tostr_s(git_commit_id(target)),
357 		g_email, git_buf_cstr(&buf));
358 
359 	cl_git_pass(git_reference_remove(g_repo, "refs/heads/" NEW_BRANCH_NAME));
360 
361 	cl_git_pass(git_annotated_commit_from_revspec(&annotated, g_repo, "e90810b8df3"));
362 	cl_git_pass(git_branch_create_from_annotated(&branch2, g_repo, NEW_BRANCH_NAME, annotated, true));
363 
364 	cl_reflog_check_entry(g_repo, "refs/heads/" NEW_BRANCH_NAME, 0,
365 		GIT_OID_HEX_ZERO,
366 		git_oid_tostr_s(git_commit_id(target)),
367 		g_email, "branch: Created from e90810b8df3");
368 
369 	git_annotated_commit_free(annotated);
370 	git_buf_dispose(&buf);
371 	git_commit_free(target);
372 	git_reference_free(branch1);
373 	git_reference_free(branch2);
374 }
375 
test_refs_reflog_messages__moving_branch_default_message(void)376 void test_refs_reflog_messages__moving_branch_default_message(void)
377 {
378 	git_reference *branch;
379 	git_reference *new_branch;
380 	git_oid id;
381 
382 	cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/master"));
383 	git_oid_cpy(&id, git_reference_target(branch));
384 	cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0));
385 
386 	cl_reflog_check_entry(g_repo, git_reference_name(new_branch), 0,
387 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
388 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
389 		g_email,
390 		"branch: renamed refs/heads/master to refs/heads/master2");
391 
392 	git_reference_free(branch);
393 	git_reference_free(new_branch);
394 }
395 
test_refs_reflog_messages__detaching_head_default_message(void)396 void test_refs_reflog_messages__detaching_head_default_message(void)
397 {
398 	git_reference *ref;
399 
400 	cl_assert_equal_i(false, git_repository_head_detached(g_repo));
401 
402 	cl_git_pass(git_repository_detach_head(g_repo));
403 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
404 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
405 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
406 		NULL, "checkout: moving from master to a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
407 	cl_assert_equal_i(true, git_repository_head_detached(g_repo));
408 
409 	/* take the repo back to its original state */
410 	cl_git_pass(git_reference_symbolic_create(&ref, g_repo, "HEAD", "refs/heads/master",
411 											  true, "REATTACH"));
412 
413 	cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
414 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
415 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
416 		NULL, "REATTACH");
417 
418 	cl_assert_equal_i(false, git_repository_head_detached(g_repo));
419 
420 	git_reference_free(ref);
421 }
422