]>
Commit | Line | Data |
---|---|---|
3fd1520c | 1 | #include "clar_libgit2.h" |
11385c3c | 2 | #include "fileops.h" |
6a67a812 | 3 | #include "ignore.h" |
11385c3c | 4 | #include "status_data.h" |
95340398 | 5 | #include "posix.h" |
a56aacf4 RB |
6 | #include "util.h" |
7 | #include "path.h" | |
b23b112d | 8 | #include "../diff/diff_helpers.h" |
e44abe16 | 9 | #include "../checkout/checkout_helpers.h" |
9c8ed499 | 10 | #include "git2/sys/diff.h" |
11385c3c | 11 | |
11385c3c VM |
12 | /** |
13 | * Cleanup | |
14 | * | |
15 | * This will be called once after each test finishes, even | |
16 | * if the test failed | |
17 | */ | |
df297a1f | 18 | void test_status_worktree__cleanup(void) |
11385c3c | 19 | { |
854eccbb | 20 | cl_git_sandbox_cleanup(); |
11385c3c VM |
21 | } |
22 | ||
23 | /** | |
24 | * Tests - Status determination on a working tree | |
25 | */ | |
a56aacf4 | 26 | /* this test is equivalent to t18-status.c:statuscb0 */ |
a5f8c1bd | 27 | void test_status_worktree__whole_repository(void) |
11385c3c | 28 | { |
dc13f1f7 | 29 | status_entry_counts counts; |
854eccbb | 30 | git_repository *repo = cl_git_sandbox_init("status"); |
11385c3c | 31 | |
dc13f1f7 | 32 | memset(&counts, 0x0, sizeof(status_entry_counts)); |
11385c3c VM |
33 | counts.expected_entry_count = entry_count0; |
34 | counts.expected_paths = entry_paths0; | |
35 | counts.expected_statuses = entry_statuses0; | |
36 | ||
2014021b | 37 | cl_git_pass( |
854eccbb | 38 | git_status_foreach(repo, cb_status__normal, &counts) |
2014021b | 39 | ); |
11385c3c | 40 | |
bd4ca902 RB |
41 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); |
42 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
43 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
11385c3c VM |
44 | } |
45 | ||
cd424ad5 RB |
46 | void assert_show( |
47 | const int entry_counts, | |
48 | const char *entry_paths[], | |
49 | const unsigned int entry_statuses[], | |
50 | git_repository *repo, | |
51 | git_status_show_t show, | |
52 | unsigned int extra_flags) | |
9b6075b2 | 53 | { |
54 | status_entry_counts counts; | |
9b6075b2 | 55 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; |
56 | ||
57 | memset(&counts, 0x0, sizeof(status_entry_counts)); | |
58 | counts.expected_entry_count = entry_counts; | |
59 | counts.expected_paths = entry_paths; | |
60 | counts.expected_statuses = entry_statuses; | |
61 | ||
cd424ad5 | 62 | opts.flags = GIT_STATUS_OPT_DEFAULTS | extra_flags; |
9b6075b2 | 63 | opts.show = show; |
64 | ||
65 | cl_git_pass( | |
66 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) | |
67 | ); | |
68 | ||
69 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); | |
70 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
71 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
72 | } | |
73 | ||
74 | void test_status_worktree__show_index_and_workdir(void) | |
75 | { | |
76 | assert_show(entry_count0, entry_paths0, entry_statuses0, | |
cd424ad5 | 77 | cl_git_sandbox_init("status"), GIT_STATUS_SHOW_INDEX_AND_WORKDIR, 0); |
9b6075b2 | 78 | } |
79 | ||
80 | void test_status_worktree__show_index_only(void) | |
81 | { | |
82 | assert_show(entry_count5, entry_paths5, entry_statuses5, | |
cd424ad5 | 83 | cl_git_sandbox_init("status"), GIT_STATUS_SHOW_INDEX_ONLY, 0); |
9b6075b2 | 84 | } |
85 | ||
86 | void test_status_worktree__show_workdir_only(void) | |
87 | { | |
88 | assert_show(entry_count6, entry_paths6, entry_statuses6, | |
cd424ad5 | 89 | cl_git_sandbox_init("status"), GIT_STATUS_SHOW_WORKDIR_ONLY, 0); |
9b6075b2 | 90 | } |
91 | ||
a56aacf4 | 92 | /* this test is equivalent to t18-status.c:statuscb1 */ |
a5f8c1bd | 93 | void test_status_worktree__empty_repository(void) |
11385c3c VM |
94 | { |
95 | int count = 0; | |
854eccbb RB |
96 | git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); |
97 | ||
98 | cl_git_pass(git_status_foreach(repo, cb_status__count, &count)); | |
11385c3c | 99 | |
bd4ca902 | 100 | cl_assert_equal_i(0, count); |
11385c3c | 101 | } |
df743c7d | 102 | |
a56aacf4 RB |
103 | static int remove_file_cb(void *data, git_buf *file) |
104 | { | |
105 | const char *filename = git_buf_cstr(file); | |
106 | ||
107 | GIT_UNUSED(data); | |
108 | ||
109 | if (git__suffixcmp(filename, ".git") == 0) | |
110 | return 0; | |
111 | ||
112 | if (git_path_isdir(filename)) | |
331e7de9 | 113 | cl_git_pass(git_futils_rmdir_r(filename, NULL, GIT_RMDIR_REMOVE_FILES)); |
a56aacf4 RB |
114 | else |
115 | cl_git_pass(p_unlink(git_buf_cstr(file))); | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
120 | /* this test is equivalent to t18-status.c:statuscb2 */ | |
121 | void test_status_worktree__purged_worktree(void) | |
122 | { | |
dc13f1f7 | 123 | status_entry_counts counts; |
a56aacf4 RB |
124 | git_repository *repo = cl_git_sandbox_init("status"); |
125 | git_buf workdir = GIT_BUF_INIT; | |
126 | ||
127 | /* first purge the contents of the worktree */ | |
128 | cl_git_pass(git_buf_sets(&workdir, git_repository_workdir(repo))); | |
219d3457 | 129 | cl_git_pass(git_path_direach(&workdir, 0, remove_file_cb, NULL)); |
ac3d33df | 130 | git_buf_dispose(&workdir); |
a56aacf4 RB |
131 | |
132 | /* now get status */ | |
dc13f1f7 | 133 | memset(&counts, 0x0, sizeof(status_entry_counts)); |
a56aacf4 RB |
134 | counts.expected_entry_count = entry_count2; |
135 | counts.expected_paths = entry_paths2; | |
136 | counts.expected_statuses = entry_statuses2; | |
137 | ||
138 | cl_git_pass( | |
139 | git_status_foreach(repo, cb_status__normal, &counts) | |
140 | ); | |
141 | ||
bd4ca902 RB |
142 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); |
143 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
144 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
a56aacf4 RB |
145 | } |
146 | ||
c8838ee9 | 147 | /* this test is similar to t18-status.c:statuscb3 */ |
a56aacf4 RB |
148 | void test_status_worktree__swap_subdir_and_file(void) |
149 | { | |
dc13f1f7 | 150 | status_entry_counts counts; |
a56aacf4 | 151 | git_repository *repo = cl_git_sandbox_init("status"); |
ec40b7f9 | 152 | git_index *index; |
79cfa20d | 153 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; |
ec40b7f9 PK |
154 | bool ignore_case; |
155 | ||
156 | cl_git_pass(git_repository_index(&index, repo)); | |
ac3d33df | 157 | ignore_case = (git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0; |
ec40b7f9 | 158 | git_index_free(index); |
a56aacf4 RB |
159 | |
160 | /* first alter the contents of the worktree */ | |
161 | cl_git_pass(p_rename("status/current_file", "status/swap")); | |
162 | cl_git_pass(p_rename("status/subdir", "status/current_file")); | |
163 | cl_git_pass(p_rename("status/swap", "status/subdir")); | |
164 | ||
165 | cl_git_mkfile("status/.HEADER", "dummy"); | |
166 | cl_git_mkfile("status/42-is-not-prime.sigh", "dummy"); | |
167 | cl_git_mkfile("status/README.md", "dummy"); | |
168 | ||
169 | /* now get status */ | |
dc13f1f7 | 170 | memset(&counts, 0x0, sizeof(status_entry_counts)); |
a56aacf4 | 171 | counts.expected_entry_count = entry_count3; |
ec40b7f9 PK |
172 | counts.expected_paths = ignore_case ? entry_paths3_icase : entry_paths3; |
173 | counts.expected_statuses = ignore_case ? entry_statuses3_icase : entry_statuses3; | |
a56aacf4 | 174 | |
c8838ee9 RB |
175 | opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | |
176 | GIT_STATUS_OPT_INCLUDE_IGNORED; | |
177 | ||
a56aacf4 | 178 | cl_git_pass( |
c8838ee9 | 179 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) |
a56aacf4 RB |
180 | ); |
181 | ||
bd4ca902 RB |
182 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); |
183 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
184 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
a56aacf4 RB |
185 | } |
186 | ||
4b136a94 RB |
187 | void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void) |
188 | { | |
dc13f1f7 | 189 | status_entry_counts counts; |
4b136a94 | 190 | git_repository *repo = cl_git_sandbox_init("status"); |
79cfa20d | 191 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; |
4b136a94 RB |
192 | |
193 | /* first alter the contents of the worktree */ | |
194 | cl_git_pass(p_rename("status/current_file", "status/swap")); | |
195 | cl_git_pass(p_rename("status/subdir", "status/current_file")); | |
196 | cl_git_pass(p_rename("status/swap", "status/subdir")); | |
197 | cl_git_mkfile("status/.new_file", "dummy"); | |
ac2fba0e | 198 | cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", 0777)); |
4b136a94 RB |
199 | cl_git_mkfile("status/zzz_new_dir/new_file", "dummy"); |
200 | cl_git_mkfile("status/zzz_new_file", "dummy"); | |
201 | ||
202 | /* now get status */ | |
dc13f1f7 | 203 | memset(&counts, 0x0, sizeof(status_entry_counts)); |
4b136a94 RB |
204 | counts.expected_entry_count = entry_count4; |
205 | counts.expected_paths = entry_paths4; | |
206 | counts.expected_statuses = entry_statuses4; | |
207 | ||
4b136a94 RB |
208 | opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | |
209 | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; | |
210 | /* TODO: set pathspec to "current_file" eventually */ | |
211 | ||
212 | cl_git_pass( | |
213 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) | |
214 | ); | |
215 | ||
bd4ca902 RB |
216 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); |
217 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
218 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
4b136a94 RB |
219 | } |
220 | ||
b6204260 | 221 | static void stage_and_commit(git_repository *repo, const char *path) |
222 | { | |
223 | git_index *index; | |
224 | ||
225 | cl_git_pass(git_repository_index(&index, repo)); | |
226 | cl_git_pass(git_index_add_bypath(index, path)); | |
227 | cl_repo_commit_from_index(NULL, repo, NULL, 1323847743, "Initial commit\n"); | |
228 | git_index_free(index); | |
229 | } | |
230 | ||
231 | void test_status_worktree__within_subdir(void) | |
232 | { | |
233 | status_entry_counts counts; | |
234 | git_repository *repo = cl_git_sandbox_init("status"); | |
235 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
236 | char *paths[] = { "zzz_new_dir" }; | |
237 | git_strarray pathsArray; | |
238 | ||
239 | /* first alter the contents of the worktree */ | |
240 | cl_git_mkfile("status/.new_file", "dummy"); | |
241 | cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", 0777)); | |
242 | cl_git_mkfile("status/zzz_new_dir/new_file", "dummy"); | |
243 | cl_git_mkfile("status/zzz_new_file", "dummy"); | |
244 | cl_git_mkfile("status/wut", "dummy"); | |
245 | ||
246 | stage_and_commit(repo, "zzz_new_dir/new_file"); | |
247 | ||
248 | /* now get status */ | |
249 | memset(&counts, 0x0, sizeof(status_entry_counts)); | |
250 | counts.expected_entry_count = entry_count4; | |
251 | counts.expected_paths = entry_paths4; | |
252 | counts.expected_statuses = entry_statuses4; | |
253 | counts.debug = true; | |
254 | ||
255 | opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | | |
256 | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS | | |
257 | GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH; | |
258 | ||
259 | pathsArray.count = 1; | |
260 | pathsArray.strings = paths; | |
261 | opts.pathspec = pathsArray; | |
262 | ||
ac3d33df | 263 | /* We committed zzz_new_dir/new_file above. It shouldn't be reported. */ |
b6204260 | 264 | cl_git_pass( |
265 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) | |
266 | ); | |
267 | ||
268 | cl_assert_equal_i(0, counts.entry_count); | |
269 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
270 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
271 | } | |
272 | ||
a56aacf4 | 273 | /* this test is equivalent to t18-status.c:singlestatus0 */ |
df743c7d RB |
274 | void test_status_worktree__single_file(void) |
275 | { | |
276 | int i; | |
277 | unsigned int status_flags; | |
854eccbb | 278 | git_repository *repo = cl_git_sandbox_init("status"); |
df743c7d RB |
279 | |
280 | for (i = 0; i < (int)entry_count0; i++) { | |
281 | cl_git_pass( | |
854eccbb | 282 | git_status_file(&status_flags, repo, entry_paths0[i]) |
df743c7d RB |
283 | ); |
284 | cl_assert(entry_statuses0[i] == status_flags); | |
285 | } | |
286 | } | |
6a67a812 | 287 | |
a56aacf4 RB |
288 | /* this test is equivalent to t18-status.c:singlestatus1 */ |
289 | void test_status_worktree__single_nonexistent_file(void) | |
290 | { | |
291 | int error; | |
292 | unsigned int status_flags; | |
293 | git_repository *repo = cl_git_sandbox_init("status"); | |
294 | ||
295 | error = git_status_file(&status_flags, repo, "nonexistent"); | |
296 | cl_git_fail(error); | |
297 | cl_assert(error == GIT_ENOTFOUND); | |
298 | } | |
299 | ||
98c4613e RB |
300 | /* this test is equivalent to t18-status.c:singlestatus2 */ |
301 | void test_status_worktree__single_nonexistent_file_empty_repo(void) | |
302 | { | |
303 | int error; | |
304 | unsigned int status_flags; | |
305 | git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); | |
306 | ||
307 | error = git_status_file(&status_flags, repo, "nonexistent"); | |
308 | cl_git_fail(error); | |
309 | cl_assert(error == GIT_ENOTFOUND); | |
310 | } | |
311 | ||
312 | /* this test is equivalent to t18-status.c:singlestatus3 */ | |
313 | void test_status_worktree__single_file_empty_repo(void) | |
314 | { | |
315 | unsigned int status_flags; | |
316 | git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); | |
317 | ||
318 | cl_git_mkfile("empty_standard_repo/new_file", "new_file\n"); | |
319 | ||
320 | cl_git_pass(git_status_file(&status_flags, repo, "new_file")); | |
321 | cl_assert(status_flags == GIT_STATUS_WT_NEW); | |
322 | } | |
323 | ||
324 | /* this test is equivalent to t18-status.c:singlestatus4 */ | |
325 | void test_status_worktree__single_folder(void) | |
326 | { | |
327 | int error; | |
328 | unsigned int status_flags; | |
329 | git_repository *repo = cl_git_sandbox_init("status"); | |
330 | ||
331 | error = git_status_file(&status_flags, repo, "subdir"); | |
332 | cl_git_fail(error); | |
722c08af | 333 | cl_assert(error != GIT_ENOTFOUND); |
98c4613e RB |
334 | } |
335 | ||
a56aacf4 | 336 | |
6a67a812 RB |
337 | void test_status_worktree__ignores(void) |
338 | { | |
339 | int i, ignored; | |
854eccbb | 340 | git_repository *repo = cl_git_sandbox_init("status"); |
6a67a812 RB |
341 | |
342 | for (i = 0; i < (int)entry_count0; i++) { | |
854eccbb | 343 | cl_git_pass( |
dc13f1f7 | 344 | git_status_should_ignore(&ignored, repo, entry_paths0[i]) |
854eccbb | 345 | ); |
e1bcc191 | 346 | cl_assert(ignored == (entry_statuses0[i] == GIT_STATUS_IGNORED)); |
6a67a812 RB |
347 | } |
348 | ||
854eccbb | 349 | cl_git_pass( |
dc13f1f7 | 350 | git_status_should_ignore(&ignored, repo, "nonexistent_file") |
854eccbb | 351 | ); |
6a67a812 RB |
352 | cl_assert(!ignored); |
353 | ||
854eccbb | 354 | cl_git_pass( |
dc13f1f7 | 355 | git_status_should_ignore(&ignored, repo, "ignored_nonexistent_file") |
854eccbb | 356 | ); |
6a67a812 RB |
357 | cl_assert(ignored); |
358 | } | |
95340398 RB |
359 | |
360 | static int cb_status__check_592(const char *p, unsigned int s, void *payload) | |
361 | { | |
e26b14c0 RB |
362 | if (s != GIT_STATUS_WT_DELETED || |
363 | (payload != NULL && strcmp(p, (const char *)payload) != 0)) | |
95340398 RB |
364 | return -1; |
365 | ||
366 | return 0; | |
367 | } | |
368 | ||
369 | void test_status_worktree__issue_592(void) | |
370 | { | |
371 | git_repository *repo; | |
372 | git_buf path = GIT_BUF_INIT; | |
373 | ||
374 | repo = cl_git_sandbox_init("issue_592"); | |
375 | cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "l.txt")); | |
376 | cl_git_pass(p_unlink(git_buf_cstr(&path))); | |
169dc616 | 377 | cl_assert(!git_path_exists("issue_592/l.txt")); |
95340398 RB |
378 | |
379 | cl_git_pass(git_status_foreach(repo, cb_status__check_592, "l.txt")); | |
380 | ||
ac3d33df | 381 | git_buf_dispose(&path); |
95340398 RB |
382 | } |
383 | ||
384 | void test_status_worktree__issue_592_2(void) | |
385 | { | |
386 | git_repository *repo; | |
387 | git_buf path = GIT_BUF_INIT; | |
388 | ||
389 | repo = cl_git_sandbox_init("issue_592"); | |
390 | cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c/a.txt")); | |
391 | cl_git_pass(p_unlink(git_buf_cstr(&path))); | |
169dc616 | 392 | cl_assert(!git_path_exists("issue_592/c/a.txt")); |
95340398 RB |
393 | |
394 | cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt")); | |
395 | ||
ac3d33df | 396 | git_buf_dispose(&path); |
95340398 RB |
397 | } |
398 | ||
399 | void test_status_worktree__issue_592_3(void) | |
400 | { | |
401 | git_repository *repo; | |
402 | git_buf path = GIT_BUF_INIT; | |
403 | ||
404 | repo = cl_git_sandbox_init("issue_592"); | |
405 | ||
406 | cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c")); | |
331e7de9 | 407 | cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES)); |
169dc616 | 408 | cl_assert(!git_path_exists("issue_592/c/a.txt")); |
95340398 RB |
409 | |
410 | cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt")); | |
411 | ||
ac3d33df | 412 | git_buf_dispose(&path); |
95340398 RB |
413 | } |
414 | ||
415 | void test_status_worktree__issue_592_4(void) | |
416 | { | |
417 | git_repository *repo; | |
418 | git_buf path = GIT_BUF_INIT; | |
419 | ||
420 | repo = cl_git_sandbox_init("issue_592"); | |
421 | ||
422 | cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t/b.txt")); | |
423 | cl_git_pass(p_unlink(git_buf_cstr(&path))); | |
424 | ||
425 | cl_git_pass(git_status_foreach(repo, cb_status__check_592, "t/b.txt")); | |
426 | ||
ac3d33df | 427 | git_buf_dispose(&path); |
95340398 RB |
428 | } |
429 | ||
430 | void test_status_worktree__issue_592_5(void) | |
431 | { | |
432 | git_repository *repo; | |
433 | git_buf path = GIT_BUF_INIT; | |
434 | ||
435 | repo = cl_git_sandbox_init("issue_592"); | |
436 | ||
437 | cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t")); | |
331e7de9 | 438 | cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES)); |
95340398 RB |
439 | cl_git_pass(p_mkdir(git_buf_cstr(&path), 0777)); |
440 | ||
441 | cl_git_pass(git_status_foreach(repo, cb_status__check_592, NULL)); | |
442 | ||
ac3d33df | 443 | git_buf_dispose(&path); |
95340398 | 444 | } |
722c08af | 445 | |
bd4ca902 RB |
446 | void test_status_worktree__issue_592_ignores_0(void) |
447 | { | |
448 | int count = 0; | |
449 | status_entry_single st; | |
450 | git_repository *repo = cl_git_sandbox_init("issue_592"); | |
451 | ||
452 | cl_git_pass(git_status_foreach(repo, cb_status__count, &count)); | |
453 | cl_assert_equal_i(0, count); | |
454 | ||
455 | cl_git_rewritefile("issue_592/.gitignore", | |
456 | ".gitignore\n*.txt\nc/\n[tT]*/\n"); | |
457 | ||
458 | cl_git_pass(git_status_foreach(repo, cb_status__count, &count)); | |
459 | cl_assert_equal_i(1, count); | |
460 | ||
461 | /* This is a situation where the behavior of libgit2 is | |
462 | * different from core git. Core git will show ignored.txt | |
463 | * in the list of ignored files, even though the directory | |
464 | * "t" is ignored and the file is untracked because we have | |
465 | * the explicit "*.txt" ignore rule. Libgit2 just excludes | |
466 | * all untracked files that are contained within ignored | |
467 | * directories without explicitly listing them. | |
468 | */ | |
469 | cl_git_rewritefile("issue_592/t/ignored.txt", "ping"); | |
470 | ||
471 | memset(&st, 0, sizeof(st)); | |
472 | cl_git_pass(git_status_foreach(repo, cb_status__single, &st)); | |
473 | cl_assert_equal_i(1, st.count); | |
474 | cl_assert(st.status == GIT_STATUS_IGNORED); | |
475 | ||
476 | cl_git_rewritefile("issue_592/c/ignored_by_dir", "ping"); | |
477 | ||
478 | memset(&st, 0, sizeof(st)); | |
479 | cl_git_pass(git_status_foreach(repo, cb_status__single, &st)); | |
480 | cl_assert_equal_i(1, st.count); | |
481 | cl_assert(st.status == GIT_STATUS_IGNORED); | |
482 | ||
483 | cl_git_rewritefile("issue_592/t/ignored_by_dir_pattern", "ping"); | |
484 | ||
485 | memset(&st, 0, sizeof(st)); | |
486 | cl_git_pass(git_status_foreach(repo, cb_status__single, &st)); | |
487 | cl_assert_equal_i(1, st.count); | |
488 | cl_assert(st.status == GIT_STATUS_IGNORED); | |
489 | } | |
490 | ||
491 | void test_status_worktree__issue_592_ignored_dirs_with_tracked_content(void) | |
492 | { | |
493 | int count = 0; | |
494 | git_repository *repo = cl_git_sandbox_init("issue_592b"); | |
495 | ||
496 | cl_git_pass(git_status_foreach(repo, cb_status__count, &count)); | |
497 | cl_assert_equal_i(1, count); | |
498 | ||
499 | /* if we are really mimicking core git, then only ignored1.txt | |
500 | * at the top level will show up in the ignores list here. | |
501 | * everything else will be unmodified or skipped completely. | |
502 | */ | |
503 | } | |
504 | ||
54254a0f | 505 | void test_status_worktree__conflict_with_diff3(void) |
151446ca | 506 | { |
54254a0f | 507 | git_repository *repo = cl_git_sandbox_init("status"); |
151446ca | 508 | git_index *index; |
54254a0f VM |
509 | unsigned int status; |
510 | git_index_entry ancestor_entry, our_entry, their_entry; | |
151446ca | 511 | |
54254a0f VM |
512 | memset(&ancestor_entry, 0x0, sizeof(git_index_entry)); |
513 | memset(&our_entry, 0x0, sizeof(git_index_entry)); | |
514 | memset(&their_entry, 0x0, sizeof(git_index_entry)); | |
151446ca | 515 | |
54254a0f | 516 | ancestor_entry.path = "modified_file"; |
d67f270e | 517 | ancestor_entry.mode = 0100644; |
d541170c | 518 | git_oid_fromstr(&ancestor_entry.id, |
54254a0f | 519 | "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); |
151446ca | 520 | |
54254a0f | 521 | our_entry.path = "modified_file"; |
d67f270e | 522 | our_entry.mode = 0100644; |
d541170c | 523 | git_oid_fromstr(&our_entry.id, |
54254a0f | 524 | "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); |
151446ca | 525 | |
54254a0f | 526 | their_entry.path = "modified_file"; |
d67f270e | 527 | their_entry.mode = 0100644; |
d541170c | 528 | git_oid_fromstr(&their_entry.id, |
54254a0f | 529 | "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); |
151446ca | 530 | |
54254a0f VM |
531 | cl_git_pass(git_status_file(&status, repo, "modified_file")); |
532 | cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status); | |
151446ca AR |
533 | |
534 | cl_git_pass(git_repository_index(&index, repo)); | |
54254a0f | 535 | cl_git_pass(git_index_remove(index, "modified_file", 0)); |
4bf630b6 RB |
536 | cl_git_pass(git_index_conflict_add( |
537 | index, &ancestor_entry, &our_entry, &their_entry)); | |
538 | cl_git_pass(git_index_write(index)); | |
539 | git_index_free(index); | |
2a99df69 | 540 | |
54254a0f | 541 | cl_git_pass(git_status_file(&status, repo, "modified_file")); |
2a99df69 | 542 | |
7c948014 | 543 | cl_assert_equal_i(GIT_STATUS_CONFLICTED, status); |
2a99df69 | 544 | } |
0abd7244 RB |
545 | |
546 | static const char *filemode_paths[] = { | |
547 | "exec_off", | |
548 | "exec_off2on_staged", | |
549 | "exec_off2on_workdir", | |
550 | "exec_off_untracked", | |
551 | "exec_on", | |
552 | "exec_on2off_staged", | |
553 | "exec_on2off_workdir", | |
554 | "exec_on_untracked", | |
555 | }; | |
556 | ||
557 | static unsigned int filemode_statuses[] = { | |
558 | GIT_STATUS_CURRENT, | |
559 | GIT_STATUS_INDEX_MODIFIED, | |
560 | GIT_STATUS_WT_MODIFIED, | |
561 | GIT_STATUS_WT_NEW, | |
562 | GIT_STATUS_CURRENT, | |
563 | GIT_STATUS_INDEX_MODIFIED, | |
564 | GIT_STATUS_WT_MODIFIED, | |
565 | GIT_STATUS_WT_NEW | |
566 | }; | |
567 | ||
e9ca852e | 568 | static const int filemode_count = 8; |
0abd7244 RB |
569 | |
570 | void test_status_worktree__filemode_changes(void) | |
571 | { | |
572 | git_repository *repo = cl_git_sandbox_init("filemodes"); | |
573 | status_entry_counts counts; | |
79cfa20d | 574 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; |
0abd7244 RB |
575 | |
576 | /* overwrite stored filemode with platform appropriate value */ | |
0abd7244 | 577 | if (cl_is_chmod_supported()) |
1323c6d1 | 578 | cl_repo_set_bool(repo, "core.filemode", true); |
0abd7244 | 579 | else { |
e9ca852e | 580 | int i; |
1323c6d1 RB |
581 | |
582 | cl_repo_set_bool(repo, "core.filemode", false); | |
0abd7244 RB |
583 | |
584 | /* won't trust filesystem mode diffs, so these will appear unchanged */ | |
585 | for (i = 0; i < filemode_count; ++i) | |
586 | if (filemode_statuses[i] == GIT_STATUS_WT_MODIFIED) | |
587 | filemode_statuses[i] = GIT_STATUS_CURRENT; | |
588 | } | |
589 | ||
0abd7244 RB |
590 | opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | |
591 | GIT_STATUS_OPT_INCLUDE_IGNORED | | |
592 | GIT_STATUS_OPT_INCLUDE_UNMODIFIED; | |
593 | ||
594 | memset(&counts, 0, sizeof(counts)); | |
595 | counts.expected_entry_count = filemode_count; | |
596 | counts.expected_paths = filemode_paths; | |
597 | counts.expected_statuses = filemode_statuses; | |
598 | ||
599 | cl_git_pass( | |
600 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) | |
601 | ); | |
602 | ||
603 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); | |
604 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
605 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
0abd7244 | 606 | } |
a1773f9d | 607 | |
5dca2010 RB |
608 | static int cb_status__interrupt(const char *p, unsigned int s, void *payload) |
609 | { | |
610 | volatile int *count = (int *)payload; | |
611 | ||
612 | GIT_UNUSED(p); | |
613 | GIT_UNUSED(s); | |
614 | ||
615 | (*count)++; | |
616 | ||
25e0b157 | 617 | return (*count == 8) ? -111 : 0; |
5dca2010 RB |
618 | } |
619 | ||
620 | void test_status_worktree__interruptable_foreach(void) | |
621 | { | |
622 | int count = 0; | |
623 | git_repository *repo = cl_git_sandbox_init("status"); | |
624 | ||
625 | cl_assert_equal_i( | |
25e0b157 | 626 | -111, git_status_foreach(repo, cb_status__interrupt, &count) |
5dca2010 RB |
627 | ); |
628 | ||
629 | cl_assert_equal_i(8, count); | |
630 | } | |
52462e1c | 631 | |
f8e2cc9a RB |
632 | void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf(void) |
633 | { | |
634 | git_repository *repo = cl_git_sandbox_init("status"); | |
f8e2cc9a RB |
635 | unsigned int status; |
636 | ||
1323c6d1 | 637 | cl_repo_set_bool(repo, "core.autocrlf", true); |
f8e2cc9a RB |
638 | |
639 | cl_git_rewritefile("status/current_file", "current_file\r\n"); | |
640 | ||
641 | cl_git_pass(git_status_file(&status, repo, "current_file")); | |
642 | ||
8ef4e11a RB |
643 | /* stat data on file should no longer match stat cache, even though |
644 | * file diff will be empty because of line-ending conversion - matches | |
645 | * the Git command-line behavior here. | |
646 | */ | |
647 | cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status); | |
f8e2cc9a | 648 | } |
b2414661 | 649 | |
b8acb775 SS |
650 | void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf_issue_1397(void) |
651 | { | |
652 | git_repository *repo = cl_git_sandbox_init("issue_1397"); | |
b8acb775 SS |
653 | unsigned int status; |
654 | ||
1098cfae | 655 | cl_repo_set_bool(repo, "core.autocrlf", true); |
b8acb775 SS |
656 | |
657 | cl_git_pass(git_status_file(&status, repo, "crlf_file.txt")); | |
658 | ||
659 | cl_assert_equal_i(GIT_STATUS_CURRENT, status); | |
660 | } | |
661 | ||
b2414661 ET |
662 | void test_status_worktree__conflicted_item(void) |
663 | { | |
664 | git_repository *repo = cl_git_sandbox_init("status"); | |
665 | git_index *index; | |
666 | unsigned int status; | |
667 | git_index_entry ancestor_entry, our_entry, their_entry; | |
668 | ||
669 | memset(&ancestor_entry, 0x0, sizeof(git_index_entry)); | |
670 | memset(&our_entry, 0x0, sizeof(git_index_entry)); | |
671 | memset(&their_entry, 0x0, sizeof(git_index_entry)); | |
672 | ||
d67f270e | 673 | ancestor_entry.mode = 0100644; |
b2414661 | 674 | ancestor_entry.path = "modified_file"; |
d541170c | 675 | git_oid_fromstr(&ancestor_entry.id, |
b2414661 ET |
676 | "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); |
677 | ||
d67f270e | 678 | our_entry.mode = 0100644; |
b2414661 | 679 | our_entry.path = "modified_file"; |
d541170c | 680 | git_oid_fromstr(&our_entry.id, |
b2414661 ET |
681 | "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); |
682 | ||
d67f270e | 683 | their_entry.mode = 0100644; |
b2414661 | 684 | their_entry.path = "modified_file"; |
d541170c | 685 | git_oid_fromstr(&their_entry.id, |
b2414661 ET |
686 | "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); |
687 | ||
688 | cl_git_pass(git_status_file(&status, repo, "modified_file")); | |
689 | cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status); | |
690 | ||
691 | cl_git_pass(git_repository_index(&index, repo)); | |
692 | cl_git_pass(git_index_conflict_add(index, &ancestor_entry, | |
693 | &our_entry, &their_entry)); | |
694 | ||
695 | cl_git_pass(git_status_file(&status, repo, "modified_file")); | |
7c948014 | 696 | cl_assert_equal_i(GIT_STATUS_CONFLICTED, status); |
b2414661 ET |
697 | |
698 | git_index_free(index); | |
699 | } | |
700 | ||
191e97a0 ET |
701 | void test_status_worktree__conflict_has_no_oid(void) |
702 | { | |
703 | git_repository *repo = cl_git_sandbox_init("status"); | |
704 | git_index *index; | |
9f3c18e2 | 705 | git_index_entry entry = {{0}}; |
191e97a0 ET |
706 | git_status_list *statuslist; |
707 | const git_status_entry *status; | |
9f3c18e2 | 708 | git_oid zero_id = {{0}}; |
191e97a0 ET |
709 | |
710 | entry.mode = 0100644; | |
711 | entry.path = "modified_file"; | |
8a5a2e2f | 712 | git_oid_fromstr(&entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); |
191e97a0 ET |
713 | |
714 | cl_git_pass(git_repository_index(&index, repo)); | |
715 | cl_git_pass(git_index_conflict_add(index, &entry, &entry, &entry)); | |
716 | ||
717 | git_status_list_new(&statuslist, repo, NULL); | |
718 | ||
719 | cl_assert_equal_i(16, git_status_list_entrycount(statuslist)); | |
720 | ||
721 | status = git_status_byindex(statuslist, 2); | |
722 | ||
723 | cl_assert_equal_i(GIT_STATUS_CONFLICTED, status->status); | |
724 | cl_assert_equal_s("modified_file", status->head_to_index->old_file.path); | |
725 | cl_assert(!git_oid_equal(&zero_id, &status->head_to_index->old_file.id)); | |
726 | cl_assert(0 != status->head_to_index->old_file.mode); | |
727 | cl_assert_equal_s("modified_file", status->head_to_index->new_file.path); | |
728 | cl_assert_equal_oid(&zero_id, &status->head_to_index->new_file.id); | |
729 | cl_assert_equal_i(0, status->head_to_index->new_file.mode); | |
730 | cl_assert_equal_i(0, status->head_to_index->new_file.size); | |
731 | ||
732 | cl_assert_equal_s("modified_file", status->index_to_workdir->old_file.path); | |
733 | cl_assert_equal_oid(&zero_id, &status->index_to_workdir->old_file.id); | |
734 | cl_assert_equal_i(0, status->index_to_workdir->old_file.mode); | |
735 | cl_assert_equal_i(0, status->index_to_workdir->old_file.size); | |
736 | cl_assert_equal_s("modified_file", status->index_to_workdir->new_file.path); | |
737 | cl_assert( | |
738 | !git_oid_equal(&zero_id, &status->index_to_workdir->new_file.id) || | |
739 | !(status->index_to_workdir->new_file.flags & GIT_DIFF_FLAG_VALID_ID)); | |
740 | cl_assert(0 != status->index_to_workdir->new_file.mode); | |
741 | cl_assert(0 != status->index_to_workdir->new_file.size); | |
742 | ||
743 | git_index_free(index); | |
744 | git_status_list_free(statuslist); | |
745 | } | |
746 | ||
bcbb1e20 | 747 | static void assert_ignore_case( |
748 | bool should_ignore_case, | |
749 | int expected_lower_cased_file_status, | |
750 | int expected_camel_cased_file_status) | |
751 | { | |
bcbb1e20 | 752 | unsigned int status; |
4b181037 | 753 | git_buf lower_case_path = GIT_BUF_INIT, camel_case_path = GIT_BUF_INIT; |
bcbb1e20 | 754 | git_repository *repo, *repo2; |
4b181037 | 755 | |
bcbb1e20 | 756 | repo = cl_git_sandbox_init("empty_standard_repo"); |
757 | cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt"); | |
758 | ||
1323c6d1 | 759 | cl_repo_set_bool(repo, "core.ignorecase", should_ignore_case); |
bcbb1e20 | 760 | |
761 | cl_git_pass(git_buf_joinpath(&lower_case_path, | |
762 | git_repository_workdir(repo), "plop")); | |
763 | ||
764 | cl_git_mkfile(git_buf_cstr(&lower_case_path), ""); | |
765 | ||
766 | stage_and_commit(repo, "plop"); | |
767 | ||
768 | cl_git_pass(git_repository_open(&repo2, "./empty_standard_repo")); | |
769 | ||
bcbb1e20 | 770 | cl_git_pass(git_status_file(&status, repo2, "plop")); |
771 | cl_assert_equal_i(GIT_STATUS_CURRENT, status); | |
772 | ||
4b181037 RB |
773 | cl_git_pass(git_buf_joinpath(&camel_case_path, |
774 | git_repository_workdir(repo), "Plop")); | |
775 | ||
bcbb1e20 | 776 | cl_git_pass(p_rename(git_buf_cstr(&lower_case_path), git_buf_cstr(&camel_case_path))); |
777 | ||
778 | cl_git_pass(git_status_file(&status, repo2, "plop")); | |
779 | cl_assert_equal_i(expected_lower_cased_file_status, status); | |
780 | ||
781 | cl_git_pass(git_status_file(&status, repo2, "Plop")); | |
782 | cl_assert_equal_i(expected_camel_cased_file_status, status); | |
783 | ||
784 | git_repository_free(repo2); | |
ac3d33df JK |
785 | git_buf_dispose(&lower_case_path); |
786 | git_buf_dispose(&camel_case_path); | |
bcbb1e20 | 787 | } |
788 | ||
4b181037 | 789 | void test_status_worktree__file_status_honors_core_ignorecase_true(void) |
bcbb1e20 | 790 | { |
791 | assert_ignore_case(true, GIT_STATUS_CURRENT, GIT_STATUS_CURRENT); | |
792 | } | |
793 | ||
4b181037 | 794 | void test_status_worktree__file_status_honors_core_ignorecase_false(void) |
bcbb1e20 | 795 | { |
796 | assert_ignore_case(false, GIT_STATUS_WT_DELETED, GIT_STATUS_WT_NEW); | |
797 | } | |
1f9e41ee RB |
798 | |
799 | void test_status_worktree__file_status_honors_case_ignorecase_regarding_untracked_files(void) | |
800 | { | |
801 | git_repository *repo = cl_git_sandbox_init("status"); | |
802 | unsigned int status; | |
803 | git_index *index; | |
804 | ||
805 | cl_repo_set_bool(repo, "core.ignorecase", false); | |
806 | ||
807 | repo = cl_git_sandbox_reopen(); | |
808 | ||
809 | /* Actually returns GIT_STATUS_IGNORED on Windows */ | |
810 | cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND); | |
811 | ||
812 | cl_git_pass(git_repository_index(&index, repo)); | |
813 | ||
814 | cl_git_pass(git_index_add_bypath(index, "new_file")); | |
815 | cl_git_pass(git_index_write(index)); | |
816 | git_index_free(index); | |
817 | ||
818 | /* Actually returns GIT_STATUS_IGNORED on Windows */ | |
819 | cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND); | |
820 | } | |
351888cf RB |
821 | |
822 | void test_status_worktree__simple_delete(void) | |
823 | { | |
824 | git_repository *repo = cl_git_sandbox_init("renames"); | |
825 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
826 | int count; | |
827 | ||
828 | opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | | |
829 | GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH | | |
830 | GIT_STATUS_OPT_EXCLUDE_SUBMODULES | | |
831 | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; | |
832 | ||
833 | count = 0; | |
834 | cl_git_pass( | |
835 | git_status_foreach_ext(repo, &opts, cb_status__count, &count) ); | |
836 | cl_assert_equal_i(0, count); | |
837 | ||
838 | cl_must_pass(p_unlink("renames/untimely.txt")); | |
839 | ||
840 | count = 0; | |
841 | cl_git_pass( | |
842 | git_status_foreach_ext(repo, &opts, cb_status__count, &count) ); | |
843 | cl_assert_equal_i(1, count); | |
844 | } | |
845 | ||
846 | void test_status_worktree__simple_delete_indexed(void) | |
847 | { | |
848 | git_repository *repo = cl_git_sandbox_init("renames"); | |
849 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
850 | git_status_list *status; | |
851 | ||
852 | opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | | |
853 | GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH | | |
854 | GIT_STATUS_OPT_EXCLUDE_SUBMODULES | | |
855 | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; | |
856 | ||
857 | cl_git_pass(git_status_list_new(&status, repo, &opts)); | |
858 | cl_assert_equal_sz(0, git_status_list_entrycount(status)); | |
859 | git_status_list_free(status); | |
860 | ||
861 | cl_must_pass(p_unlink("renames/untimely.txt")); | |
862 | ||
863 | cl_git_pass(git_status_list_new(&status, repo, &opts)); | |
864 | cl_assert_equal_sz(1, git_status_list_entrycount(status)); | |
865 | cl_assert_equal_i( | |
866 | GIT_STATUS_WT_DELETED, git_status_byindex(status, 0)->status); | |
867 | git_status_list_free(status); | |
868 | } | |
22b6b82f RB |
869 | |
870 | static const char *icase_paths[] = { "B", "c", "g", "H" }; | |
871 | static unsigned int icase_statuses[] = { | |
872 | GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED, | |
873 | GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED, | |
874 | }; | |
875 | ||
876 | static const char *case_paths[] = { "B", "H", "c", "g" }; | |
877 | static unsigned int case_statuses[] = { | |
878 | GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED, | |
879 | GIT_STATUS_WT_DELETED, GIT_STATUS_WT_MODIFIED, | |
880 | }; | |
881 | ||
882 | void test_status_worktree__sorting_by_case(void) | |
883 | { | |
884 | git_repository *repo = cl_git_sandbox_init("icase"); | |
885 | git_index *index; | |
886 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
887 | bool native_ignore_case; | |
888 | status_entry_counts counts; | |
889 | ||
890 | cl_git_pass(git_repository_index(&index, repo)); | |
891 | native_ignore_case = | |
ac3d33df | 892 | (git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0; |
22b6b82f RB |
893 | git_index_free(index); |
894 | ||
895 | memset(&counts, 0, sizeof(counts)); | |
896 | counts.expected_entry_count = 0; | |
897 | counts.expected_paths = NULL; | |
898 | counts.expected_statuses = NULL; | |
899 | cl_git_pass( | |
900 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); | |
901 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); | |
902 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
903 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
904 | ||
905 | cl_git_rewritefile("icase/B", "new stuff"); | |
906 | cl_must_pass(p_unlink("icase/c")); | |
907 | cl_git_rewritefile("icase/g", "new stuff"); | |
908 | cl_must_pass(p_unlink("icase/H")); | |
909 | ||
910 | memset(&counts, 0, sizeof(counts)); | |
911 | counts.expected_entry_count = 4; | |
912 | if (native_ignore_case) { | |
913 | counts.expected_paths = icase_paths; | |
914 | counts.expected_statuses = icase_statuses; | |
915 | } else { | |
916 | counts.expected_paths = case_paths; | |
917 | counts.expected_statuses = case_statuses; | |
918 | } | |
919 | cl_git_pass( | |
920 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); | |
921 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); | |
922 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
923 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
924 | ||
925 | opts.flags = GIT_STATUS_OPT_SORT_CASE_SENSITIVELY; | |
926 | ||
927 | memset(&counts, 0, sizeof(counts)); | |
928 | counts.expected_entry_count = 4; | |
929 | counts.expected_paths = case_paths; | |
930 | counts.expected_statuses = case_statuses; | |
931 | cl_git_pass( | |
932 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); | |
933 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); | |
934 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
935 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
936 | ||
937 | opts.flags = GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY; | |
938 | ||
939 | memset(&counts, 0, sizeof(counts)); | |
940 | counts.expected_entry_count = 4; | |
941 | counts.expected_paths = icase_paths; | |
942 | counts.expected_statuses = icase_statuses; | |
943 | cl_git_pass( | |
944 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); | |
945 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); | |
946 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
947 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
948 | } | |
8c8a5490 BS |
949 | |
950 | void test_status_worktree__long_filenames(void) | |
951 | { | |
2984f319 | 952 | char path[260*4+1]; |
8c8a5490 BS |
953 | const char *expected_paths[] = {path}; |
954 | const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; | |
955 | ||
956 | git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); | |
957 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
958 | status_entry_counts counts = {0}; | |
959 | ||
ac3d33df | 960 | /* Create directory with amazingly long filename */ |
8c8a5490 | 961 | sprintf(path, "empty_standard_repo/%s", longname); |
ac2fba0e | 962 | cl_git_pass(git_futils_mkdir_r(path, 0777)); |
8c8a5490 BS |
963 | sprintf(path, "empty_standard_repo/%s/foo", longname); |
964 | cl_git_mkfile(path, "dummy"); | |
965 | ||
966 | sprintf(path, "%s/foo", longname); | |
967 | counts.expected_entry_count = 1; | |
968 | counts.expected_paths = expected_paths; | |
969 | counts.expected_statuses = expected_statuses; | |
970 | ||
971 | opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; | |
972 | opts.flags = GIT_STATUS_OPT_DEFAULTS; | |
973 | ||
974 | cl_git_pass( | |
975 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); | |
976 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); | |
977 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
978 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
979 | } | |
980 | ||
cd424ad5 RB |
981 | /* The update stat cache tests mostly just mirror other tests and try |
982 | * to make sure that updating the stat cache doesn't change the results | |
983 | * while reducing the amount of work that needs to be done | |
984 | */ | |
985 | ||
9c8ed499 RB |
986 | static void check_status0(git_status_list *status) |
987 | { | |
988 | size_t i, max_i = git_status_list_entrycount(status); | |
989 | cl_assert_equal_sz(entry_count0, max_i); | |
990 | for (i = 0; i < max_i; ++i) { | |
991 | const git_status_entry *entry = git_status_byindex(status, i); | |
992 | cl_assert_equal_i(entry_statuses0[i], entry->status); | |
993 | } | |
994 | } | |
995 | ||
cd424ad5 RB |
996 | void test_status_worktree__update_stat_cache_0(void) |
997 | { | |
998 | git_repository *repo = cl_git_sandbox_init("status"); | |
9c8ed499 RB |
999 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; |
1000 | git_status_list *status; | |
1001 | git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT; | |
e44abe16 | 1002 | git_index *index; |
cd424ad5 | 1003 | |
9c8ed499 | 1004 | opts.flags = GIT_STATUS_OPT_DEFAULTS; |
cd424ad5 | 1005 | |
9c8ed499 RB |
1006 | cl_git_pass(git_status_list_new(&status, repo, &opts)); |
1007 | check_status0(status); | |
1008 | cl_git_pass(git_status_list_get_perfdata(&perf, status)); | |
1009 | cl_assert_equal_sz(13 + 3, perf.stat_calls); | |
1010 | cl_assert_equal_sz(5, perf.oid_calculations); | |
cd424ad5 | 1011 | |
9c8ed499 | 1012 | git_status_list_free(status); |
cd424ad5 | 1013 | |
e44abe16 CMN |
1014 | /* tick the index so we avoid recalculating racily-clean entries */ |
1015 | cl_git_pass(git_repository_index__weakptr(&index, repo)); | |
1016 | tick_index(index); | |
1017 | ||
9c8ed499 RB |
1018 | opts.flags |= GIT_STATUS_OPT_UPDATE_INDEX; |
1019 | ||
1020 | cl_git_pass(git_status_list_new(&status, repo, &opts)); | |
1021 | check_status0(status); | |
1022 | cl_git_pass(git_status_list_get_perfdata(&perf, status)); | |
1023 | cl_assert_equal_sz(13 + 3, perf.stat_calls); | |
1024 | cl_assert_equal_sz(5, perf.oid_calculations); | |
cd424ad5 | 1025 | |
9c8ed499 | 1026 | git_status_list_free(status); |
cd424ad5 | 1027 | |
9c8ed499 | 1028 | opts.flags &= ~GIT_STATUS_OPT_UPDATE_INDEX; |
cd424ad5 | 1029 | |
ff475375 CMN |
1030 | /* tick again as the index updating from the previous diff might have reset the timestamp */ |
1031 | tick_index(index); | |
9c8ed499 RB |
1032 | cl_git_pass(git_status_list_new(&status, repo, &opts)); |
1033 | check_status0(status); | |
1034 | cl_git_pass(git_status_list_get_perfdata(&perf, status)); | |
1035 | cl_assert_equal_sz(13 + 3, perf.stat_calls); | |
1036 | cl_assert_equal_sz(0, perf.oid_calculations); | |
cd424ad5 | 1037 | |
9c8ed499 | 1038 | git_status_list_free(status); |
cd424ad5 | 1039 | } |
2b5a99d8 | 1040 | |
79d5b5c9 | 1041 | void test_status_worktree__unreadable(void) |
2b5a99d8 | 1042 | { |
c8402334 | 1043 | #ifndef GIT_WIN32 |
523553f9 | 1044 | const char *expected_paths[] = { "no_permission/foo" }; |
9532edc0 | 1045 | const unsigned int expected_statuses[] = {GIT_STATUS_WT_UNREADABLE}; |
2b5a99d8 AR |
1046 | |
1047 | git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); | |
1048 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
1049 | status_entry_counts counts = {0}; | |
1050 | ||
bbd65ad2 ET |
1051 | if (geteuid() == 0) |
1052 | cl_skip(); | |
1053 | ||
9532edc0 | 1054 | /* Create directory with no read permission */ |
ac2fba0e | 1055 | cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777)); |
8d3a2d5f AR |
1056 | cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy"); |
1057 | p_chmod("empty_standard_repo/no_permission", 0644); | |
2b5a99d8 | 1058 | |
2b5a99d8 AR |
1059 | counts.expected_entry_count = 1; |
1060 | counts.expected_paths = expected_paths; | |
1061 | counts.expected_statuses = expected_statuses; | |
1062 | ||
1063 | opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; | |
523553f9 | 1064 | opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_INCLUDE_UNREADABLE; |
2b5a99d8 AR |
1065 | |
1066 | cl_git_pass( | |
1067 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); | |
dc4906f1 | 1068 | |
9532edc0 | 1069 | /* Restore permissions so we can cleanup :) */ |
dc4906f1 AR |
1070 | p_chmod("empty_standard_repo/no_permission", 0777); |
1071 | ||
2b5a99d8 AR |
1072 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); |
1073 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
1074 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
eae0bfdc PP |
1075 | #else |
1076 | cl_skip(); | |
c8402334 | 1077 | #endif |
2b5a99d8 | 1078 | } |
79d5b5c9 | 1079 | |
a576a342 AR |
1080 | void test_status_worktree__unreadable_not_included(void) |
1081 | { | |
c8402334 | 1082 | #ifndef GIT_WIN32 |
a576a342 AR |
1083 | const char *expected_paths[] = { "no_permission/" }; |
1084 | const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; | |
1085 | ||
1086 | git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); | |
1087 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
1088 | status_entry_counts counts = {0}; | |
1089 | ||
1090 | /* Create directory with no read permission */ | |
ac2fba0e | 1091 | cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777)); |
a576a342 AR |
1092 | cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy"); |
1093 | p_chmod("empty_standard_repo/no_permission", 0644); | |
1094 | ||
1095 | counts.expected_entry_count = 1; | |
1096 | counts.expected_paths = expected_paths; | |
1097 | counts.expected_statuses = expected_statuses; | |
1098 | ||
1099 | opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; | |
1100 | opts.flags = (GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_INCLUDE_UNTRACKED); | |
1101 | ||
1102 | cl_git_pass( | |
1103 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); | |
1104 | ||
1105 | /* Restore permissions so we can cleanup :) */ | |
1106 | p_chmod("empty_standard_repo/no_permission", 0777); | |
1107 | ||
1108 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); | |
1109 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
1110 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
eae0bfdc PP |
1111 | #else |
1112 | cl_skip(); | |
c8402334 | 1113 | #endif |
a576a342 AR |
1114 | } |
1115 | ||
79d5b5c9 AR |
1116 | void test_status_worktree__unreadable_as_untracked(void) |
1117 | { | |
1118 | const char *expected_paths[] = { "no_permission/foo" }; | |
1119 | const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; | |
1120 | ||
1121 | git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); | |
1122 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
1123 | status_entry_counts counts = {0}; | |
1124 | ||
1125 | /* Create directory with no read permission */ | |
ac2fba0e | 1126 | cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777)); |
79d5b5c9 AR |
1127 | cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy"); |
1128 | p_chmod("empty_standard_repo/no_permission", 0644); | |
1129 | ||
1130 | counts.expected_entry_count = 1; | |
1131 | counts.expected_paths = expected_paths; | |
1132 | counts.expected_statuses = expected_statuses; | |
1133 | ||
1134 | opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; | |
1135 | opts.flags = GIT_STATUS_OPT_DEFAULTS | | |
1136 | GIT_STATUS_OPT_INCLUDE_UNREADABLE | | |
1137 | GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED; | |
1138 | ||
1139 | cl_git_pass( | |
1140 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); | |
1141 | ||
1142 | /* Restore permissions so we can cleanup :) */ | |
1143 | p_chmod("empty_standard_repo/no_permission", 0777); | |
1144 | ||
1145 | cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); | |
1146 | cl_assert_equal_i(0, counts.wrong_status_flags_count); | |
1147 | cl_assert_equal_i(0, counts.wrong_sorted_path); | |
1148 | } | |
1149 | ||
fc656802 ET |
1150 | void test_status_worktree__update_index_with_symlink_doesnt_change_mode(void) |
1151 | { | |
1152 | git_repository *repo = cl_git_sandbox_init("testrepo"); | |
1153 | git_reference *head; | |
1154 | git_object *head_object; | |
1155 | git_index *index; | |
1156 | const git_index_entry *idx_entry; | |
1157 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
1158 | status_entry_counts counts = {0}; | |
1159 | const char *expected_paths[] = { "README" }; | |
1160 | const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; | |
1161 | ||
1162 | opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; | |
1163 | opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_UPDATE_INDEX; | |
1164 | ||
1165 | cl_git_pass(git_repository_head(&head, repo)); | |
ac3d33df | 1166 | cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJECT_COMMIT)); |
fc656802 ET |
1167 | |
1168 | cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL)); | |
1169 | ||
1170 | cl_git_rewritefile("testrepo/README", "This was rewritten."); | |
1171 | ||
1172 | /* this status rewrites the index because we have changed the | |
1173 | * contents of a tracked file | |
1174 | */ | |
1175 | counts.expected_entry_count = 1; | |
1176 | counts.expected_paths = expected_paths; | |
1177 | counts.expected_statuses = expected_statuses; | |
1178 | ||
1179 | cl_git_pass( | |
1180 | git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); | |
1181 | cl_assert_equal_i(1, counts.entry_count); | |
1182 | ||
1183 | /* now ensure that the status's rewrite of the index did not screw | |
1184 | * up the mode of the symlink `link_to_new.txt`, particularly | |
1185 | * on platforms that don't support symlinks | |
1186 | */ | |
1187 | cl_git_pass(git_repository_index(&index, repo)); | |
1188 | cl_git_pass(git_index_read(index, true)); | |
1189 | ||
1190 | cl_assert(idx_entry = git_index_get_bypath(index, "link_to_new.txt", 0)); | |
1191 | cl_assert(S_ISLNK(idx_entry->mode)); | |
1192 | ||
1193 | git_index_free(index); | |
1194 | git_object_free(head_object); | |
1195 | git_reference_free(head); | |
1196 | } | |
1197 | ||
ae86aa5a MS |
1198 | static const char *testrepo2_subdir_paths[] = { |
1199 | "subdir/README", | |
1200 | "subdir/new.txt", | |
1201 | "subdir/subdir2/README", | |
1202 | "subdir/subdir2/new.txt", | |
1203 | }; | |
1204 | ||
1205 | static const char *testrepo2_subdir_paths_icase[] = { | |
1206 | "subdir/new.txt", | |
1207 | "subdir/README", | |
1208 | "subdir/subdir2/new.txt", | |
1209 | "subdir/subdir2/README" | |
1210 | }; | |
1211 | ||
1212 | void test_status_worktree__with_directory_in_pathlist(void) | |
1213 | { | |
1214 | git_repository *repo = cl_git_sandbox_init("testrepo2"); | |
1215 | git_index *index; | |
1216 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
1217 | git_status_list *statuslist; | |
1218 | const git_status_entry *status; | |
1219 | size_t i, entrycount; | |
1220 | bool native_ignore_case; | |
17442b28 | 1221 | char *subdir_path = "subdir"; |
ae86aa5a MS |
1222 | |
1223 | cl_git_pass(git_repository_index(&index, repo)); | |
1224 | native_ignore_case = | |
ac3d33df | 1225 | (git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0; |
ae86aa5a MS |
1226 | git_index_free(index); |
1227 | ||
17442b28 | 1228 | opts.pathspec.strings = &subdir_path; |
ae86aa5a | 1229 | opts.pathspec.count = 1; |
ae86aa5a MS |
1230 | opts.flags = |
1231 | GIT_STATUS_OPT_DEFAULTS | | |
1232 | GIT_STATUS_OPT_INCLUDE_UNMODIFIED | | |
1233 | GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH; | |
1234 | ||
1235 | opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; | |
1236 | git_status_list_new(&statuslist, repo, &opts); | |
1237 | ||
1238 | entrycount = git_status_list_entrycount(statuslist); | |
1239 | cl_assert_equal_i(4, entrycount); | |
1240 | ||
1241 | for (i = 0; i < entrycount; i++) { | |
1242 | status = git_status_byindex(statuslist, i); | |
1243 | cl_assert_equal_i(0, status->status); | |
1244 | cl_assert_equal_s(native_ignore_case ? | |
1245 | testrepo2_subdir_paths_icase[i] : | |
1246 | testrepo2_subdir_paths[i], | |
1247 | status->index_to_workdir->old_file.path); | |
1248 | } | |
1249 | ||
17442b28 ET |
1250 | git_status_list_free(statuslist); |
1251 | ||
ae86aa5a MS |
1252 | opts.show = GIT_STATUS_SHOW_INDEX_ONLY; |
1253 | git_status_list_new(&statuslist, repo, &opts); | |
1254 | ||
1255 | entrycount = git_status_list_entrycount(statuslist); | |
1256 | cl_assert_equal_i(4, entrycount); | |
1257 | ||
1258 | for (i = 0; i < entrycount; i++) { | |
1259 | status = git_status_byindex(statuslist, i); | |
1260 | cl_assert_equal_i(0, status->status); | |
1261 | cl_assert_equal_s(native_ignore_case ? | |
1262 | testrepo2_subdir_paths_icase[i] : | |
1263 | testrepo2_subdir_paths[i], | |
1264 | status->head_to_index->old_file.path); | |
1265 | } | |
1266 | ||
17442b28 ET |
1267 | git_status_list_free(statuslist); |
1268 | ||
ae86aa5a MS |
1269 | opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; |
1270 | git_status_list_new(&statuslist, repo, &opts); | |
1271 | ||
1272 | entrycount = git_status_list_entrycount(statuslist); | |
1273 | cl_assert_equal_i(4, entrycount); | |
1274 | ||
1275 | for (i = 0; i < entrycount; i++) { | |
1276 | status = git_status_byindex(statuslist, i); | |
1277 | cl_assert_equal_i(0, status->status); | |
1278 | cl_assert_equal_s(native_ignore_case ? | |
1279 | testrepo2_subdir_paths_icase[i] : | |
1280 | testrepo2_subdir_paths[i], | |
1281 | status->index_to_workdir->old_file.path); | |
1282 | } | |
17442b28 ET |
1283 | |
1284 | git_status_list_free(statuslist); | |
ae86aa5a | 1285 | } |
b6204260 | 1286 | |
eae0bfdc PP |
1287 | void test_status_worktree__at_head_parent(void) |
1288 | { | |
1289 | git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); | |
1290 | git_status_options opts = GIT_STATUS_OPTIONS_INIT; | |
1291 | git_status_list *statuslist; | |
1292 | git_tree *parent_tree; | |
1293 | const git_status_entry *status; | |
1294 | ||
1295 | cl_git_mkfile("empty_standard_repo/file1", "ping"); | |
1296 | stage_and_commit(repo, "file1"); | |
1297 | ||
1298 | cl_git_pass(git_repository_head_tree(&parent_tree, repo)); | |
1299 | ||
1300 | cl_git_mkfile("empty_standard_repo/file2", "pong"); | |
1301 | stage_and_commit(repo, "file2"); | |
1302 | ||
1303 | cl_git_rewritefile("empty_standard_repo/file2", "pyng"); | |
1304 | ||
1305 | opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; | |
1306 | opts.baseline = parent_tree; | |
1307 | cl_git_pass(git_status_list_new(&statuslist, repo, &opts)); | |
1308 | ||
1309 | cl_assert_equal_sz(1, git_status_list_entrycount(statuslist)); | |
1310 | status = git_status_byindex(statuslist, 0); | |
1311 | cl_assert(status != NULL); | |
1312 | cl_assert_equal_s("file2", status->index_to_workdir->old_file.path); | |
1313 | cl_assert_equal_i(GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW, status->status); | |
1314 | ||
1315 | git_tree_free(parent_tree); | |
1316 | git_status_list_free(statuslist); | |
1317 | } |