1 #include "clar_libgit2.h"
3 #include "stash_helpers.h"
5 static git_repository
*repo
;
6 static git_signature
*signature
;
7 static git_oid stash_tip_oid
;
10 * Friendly reminder, in order to ease the reading of the following tests:
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
18 void test_stash_save__initialize(void)
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 */
23 setup_stash(repo
, signature
);
26 void test_stash_save__cleanup(void)
28 git_signature_free(signature
);
31 git_repository_free(repo
);
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");
38 static void assert_object_oid(const char* revision
, const char* expected_oid
, git_object_t type
)
43 result
= git_revparse_single(&obj
, repo
, revision
);
46 cl_assert_equal_i(GIT_ENOTFOUND
, result
);
49 cl_assert_equal_i(0, result
);
51 cl_git_pass(git_oid_streq(git_object_id(obj
), expected_oid
));
52 cl_assert_equal_i(type
, git_object_type(obj
));
56 static void assert_blob_oid(const char* revision
, const char* expected_oid
)
58 assert_object_oid(revision
, expected_oid
, GIT_OBJECT_BLOB
);
61 void test_stash_save__does_not_keep_index_by_default(void)
66 $ git show refs/stash:what
69 $ git show refs/stash:how
72 $ git show refs/stash:who
75 $ git show refs/stash:when
76 fatal: Path 'when' exists on disk, but not in 'stash'.
78 $ git show refs/stash^2:what
81 $ git show refs/stash^2:how
84 $ git show refs/stash^2:who
87 $ git show refs/stash^2:when
88 fatal: Path 'when' exists on disk, but not in 'stash^2'.
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"));
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
);
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
);
117 assert_blob_oid("refs/stash^3", NULL
);
119 cl_assert_equal_i(GIT_STATUS_WT_NEW
, status
);
122 void test_stash_save__can_keep_index(void)
124 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_KEEP_INDEX
));
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
);
133 static void assert_commit_message_contains(const char *revision
, const char *fragment
)
137 cl_git_pass(git_revparse_single((git_object
**)&commit
, repo
, revision
));
139 cl_assert(strstr(git_commit_message(commit
), fragment
) != NULL
);
141 git_commit_free(commit
);
144 void test_stash_save__can_include_untracked_files(void)
146 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_INCLUDE_UNTRACKED
));
148 assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
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
);
157 void test_stash_save__untracked_skips_ignored(void)
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");
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 */
168 cl_git_pass(git_stash_save(
169 &stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_INCLUDE_UNTRACKED
));
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"));
176 void test_stash_save__can_include_untracked_and_ignored_files(void)
178 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_INCLUDE_UNTRACKED
| GIT_STASH_INCLUDE_IGNORED
));
180 assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
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");
188 cl_assert(!git_fs_path_exists("stash/just.ignore"));
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.
198 void test_stash_save__untracked_regression(void)
200 git_checkout_options opts
= GIT_CHECKOUT_OPTIONS_INIT
;
201 const char *paths
[] = {"what", "where", "how", "why"};
203 git_commit
*head_commit
;
204 git_str untracked_dir
;
206 const char* workdir
= git_repository_workdir(repo
);
208 git_str_init(&untracked_dir
, 0);
209 git_str_printf(&untracked_dir
, "%sz", workdir
);
211 cl_assert(!p_mkdir(untracked_dir
.ptr
, 0777));
213 cl_git_pass(git_repository_head(&head
, repo
));
215 cl_git_pass(git_reference_peel((git_object
**)&head_commit
, head
, GIT_OBJECT_COMMIT
));
217 opts
.checkout_strategy
= GIT_CHECKOUT_FORCE
;
219 opts
.paths
.strings
= (char **)paths
;
220 opts
.paths
.count
= 4;
222 cl_git_pass(git_checkout_tree(repo
, (git_object
*)head_commit
, &opts
));
224 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_DEFAULT
));
226 assert_commit_message_contains("refs/stash", "WIP on master");
228 git_reference_free(head
);
229 git_commit_free(head_commit
);
230 git_str_dispose(&untracked_dir
);
233 #define MESSAGE "Look Ma! I'm on TV!"
234 void test_stash_save__can_accept_a_message(void)
236 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, MESSAGE
, GIT_STASH_DEFAULT
));
238 assert_commit_message_contains("refs/stash^2", "index on master: ");
239 assert_commit_message_contains("refs/stash", "On master: " MESSAGE
);
242 void test_stash_save__cannot_stash_against_an_unborn_branch(void)
246 cl_git_pass(git_reference_symbolic_create(&head
, repo
, "HEAD", "refs/heads/unborn", 1, NULL
));
248 cl_assert_equal_i(GIT_EUNBORNBRANCH
,
249 git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_DEFAULT
));
251 git_reference_free(head
);
254 void test_stash_save__cannot_stash_against_a_bare_repository(void)
256 git_repository
*local
;
258 cl_git_pass(git_repository_init(&local
, "sorry-it-is-a-non-bare-only-party", 1));
260 cl_assert_equal_i(GIT_EBAREREPO
,
261 git_stash_save(&stash_tip_oid
, local
, signature
, NULL
, GIT_STASH_DEFAULT
));
263 git_repository_free(local
);
266 void test_stash_save__can_stash_against_a_detached_head(void)
268 git_repository_detach_head(repo
);
270 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_DEFAULT
));
272 assert_commit_message_contains("refs/stash^2", "index on (no branch): ");
273 assert_commit_message_contains("refs/stash", "WIP on (no branch): ");
276 void test_stash_save__stashing_updates_the_reflog(void)
278 assert_object_oid("refs/stash@{0}", NULL
, GIT_OBJECT_COMMIT
);
280 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_DEFAULT
));
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
);
286 void test_stash_save__multiline_message(void)
288 const char *msg
= "This\n\nis a multiline message\n";
289 const git_reflog_entry
*entry
;
292 assert_object_oid("refs/stash@{0}", NULL
, GIT_OBJECT_COMMIT
);
294 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, msg
, GIT_STASH_DEFAULT
));
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");
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
);
303 git_reflog_free(reflog
);
306 void test_stash_save__cannot_stash_when_there_are_no_local_change(void)
309 git_oid stash_tip_oid
;
311 cl_git_pass(git_repository_index(&index
, repo
));
314 * 'what', 'where' and 'who' are being committed.
315 * 'when' remains untracked.
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"));
321 cl_repo_commit_from_index(NULL
, repo
, signature
, 0, "Initial commit");
322 git_index_free(index
);
324 cl_assert_equal_i(GIT_ENOTFOUND
,
325 git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_DEFAULT
));
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
));
332 void test_stash_save__can_stage_normal_then_stage_untracked(void)
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
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
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
353 * $ git ls-tree stash@{1}^3
354 * fatal: Not a valid object name stash@{1}^3
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
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
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
374 * $ git ls-tree stash@{0}^3
375 * 100644 blob b6ed15e81e2593d7bb6265eb4a991d29dc3e628b when
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
);
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
);
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
);
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
);
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
);
409 assert_object_oid("stash@{1}^3", NULL
, GIT_OBJECT_COMMIT
);
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
);
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
);
421 assert_blob_oid("stash@{0}^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b"); /* now */
424 #define EMPTY_TREE "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
426 void test_stash_save__including_untracked_without_any_untracked_file_creates_an_empty_tree(void)
428 cl_must_pass(p_unlink("stash/when"));
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
);
436 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_INCLUDE_UNTRACKED
));
438 assert_object_oid("stash^3^{tree}", EMPTY_TREE
, GIT_OBJECT_TREE
);
441 void test_stash_save__ignored_directory(void)
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");
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
);
451 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_INCLUDE_UNTRACKED
| GIT_STASH_INCLUDE_IGNORED
));
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"));
458 void test_stash_save__skip_submodules(void)
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
);
465 assert_status(repo
, "untracked_repo/", GIT_STATUS_WT_NEW
);
467 cl_git_pass(git_stash_save(
468 &stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_INCLUDE_UNTRACKED
));
470 assert_status(repo
, "untracked_repo/", GIT_STATUS_WT_NEW
);
473 void test_stash_save__deleted_in_index_modified_in_workdir(void)
477 git_repository_index(&index
, repo
);
479 cl_git_pass(git_index_remove_bypath(index
, "who"));
480 cl_git_pass(git_index_write(index
));
482 assert_status(repo
, "who", GIT_STATUS_WT_NEW
| GIT_STATUS_INDEX_DELETED
);
484 cl_git_pass(git_stash_save(&stash_tip_oid
, repo
, signature
, NULL
, GIT_STASH_DEFAULT
));
486 assert_blob_oid("stash@{0}^0:who", "a0400d4954659306a976567af43125a0b1aa8595");
487 assert_blob_oid("stash@{0}^2:who", NULL
);
489 git_index_free(index
);