]> git.proxmox.com Git - libgit2.git/blob - tests/stash/save.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / tests / stash / save.c
1 #include "clar_libgit2.h"
2 #include "futils.h"
3 #include "stash_helpers.h"
4
5 static git_repository *repo;
6 static git_signature *signature;
7 static git_oid stash_tip_oid;
8
9 /*
10 * Friendly reminder, in order to ease the reading of the following tests:
11 *
12 * "stash" points to the worktree commit
13 * "stash^1" points to the base commit (HEAD when the stash was created)
14 * "stash^2" points to the index commit
15 * "stash^3" points to the untracked commit
16 */
17
18 void test_stash_save__initialize(void)
19 {
20 cl_git_pass(git_repository_init(&repo, "stash", 0));
21 cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
22
23 setup_stash(repo, signature);
24 }
25
26 void test_stash_save__cleanup(void)
27 {
28 git_signature_free(signature);
29 signature = NULL;
30
31 git_repository_free(repo);
32 repo = NULL;
33
34 cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_RMDIR_REMOVE_FILES));
35 cl_fixture_cleanup("sorry-it-is-a-non-bare-only-party");
36 }
37
38 static void assert_object_oid(const char* revision, const char* expected_oid, git_object_t type)
39 {
40 int result;
41 git_object *obj;
42
43 result = git_revparse_single(&obj, repo, revision);
44
45 if (!expected_oid) {
46 cl_assert_equal_i(GIT_ENOTFOUND, result);
47 return;
48 } else
49 cl_assert_equal_i(0, result);
50
51 cl_git_pass(git_oid_streq(git_object_id(obj), expected_oid));
52 cl_assert_equal_i(type, git_object_type(obj));
53 git_object_free(obj);
54 }
55
56 static void assert_blob_oid(const char* revision, const char* expected_oid)
57 {
58 assert_object_oid(revision, expected_oid, GIT_OBJECT_BLOB);
59 }
60
61 void test_stash_save__does_not_keep_index_by_default(void)
62 {
63 /*
64 $ git stash
65
66 $ git show refs/stash:what
67 see you later
68
69 $ git show refs/stash:how
70 not so small and
71
72 $ git show refs/stash:who
73 funky world
74
75 $ git show refs/stash:when
76 fatal: Path 'when' exists on disk, but not in 'stash'.
77
78 $ git show refs/stash^2:what
79 goodbye
80
81 $ git show refs/stash^2:how
82 not so small and
83
84 $ git show refs/stash^2:who
85 world
86
87 $ git show refs/stash^2:when
88 fatal: Path 'when' exists on disk, but not in 'stash^2'.
89
90 $ git status --short
91 ?? when
92
93 */
94 unsigned int status;
95
96 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
97 cl_git_pass(git_status_file(&status, repo, "when"));
98
99 assert_blob_oid("refs/stash:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
100 assert_blob_oid("refs/stash:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
101 assert_blob_oid("refs/stash:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
102 assert_blob_oid("refs/stash:when", NULL);
103 assert_blob_oid("refs/stash:why", "88c2533e21f098b89c91a431d8075cbdbe422a51"); /* would anybody use stash? */
104 assert_blob_oid("refs/stash:where", "e3d6434ec12eb76af8dfa843a64ba6ab91014a0b"); /* .... */
105 assert_blob_oid("refs/stash:.gitignore", "ac4d88de61733173d9959e4b77c69b9f17a00980");
106 assert_blob_oid("refs/stash:just.ignore", NULL);
107
108 assert_blob_oid("refs/stash^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
109 assert_blob_oid("refs/stash^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
110 assert_blob_oid("refs/stash^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
111 assert_blob_oid("refs/stash^2:when", NULL);
112 assert_blob_oid("refs/stash^2:why", "88c2533e21f098b89c91a431d8075cbdbe422a51"); /* would anybody use stash? */
113 assert_blob_oid("refs/stash^2:where", "e08f7fbb9a42a0c5367cf8b349f1f08c3d56bd72"); /* ???? */
114 assert_blob_oid("refs/stash^2:.gitignore", "ac4d88de61733173d9959e4b77c69b9f17a00980");
115 assert_blob_oid("refs/stash^2:just.ignore", NULL);
116
117 assert_blob_oid("refs/stash^3", NULL);
118
119 cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
120 }
121
122 void test_stash_save__can_keep_index(void)
123 {
124 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_KEEP_INDEX));
125
126 assert_status(repo, "what", GIT_STATUS_INDEX_MODIFIED);
127 assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
128 assert_status(repo, "who", GIT_STATUS_CURRENT);
129 assert_status(repo, "when", GIT_STATUS_WT_NEW);
130 assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
131 }
132
133 static void assert_commit_message_contains(const char *revision, const char *fragment)
134 {
135 git_commit *commit;
136
137 cl_git_pass(git_revparse_single((git_object**)&commit, repo, revision));
138
139 cl_assert(strstr(git_commit_message(commit), fragment) != NULL);
140
141 git_commit_free(commit);
142 }
143
144 void test_stash_save__can_include_untracked_files(void)
145 {
146 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
147
148 assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
149
150 assert_blob_oid("refs/stash^3:what", NULL);
151 assert_blob_oid("refs/stash^3:how", NULL);
152 assert_blob_oid("refs/stash^3:who", NULL);
153 assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b");
154 assert_blob_oid("refs/stash^3:just.ignore", NULL);
155 }
156
157 void test_stash_save__untracked_skips_ignored(void)
158 {
159 cl_git_append2file("stash/.gitignore", "bundle/vendor/\n");
160 cl_must_pass(p_mkdir("stash/bundle", 0777));
161 cl_must_pass(p_mkdir("stash/bundle/vendor", 0777));
162 cl_git_mkfile("stash/bundle/vendor/blah", "contents\n");
163
164 cl_assert(git_fs_path_exists("stash/when")); /* untracked */
165 cl_assert(git_fs_path_exists("stash/just.ignore")); /* ignored */
166 cl_assert(git_fs_path_exists("stash/bundle/vendor/blah")); /* ignored */
167
168 cl_git_pass(git_stash_save(
169 &stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
170
171 cl_assert(!git_fs_path_exists("stash/when"));
172 cl_assert(git_fs_path_exists("stash/bundle/vendor/blah"));
173 cl_assert(git_fs_path_exists("stash/just.ignore"));
174 }
175
176 void test_stash_save__can_include_untracked_and_ignored_files(void)
177 {
178 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED));
179
180 assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
181
182 assert_blob_oid("refs/stash^3:what", NULL);
183 assert_blob_oid("refs/stash^3:how", NULL);
184 assert_blob_oid("refs/stash^3:who", NULL);
185 assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b");
186 assert_blob_oid("refs/stash^3:just.ignore", "78925fb1236b98b37a35e9723033e627f97aa88b");
187
188 cl_assert(!git_fs_path_exists("stash/just.ignore"));
189 }
190
191 /*
192 * Note: this test was flaky prior to fixing #4101 -- run it several
193 * times to get a failure. The issues is that whether the fast
194 * (stat-only) codepath is used inside stash's diff operation depends
195 * on whether files are "racily clean", and there doesn't seem to be
196 * an easy way to force the exact required state.
197 */
198 void test_stash_save__untracked_regression(void)
199 {
200 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
201 const char *paths[] = {"what", "where", "how", "why"};
202 git_reference *head;
203 git_commit *head_commit;
204 git_str untracked_dir;
205
206 const char* workdir = git_repository_workdir(repo);
207
208 git_str_init(&untracked_dir, 0);
209 git_str_printf(&untracked_dir, "%sz", workdir);
210
211 cl_assert(!p_mkdir(untracked_dir.ptr, 0777));
212
213 cl_git_pass(git_repository_head(&head, repo));
214
215 cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
216
217 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
218
219 opts.paths.strings = (char **)paths;
220 opts.paths.count = 4;
221
222 cl_git_pass(git_checkout_tree(repo, (git_object*)head_commit, &opts));
223
224 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
225
226 assert_commit_message_contains("refs/stash", "WIP on master");
227
228 git_reference_free(head);
229 git_commit_free(head_commit);
230 git_str_dispose(&untracked_dir);
231 }
232
233 #define MESSAGE "Look Ma! I'm on TV!"
234 void test_stash_save__can_accept_a_message(void)
235 {
236 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, MESSAGE, GIT_STASH_DEFAULT));
237
238 assert_commit_message_contains("refs/stash^2", "index on master: ");
239 assert_commit_message_contains("refs/stash", "On master: " MESSAGE);
240 }
241
242 void test_stash_save__cannot_stash_against_an_unborn_branch(void)
243 {
244 git_reference *head;
245
246 cl_git_pass(git_reference_symbolic_create(&head, repo, "HEAD", "refs/heads/unborn", 1, NULL));
247
248 cl_assert_equal_i(GIT_EUNBORNBRANCH,
249 git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
250
251 git_reference_free(head);
252 }
253
254 void test_stash_save__cannot_stash_against_a_bare_repository(void)
255 {
256 git_repository *local;
257
258 cl_git_pass(git_repository_init(&local, "sorry-it-is-a-non-bare-only-party", 1));
259
260 cl_assert_equal_i(GIT_EBAREREPO,
261 git_stash_save(&stash_tip_oid, local, signature, NULL, GIT_STASH_DEFAULT));
262
263 git_repository_free(local);
264 }
265
266 void test_stash_save__can_stash_against_a_detached_head(void)
267 {
268 git_repository_detach_head(repo);
269
270 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
271
272 assert_commit_message_contains("refs/stash^2", "index on (no branch): ");
273 assert_commit_message_contains("refs/stash", "WIP on (no branch): ");
274 }
275
276 void test_stash_save__stashing_updates_the_reflog(void)
277 {
278 assert_object_oid("refs/stash@{0}", NULL, GIT_OBJECT_COMMIT);
279
280 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
281
282 assert_object_oid("refs/stash@{0}", git_oid_tostr_s(&stash_tip_oid), GIT_OBJECT_COMMIT);
283 assert_object_oid("refs/stash@{1}", NULL, GIT_OBJECT_COMMIT);
284 }
285
286 void test_stash_save__multiline_message(void)
287 {
288 const char *msg = "This\n\nis a multiline message\n";
289 const git_reflog_entry *entry;
290 git_reflog *reflog;
291
292 assert_object_oid("refs/stash@{0}", NULL, GIT_OBJECT_COMMIT);
293
294 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, msg, GIT_STASH_DEFAULT));
295
296 cl_git_pass(git_reflog_read(&reflog, repo, "refs/stash"));
297 cl_assert(entry = git_reflog_entry_byindex(reflog, 0));
298 cl_assert_equal_s(git_reflog_entry_message(entry), "On master: This is a multiline message");
299
300 assert_object_oid("refs/stash@{0}", git_oid_tostr_s(&stash_tip_oid), GIT_OBJECT_COMMIT);
301 assert_commit_message_contains("refs/stash@{0}", msg);
302
303 git_reflog_free(reflog);
304 }
305
306 void test_stash_save__cannot_stash_when_there_are_no_local_change(void)
307 {
308 git_index *index;
309 git_oid stash_tip_oid;
310
311 cl_git_pass(git_repository_index(&index, repo));
312
313 /*
314 * 'what', 'where' and 'who' are being committed.
315 * 'when' remains untracked.
316 */
317 cl_git_pass(git_index_add_bypath(index, "what"));
318 cl_git_pass(git_index_add_bypath(index, "where"));
319 cl_git_pass(git_index_add_bypath(index, "who"));
320
321 cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
322 git_index_free(index);
323
324 cl_assert_equal_i(GIT_ENOTFOUND,
325 git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
326
327 p_unlink("stash/when");
328 cl_assert_equal_i(GIT_ENOTFOUND,
329 git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
330 }
331
332 void test_stash_save__can_stage_normal_then_stage_untracked(void)
333 {
334 /*
335 * $ git ls-tree stash@{1}^0
336 * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
337 * 100644 blob e6d64adb2c7f3eb8feb493b556cc8070dca379a3 how
338 * 100644 blob bc99dc98b3eba0e9157e94769cd4d49cb49de449 what
339 * 100644 blob a0400d4954659306a976567af43125a0b1aa8595 who
340 *
341 * $ git ls-tree stash@{1}^1
342 * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
343 * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
344 * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
345 * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
346 *
347 * $ git ls-tree stash@{1}^2
348 * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
349 * 100644 blob e6d64adb2c7f3eb8feb493b556cc8070dca379a3 how
350 * 100644 blob dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 what
351 * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
352 *
353 * $ git ls-tree stash@{1}^3
354 * fatal: Not a valid object name stash@{1}^3
355 *
356 * $ git ls-tree stash@{0}^0
357 * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
358 * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
359 * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
360 * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
361 *
362 * $ git ls-tree stash@{0}^1
363 * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
364 * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
365 * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
366 * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
367 *
368 * $ git ls-tree stash@{0}^2
369 * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
370 * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
371 * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
372 * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
373 *
374 * $ git ls-tree stash@{0}^3
375 * 100644 blob b6ed15e81e2593d7bb6265eb4a991d29dc3e628b when
376 */
377
378 assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
379 assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
380 assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
381 assert_status(repo, "when", GIT_STATUS_WT_NEW);
382 assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
383
384 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
385 assert_status(repo, "what", GIT_STATUS_CURRENT);
386 assert_status(repo, "how", GIT_STATUS_CURRENT);
387 assert_status(repo, "who", GIT_STATUS_CURRENT);
388 assert_status(repo, "when", GIT_STATUS_WT_NEW);
389 assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
390
391 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
392 assert_status(repo, "what", GIT_STATUS_CURRENT);
393 assert_status(repo, "how", GIT_STATUS_CURRENT);
394 assert_status(repo, "who", GIT_STATUS_CURRENT);
395 assert_status(repo, "when", GIT_ENOTFOUND);
396 assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
397
398
399 assert_blob_oid("stash@{1}^0:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
400 assert_blob_oid("stash@{1}^0:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
401 assert_blob_oid("stash@{1}^0:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
402 assert_blob_oid("stash@{1}^0:when", NULL);
403
404 assert_blob_oid("stash@{1}^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
405 assert_blob_oid("stash@{1}^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
406 assert_blob_oid("stash@{1}^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
407 assert_blob_oid("stash@{1}^2:when", NULL);
408
409 assert_object_oid("stash@{1}^3", NULL, GIT_OBJECT_COMMIT);
410
411 assert_blob_oid("stash@{0}^0:what", "ce013625030ba8dba906f756967f9e9ca394464a"); /* hello */
412 assert_blob_oid("stash@{0}^0:how", "ac790413e2d7a26c3767e78c57bb28716686eebc"); /* small */
413 assert_blob_oid("stash@{0}^0:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
414 assert_blob_oid("stash@{0}^0:when", NULL);
415
416 assert_blob_oid("stash@{0}^2:what", "ce013625030ba8dba906f756967f9e9ca394464a"); /* hello */
417 assert_blob_oid("stash@{0}^2:how", "ac790413e2d7a26c3767e78c57bb28716686eebc"); /* small */
418 assert_blob_oid("stash@{0}^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
419 assert_blob_oid("stash@{0}^2:when", NULL);
420
421 assert_blob_oid("stash@{0}^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b"); /* now */
422 }
423
424 #define EMPTY_TREE "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
425
426 void test_stash_save__including_untracked_without_any_untracked_file_creates_an_empty_tree(void)
427 {
428 cl_must_pass(p_unlink("stash/when"));
429
430 assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
431 assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
432 assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
433 assert_status(repo, "when", GIT_ENOTFOUND);
434 assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
435
436 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
437
438 assert_object_oid("stash^3^{tree}", EMPTY_TREE, GIT_OBJECT_TREE);
439 }
440
441 void test_stash_save__ignored_directory(void)
442 {
443 cl_git_pass(p_mkdir("stash/ignored_directory", 0777));
444 cl_git_pass(p_mkdir("stash/ignored_directory/sub", 0777));
445 cl_git_mkfile("stash/ignored_directory/sub/some_file", "stuff");
446
447 assert_status(repo, "ignored_directory/sub/some_file", GIT_STATUS_WT_NEW);
448 cl_git_pass(git_ignore_add_rule(repo, "ignored_directory/"));
449 assert_status(repo, "ignored_directory/sub/some_file", GIT_STATUS_IGNORED);
450
451 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED));
452
453 cl_assert(!git_fs_path_exists("stash/ignored_directory/sub/some_file"));
454 cl_assert(!git_fs_path_exists("stash/ignored_directory/sub"));
455 cl_assert(!git_fs_path_exists("stash/ignored_directory"));
456 }
457
458 void test_stash_save__skip_submodules(void)
459 {
460 git_repository *untracked_repo;
461 cl_git_pass(git_repository_init(&untracked_repo, "stash/untracked_repo", false));
462 cl_git_mkfile("stash/untracked_repo/content", "stuff");
463 git_repository_free(untracked_repo);
464
465 assert_status(repo, "untracked_repo/", GIT_STATUS_WT_NEW);
466
467 cl_git_pass(git_stash_save(
468 &stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
469
470 assert_status(repo, "untracked_repo/", GIT_STATUS_WT_NEW);
471 }
472
473 void test_stash_save__deleted_in_index_modified_in_workdir(void)
474 {
475 git_index *index;
476
477 git_repository_index(&index, repo);
478
479 cl_git_pass(git_index_remove_bypath(index, "who"));
480 cl_git_pass(git_index_write(index));
481
482 assert_status(repo, "who", GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED);
483
484 cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
485
486 assert_blob_oid("stash@{0}^0:who", "a0400d4954659306a976567af43125a0b1aa8595");
487 assert_blob_oid("stash@{0}^2:who", NULL);
488
489 git_index_free(index);
490 }