]> git.proxmox.com Git - libgit2.git/blob - tests/index/tests.c
Prep for unstable release
[libgit2.git] / tests / index / tests.c
1 #include "clar_libgit2.h"
2 #include "index.h"
3
4 static const size_t index_entry_count = 109;
5 static const size_t index_entry_count_2 = 1437;
6 #define TEST_INDEX_PATH cl_fixture("testrepo.git/index")
7 #define TEST_INDEX2_PATH cl_fixture("gitgit.index")
8 #define TEST_INDEXBIG_PATH cl_fixture("big.index")
9 #define TEST_INDEXBAD_PATH cl_fixture("bad.index")
10
11
12 /* Suite data */
13 struct test_entry {
14 size_t index;
15 char path[128];
16 off64_t file_size;
17 git_time_t mtime;
18 };
19
20 static struct test_entry test_entries[] = {
21 {4, "Makefile", 5064, 0x4C3F7F33},
22 {6, "git.git-authors", 2709, 0x4C3F7F33},
23 {36, "src/index.c", 10014, 0x4C43368D},
24 {48, "src/revobject.h", 1448, 0x4C3F7FE2},
25 {62, "tests/Makefile", 2631, 0x4C3F7F33}
26 };
27
28 /* Helpers */
29 static void copy_file(const char *src, const char *dst)
30 {
31 git_buf source_buf = GIT_BUF_INIT;
32 git_file dst_fd;
33
34 cl_git_pass(git_futils_readbuffer(&source_buf, src));
35
36 dst_fd = git_futils_creat_withpath(dst, 0777, 0666); /* -V536 */
37 if (dst_fd < 0)
38 goto cleanup;
39
40 cl_git_pass(p_write(dst_fd, source_buf.ptr, source_buf.size));
41
42 cleanup:
43 git_buf_dispose(&source_buf);
44 p_close(dst_fd);
45 }
46
47 static void files_are_equal(const char *a, const char *b)
48 {
49 git_buf buf_a = GIT_BUF_INIT;
50 git_buf buf_b = GIT_BUF_INIT;
51 int pass;
52
53 if (git_futils_readbuffer(&buf_a, a) < 0)
54 cl_assert(0);
55
56 if (git_futils_readbuffer(&buf_b, b) < 0) {
57 git_buf_dispose(&buf_a);
58 cl_assert(0);
59 }
60
61 pass = (buf_a.size == buf_b.size && !memcmp(buf_a.ptr, buf_b.ptr, buf_a.size));
62
63 git_buf_dispose(&buf_a);
64 git_buf_dispose(&buf_b);
65
66 cl_assert(pass);
67 }
68
69
70 /* Fixture setup and teardown */
71 void test_index_tests__initialize(void)
72 {
73 }
74
75 void test_index_tests__cleanup(void)
76 {
77 cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, 0));
78 }
79
80 void test_index_tests__empty_index(void)
81 {
82 git_index *index;
83
84 cl_git_pass(git_index_open(&index, "in-memory-index"));
85 cl_assert(index->on_disk == 0);
86
87 cl_assert(git_index_entrycount(index) == 0);
88 cl_assert(git_vector_is_sorted(&index->entries));
89
90 git_index_free(index);
91 }
92
93 void test_index_tests__default_test_index(void)
94 {
95 git_index *index;
96 unsigned int i;
97 git_index_entry **entries;
98
99 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
100 cl_assert(index->on_disk);
101
102 cl_assert(git_index_entrycount(index) == index_entry_count);
103 cl_assert(git_vector_is_sorted(&index->entries));
104
105 entries = (git_index_entry **)index->entries.contents;
106
107 for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
108 git_index_entry *e = entries[test_entries[i].index];
109
110 cl_assert_equal_s(e->path, test_entries[i].path);
111 cl_assert_equal_i(e->mtime.seconds, test_entries[i].mtime);
112 cl_assert_equal_i(e->file_size, test_entries[i].file_size);
113 }
114
115 git_index_free(index);
116 }
117
118 void test_index_tests__gitgit_index(void)
119 {
120 git_index *index;
121
122 cl_git_pass(git_index_open(&index, TEST_INDEX2_PATH));
123 cl_assert(index->on_disk);
124
125 cl_assert(git_index_entrycount(index) == index_entry_count_2);
126 cl_assert(git_vector_is_sorted(&index->entries));
127 cl_assert(index->tree != NULL);
128
129 git_index_free(index);
130 }
131
132 void test_index_tests__find_in_existing(void)
133 {
134 git_index *index;
135 unsigned int i;
136
137 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
138
139 for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
140 size_t idx;
141
142 cl_assert(!git_index_find(&idx, index, test_entries[i].path));
143 cl_assert(idx == test_entries[i].index);
144 }
145
146 git_index_free(index);
147 }
148
149 void test_index_tests__find_in_empty(void)
150 {
151 git_index *index;
152 unsigned int i;
153
154 cl_git_pass(git_index_open(&index, "fake-index"));
155
156 for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
157 cl_assert(GIT_ENOTFOUND == git_index_find(NULL, index, test_entries[i].path));
158 }
159
160 git_index_free(index);
161 }
162
163 void test_index_tests__find_prefix(void)
164 {
165 git_index *index;
166 const git_index_entry *entry;
167 size_t pos;
168
169 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
170
171 cl_git_pass(git_index_find_prefix(&pos, index, "src"));
172 entry = git_index_get_byindex(index, pos);
173 cl_assert(git__strcmp(entry->path, "src/block-sha1/sha1.c") == 0);
174
175 cl_git_pass(git_index_find_prefix(&pos, index, "src/co"));
176 entry = git_index_get_byindex(index, pos);
177 cl_assert(git__strcmp(entry->path, "src/commit.c") == 0);
178
179 cl_assert(GIT_ENOTFOUND == git_index_find_prefix(NULL, index, "blah"));
180
181 git_index_free(index);
182 }
183
184 void test_index_tests__write(void)
185 {
186 git_index *index;
187
188 copy_file(TEST_INDEXBIG_PATH, "index_rewrite");
189
190 cl_git_pass(git_index_open(&index, "index_rewrite"));
191 cl_assert(index->on_disk);
192
193 cl_git_pass(git_index_write(index));
194 files_are_equal(TEST_INDEXBIG_PATH, "index_rewrite");
195
196 git_index_free(index);
197
198 p_unlink("index_rewrite");
199 }
200
201 void test_index_tests__sort0(void)
202 {
203 /* sort the entires in an index */
204
205 /*
206 * TODO: This no longer applies:
207 * index sorting in Git uses some specific changes to the way
208 * directories are sorted.
209 *
210 * We need to specificially check for this by creating a new
211 * index, adding entries in random order and then
212 * checking for consistency
213 */
214 }
215
216 void test_index_tests__sort1(void)
217 {
218 /* sort the entires in an empty index */
219 git_index *index;
220
221 cl_git_pass(git_index_open(&index, "fake-index"));
222
223 /* FIXME: this test is slightly dumb */
224 cl_assert(git_vector_is_sorted(&index->entries));
225
226 git_index_free(index);
227 }
228
229 static void cleanup_myrepo(void *opaque)
230 {
231 GIT_UNUSED(opaque);
232 cl_fixture_cleanup("myrepo");
233 }
234
235 void test_index_tests__add(void)
236 {
237 git_index *index;
238 git_filebuf file = GIT_FILEBUF_INIT;
239 git_repository *repo;
240 const git_index_entry *entry;
241 git_oid id1;
242
243 cl_set_cleanup(&cleanup_myrepo, NULL);
244
245 /* Intialize a new repository */
246 cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
247
248 /* Ensure we're the only guy in the room */
249 cl_git_pass(git_repository_index(&index, repo));
250 cl_assert(git_index_entrycount(index) == 0);
251
252 /* Create a new file in the working directory */
253 cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
254 cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
255 cl_git_pass(git_filebuf_write(&file, "hey there\n", 10));
256 cl_git_pass(git_filebuf_commit(&file));
257
258 /* Store the expected hash of the file/blob
259 * This has been generated by executing the following
260 * $ echo "hey there" | git hash-object --stdin
261 */
262 cl_git_pass(git_oid_fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"));
263
264 /* Add the new file to the index */
265 cl_git_pass(git_index_add_bypath(index, "test.txt"));
266
267 /* Wow... it worked! */
268 cl_assert(git_index_entrycount(index) == 1);
269 entry = git_index_get_byindex(index, 0);
270
271 /* And the built-in hashing mechanism worked as expected */
272 cl_assert_equal_oid(&id1, &entry->id);
273
274 /* Test access by path instead of index */
275 cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
276 cl_assert_equal_oid(&id1, &entry->id);
277
278 git_index_free(index);
279 git_repository_free(repo);
280 }
281
282 void test_index_tests__add_frombuffer(void)
283 {
284 git_index *index;
285 git_repository *repo;
286 git_index_entry entry;
287 const git_index_entry *returned_entry;
288
289 git_oid id1;
290 git_blob *blob;
291
292 const char *content = "hey there\n";
293
294 cl_set_cleanup(&cleanup_myrepo, NULL);
295
296 /* Intialize a new repository */
297 cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
298
299 /* Ensure we're the only guy in the room */
300 cl_git_pass(git_repository_index(&index, repo));
301 cl_assert(git_index_entrycount(index) == 0);
302
303 /* Store the expected hash of the file/blob
304 * This has been generated by executing the following
305 * $ echo "hey there" | git hash-object --stdin
306 */
307 cl_git_pass(git_oid_fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"));
308
309 /* Add the new file to the index */
310 memset(&entry, 0x0, sizeof(git_index_entry));
311 entry.mode = GIT_FILEMODE_BLOB;
312 entry.path = "test.txt";
313 cl_git_pass(git_index_add_from_buffer(index, &entry,
314 content, strlen(content)));
315
316 /* Wow... it worked! */
317 cl_assert(git_index_entrycount(index) == 1);
318 returned_entry = git_index_get_byindex(index, 0);
319
320 /* And the built-in hashing mechanism worked as expected */
321 cl_assert_equal_oid(&id1, &returned_entry->id);
322 /* And mode is the one asked */
323 cl_assert_equal_i(GIT_FILEMODE_BLOB, returned_entry->mode);
324
325 /* Test access by path instead of index */
326 cl_assert((returned_entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
327 cl_assert_equal_oid(&id1, &returned_entry->id);
328
329 /* Test the blob is in the repository */
330 cl_git_pass(git_blob_lookup(&blob, repo, &id1));
331 cl_assert_equal_s(
332 content, git_blob_rawcontent(blob));
333 git_blob_free(blob);
334
335 git_index_free(index);
336 git_repository_free(repo);
337 }
338
339 void test_index_tests__dirty_and_clean(void)
340 {
341 git_repository *repo;
342 git_index *index;
343 git_index_entry entry = {{0}};
344
345 /* Index is not dirty after opening */
346 cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
347 cl_git_pass(git_repository_index(&index, repo));
348
349 cl_assert(git_index_entrycount(index) == 0);
350 cl_assert(!git_index_is_dirty(index));
351
352 /* Index is dirty after adding an entry */
353 entry.mode = GIT_FILEMODE_BLOB;
354 entry.path = "test.txt";
355 cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
356 cl_assert(git_index_entrycount(index) == 1);
357 cl_assert(git_index_is_dirty(index));
358
359 /* Index is not dirty after write */
360 cl_git_pass(git_index_write(index));
361 cl_assert(!git_index_is_dirty(index));
362
363 /* Index is dirty after removing an entry */
364 cl_git_pass(git_index_remove_bypath(index, "test.txt"));
365 cl_assert(git_index_entrycount(index) == 0);
366 cl_assert(git_index_is_dirty(index));
367
368 /* Index is not dirty after write */
369 cl_git_pass(git_index_write(index));
370 cl_assert(!git_index_is_dirty(index));
371
372 /* Index remains not dirty after read */
373 cl_git_pass(git_index_read(index, 0));
374 cl_assert(!git_index_is_dirty(index));
375
376 /* Index is dirty when we do an unforced read with dirty content */
377 cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
378 cl_assert(git_index_entrycount(index) == 1);
379 cl_assert(git_index_is_dirty(index));
380
381 cl_git_pass(git_index_read(index, 0));
382 cl_assert(git_index_is_dirty(index));
383
384 /* Index is clean when we force a read with dirty content */
385 cl_git_pass(git_index_read(index, 1));
386 cl_assert(!git_index_is_dirty(index));
387
388 git_index_free(index);
389 git_repository_free(repo);
390 }
391
392 void test_index_tests__dirty_fails_optionally(void)
393 {
394 git_repository *repo;
395 git_index *index;
396 git_index_entry entry = {{0}};
397
398 /* Index is not dirty after opening */
399 repo = cl_git_sandbox_init("testrepo");
400 cl_git_pass(git_repository_index(&index, repo));
401
402 /* Index is dirty after adding an entry */
403 entry.mode = GIT_FILEMODE_BLOB;
404 entry.path = "test.txt";
405 cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
406 cl_assert(git_index_is_dirty(index));
407
408 cl_git_pass(git_checkout_head(repo, NULL));
409
410 /* Index is dirty (again) after adding an entry */
411 entry.mode = GIT_FILEMODE_BLOB;
412 entry.path = "test.txt";
413 cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
414 cl_assert(git_index_is_dirty(index));
415
416 cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, 1));
417 cl_git_fail_with(GIT_EINDEXDIRTY, git_checkout_head(repo, NULL));
418
419 git_index_free(index);
420 cl_git_sandbox_cleanup();
421 }
422
423 void test_index_tests__add_frombuffer_reset_entry(void)
424 {
425 git_index *index;
426 git_repository *repo;
427 git_index_entry entry;
428 const git_index_entry *returned_entry;
429 git_filebuf file = GIT_FILEBUF_INIT;
430
431 git_oid id1;
432 git_blob *blob;
433 const char *old_content = "here\n";
434 const char *content = "hey there\n";
435
436 cl_set_cleanup(&cleanup_myrepo, NULL);
437
438 /* Intialize a new repository */
439 cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
440 cl_git_pass(git_repository_index(&index, repo));
441 cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
442 cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
443 cl_git_pass(git_filebuf_write(&file, old_content, strlen(old_content)));
444 cl_git_pass(git_filebuf_commit(&file));
445
446 /* Store the expected hash of the file/blob
447 * This has been generated by executing the following
448 * $ echo "hey there" | git hash-object --stdin
449 */
450 cl_git_pass(git_oid_fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"));
451
452 cl_git_pass(git_index_add_bypath(index, "test.txt"));
453
454 /* Add the new file to the index */
455 memset(&entry, 0x0, sizeof(git_index_entry));
456 entry.mode = GIT_FILEMODE_BLOB;
457 entry.path = "test.txt";
458 cl_git_pass(git_index_add_from_buffer(index, &entry,
459 content, strlen(content)));
460
461 /* Wow... it worked! */
462 cl_assert(git_index_entrycount(index) == 1);
463 returned_entry = git_index_get_byindex(index, 0);
464
465 /* And the built-in hashing mechanism worked as expected */
466 cl_assert_equal_oid(&id1, &returned_entry->id);
467 /* And mode is the one asked */
468 cl_assert_equal_i(GIT_FILEMODE_BLOB, returned_entry->mode);
469
470 /* Test access by path instead of index */
471 cl_assert((returned_entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
472 cl_assert_equal_oid(&id1, &returned_entry->id);
473 cl_assert_equal_i(0, returned_entry->dev);
474 cl_assert_equal_i(0, returned_entry->ino);
475 cl_assert_equal_i(0, returned_entry->uid);
476 cl_assert_equal_i(0, returned_entry->uid);
477 cl_assert_equal_i(10, returned_entry->file_size);
478
479 /* Test the blob is in the repository */
480 cl_git_pass(git_blob_lookup(&blob, repo, &id1));
481 cl_assert_equal_s(content, git_blob_rawcontent(blob));
482 git_blob_free(blob);
483
484 git_index_free(index);
485 git_repository_free(repo);
486 }
487
488 static void cleanup_1397(void *opaque)
489 {
490 GIT_UNUSED(opaque);
491 cl_git_sandbox_cleanup();
492 }
493
494 void test_index_tests__add_issue_1397(void)
495 {
496 git_index *index;
497 git_repository *repo;
498 const git_index_entry *entry;
499 git_oid id1;
500
501 cl_set_cleanup(&cleanup_1397, NULL);
502
503 repo = cl_git_sandbox_init("issue_1397");
504
505 cl_repo_set_bool(repo, "core.autocrlf", true);
506
507 /* Ensure we're the only guy in the room */
508 cl_git_pass(git_repository_index(&index, repo));
509
510 /* Store the expected hash of the file/blob
511 * This has been generated by executing the following
512 * $ git hash-object crlf_file.txt
513 */
514 cl_git_pass(git_oid_fromstr(&id1, "8312e0889a9cbab77c732b6bc39b51a683e3a318"));
515
516 /* Make sure the initial SHA-1 is correct */
517 cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
518 cl_assert_equal_oid(&id1, &entry->id);
519
520 /* Update the index */
521 cl_git_pass(git_index_add_bypath(index, "crlf_file.txt"));
522
523 /* Check the new SHA-1 */
524 cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
525 cl_assert_equal_oid(&id1, &entry->id);
526
527 git_index_free(index);
528 }
529
530 void test_index_tests__add_bypath_to_a_bare_repository_returns_EBAREPO(void)
531 {
532 git_repository *bare_repo;
533 git_index *index;
534
535 cl_git_pass(git_repository_open(&bare_repo, cl_fixture("testrepo.git")));
536 cl_git_pass(git_repository_index(&index, bare_repo));
537
538 cl_assert_equal_i(GIT_EBAREREPO, git_index_add_bypath(index, "test.txt"));
539
540 git_index_free(index);
541 git_repository_free(bare_repo);
542 }
543
544 static void assert_add_bypath_fails(git_repository *repo, const char *fn)
545 {
546 git_index *index;
547 git_buf path = GIT_BUF_INIT;
548
549 cl_git_pass(git_repository_index(&index, repo));
550 cl_assert(git_index_entrycount(index) == 0);
551
552 git_buf_joinpath(&path, "./invalid", fn);
553
554 cl_git_mkfile(path.ptr, NULL);
555 cl_git_fail(git_index_add_bypath(index, fn));
556 cl_must_pass(p_unlink(path.ptr));
557
558 cl_assert(git_index_entrycount(index) == 0);
559
560 git_buf_dispose(&path);
561 git_index_free(index);
562 }
563
564 /* Test that writing an invalid filename fails */
565 void test_index_tests__cannot_add_invalid_filename(void)
566 {
567 git_repository *repo;
568
569 p_mkdir("invalid", 0700);
570
571 cl_git_pass(git_repository_init(&repo, "./invalid", 0));
572 cl_must_pass(p_mkdir("./invalid/subdir", 0777));
573
574 /* cl_git_mkfile() needs the dir to exist */
575 if (!git_path_exists("./invalid/.GIT"))
576 cl_must_pass(p_mkdir("./invalid/.GIT", 0777));
577 if (!git_path_exists("./invalid/.GiT"))
578 cl_must_pass(p_mkdir("./invalid/.GiT", 0777));
579
580 assert_add_bypath_fails(repo, ".git/hello");
581 assert_add_bypath_fails(repo, ".GIT/hello");
582 assert_add_bypath_fails(repo, ".GiT/hello");
583 assert_add_bypath_fails(repo, "./.git/hello");
584 assert_add_bypath_fails(repo, "./foo");
585 assert_add_bypath_fails(repo, "./bar");
586 assert_add_bypath_fails(repo, "subdir/../bar");
587
588 git_repository_free(repo);
589
590 cl_fixture_cleanup("invalid");
591 }
592
593 static void assert_add_fails(git_repository *repo, const char *fn)
594 {
595 git_index *index;
596 git_buf path = GIT_BUF_INIT;
597 git_index_entry entry = {{0}};
598
599 cl_git_pass(git_repository_index(&index, repo));
600 cl_assert(git_index_entrycount(index) == 0);
601
602 entry.path = fn;
603 entry.mode = GIT_FILEMODE_BLOB;
604 cl_git_pass(git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"));
605
606 cl_git_fail(git_index_add(index, &entry));
607
608 cl_assert(git_index_entrycount(index) == 0);
609
610 git_buf_dispose(&path);
611 git_index_free(index);
612 }
613
614 /*
615 * Test that writing an invalid filename fails on filesystem
616 * specific protected names
617 */
618 void test_index_tests__cannot_add_protected_invalid_filename(void)
619 {
620 git_repository *repo;
621 git_index *index;
622
623 cl_must_pass(p_mkdir("invalid", 0700));
624
625 cl_git_pass(git_repository_init(&repo, "./invalid", 0));
626
627 /* add a file to the repository so we can reference it later */
628 cl_git_pass(git_repository_index(&index, repo));
629 cl_git_mkfile("invalid/dummy.txt", "");
630 cl_git_pass(git_index_add_bypath(index, "dummy.txt"));
631 cl_must_pass(p_unlink("invalid/dummy.txt"));
632 cl_git_pass(git_index_remove_bypath(index, "dummy.txt"));
633 git_index_free(index);
634
635 cl_repo_set_bool(repo, "core.protectHFS", true);
636 cl_repo_set_bool(repo, "core.protectNTFS", true);
637
638 assert_add_fails(repo, ".git./hello");
639 assert_add_fails(repo, ".git\xe2\x80\xad/hello");
640 assert_add_fails(repo, "git~1/hello");
641 assert_add_fails(repo, ".git\xe2\x81\xaf/hello");
642 assert_add_fails(repo, ".git::$INDEX_ALLOCATION/dummy-file");
643
644 git_repository_free(repo);
645
646 cl_fixture_cleanup("invalid");
647 }
648
649 static void replace_char(char *str, char in, char out)
650 {
651 char *c = str;
652
653 while (*c++)
654 if (*c == in)
655 *c = out;
656 }
657
658 static void assert_write_fails(git_repository *repo, const char *fn_orig)
659 {
660 git_index *index;
661 git_oid expected;
662 const git_index_entry *entry;
663 git_buf path = GIT_BUF_INIT;
664 char *fn;
665
666 cl_git_pass(git_repository_index(&index, repo));
667 cl_assert(git_index_entrycount(index) == 0);
668
669 /*
670 * Sneak a valid path into the index, we'll update it
671 * to an invalid path when we try to write the index.
672 */
673 fn = git__strdup(fn_orig);
674 replace_char(fn, '/', '_');
675 replace_char(fn, ':', '!');
676
677 git_buf_joinpath(&path, "./invalid", fn);
678
679 cl_git_mkfile(path.ptr, NULL);
680
681 cl_git_pass(git_index_add_bypath(index, fn));
682
683 cl_assert(entry = git_index_get_bypath(index, fn, 0));
684
685 /* kids, don't try this at home */
686 replace_char((char *)entry->path, '_', '/');
687 replace_char((char *)entry->path, '!', ':');
688
689 /* write-tree */
690 cl_git_fail(git_index_write_tree(&expected, index));
691
692 p_unlink(path.ptr);
693
694 cl_git_pass(git_index_remove_all(index, NULL, NULL, NULL));
695 git_buf_dispose(&path);
696 git_index_free(index);
697 git__free(fn);
698 }
699
700 void test_index_tests__write_tree_invalid_unowned_index(void)
701 {
702 git_index *idx;
703 git_repository *repo;
704 git_index_entry entry = {{0}};
705 git_oid tree_id;
706
707 cl_git_pass(git_index_new(&idx));
708
709 cl_git_pass(git_oid_fromstr(&entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318"));
710 entry.path = "foo";
711 entry.mode = GIT_FILEMODE_BLOB;
712 cl_git_pass(git_index_add(idx, &entry));
713
714 cl_git_pass(git_repository_init(&repo, "./invalid-id", 0));
715
716 cl_git_fail(git_index_write_tree_to(&tree_id, idx, repo));
717
718 git_index_free(idx);
719 git_repository_free(repo);
720
721 cl_fixture_cleanup("invalid-id");
722 }
723
724 /* Test that writing an invalid filename fails */
725 void test_index_tests__write_invalid_filename(void)
726 {
727 git_repository *repo;
728
729 p_mkdir("invalid", 0700);
730
731 cl_git_pass(git_repository_init(&repo, "./invalid", 0));
732
733 assert_write_fails(repo, ".git/hello");
734 assert_write_fails(repo, ".GIT/hello");
735 assert_write_fails(repo, ".GiT/hello");
736 assert_write_fails(repo, "./.git/hello");
737 assert_write_fails(repo, "./foo");
738 assert_write_fails(repo, "./bar");
739 assert_write_fails(repo, "foo/../bar");
740
741 git_repository_free(repo);
742
743 cl_fixture_cleanup("invalid");
744 }
745
746 void test_index_tests__honors_protect_filesystems(void)
747 {
748 git_repository *repo;
749
750 p_mkdir("invalid", 0700);
751
752 cl_git_pass(git_repository_init(&repo, "./invalid", 0));
753
754 cl_repo_set_bool(repo, "core.protectHFS", true);
755 cl_repo_set_bool(repo, "core.protectNTFS", true);
756
757 assert_write_fails(repo, ".git./hello");
758 assert_write_fails(repo, ".git\xe2\x80\xad/hello");
759 assert_write_fails(repo, "git~1/hello");
760 assert_write_fails(repo, ".git\xe2\x81\xaf/hello");
761 assert_write_fails(repo, ".git::$INDEX_ALLOCATION/dummy-file");
762
763 git_repository_free(repo);
764
765 cl_fixture_cleanup("invalid");
766 }
767
768 void test_index_tests__protectntfs_on_by_default(void)
769 {
770 git_repository *repo;
771
772 p_mkdir("invalid", 0700);
773
774 cl_git_pass(git_repository_init(&repo, "./invalid", 0));
775 assert_write_fails(repo, ".git./hello");
776 assert_write_fails(repo, "git~1/hello");
777
778 git_repository_free(repo);
779
780 cl_fixture_cleanup("invalid");
781 }
782
783 void test_index_tests__can_disable_protectntfs(void)
784 {
785 git_repository *repo;
786 git_index *index;
787
788 cl_must_pass(p_mkdir("valid", 0700));
789 cl_git_rewritefile("valid/git~1", "steal the shortname");
790
791 cl_git_pass(git_repository_init(&repo, "./valid", 0));
792 cl_git_pass(git_repository_index(&index, repo));
793 cl_repo_set_bool(repo, "core.protectNTFS", false);
794
795 cl_git_pass(git_index_add_bypath(index, "git~1"));
796
797 git_index_free(index);
798 git_repository_free(repo);
799
800 cl_fixture_cleanup("valid");
801 }
802
803 void test_index_tests__remove_entry(void)
804 {
805 git_repository *repo;
806 git_index *index;
807
808 p_mkdir("index_test", 0770);
809
810 cl_git_pass(git_repository_init(&repo, "index_test", 0));
811 cl_git_pass(git_repository_index(&index, repo));
812 cl_assert(git_index_entrycount(index) == 0);
813
814 cl_git_mkfile("index_test/hello", NULL);
815 cl_git_pass(git_index_add_bypath(index, "hello"));
816 cl_git_pass(git_index_write(index));
817
818 cl_git_pass(git_index_read(index, true)); /* reload */
819 cl_assert(git_index_entrycount(index) == 1);
820 cl_assert(git_index_get_bypath(index, "hello", 0) != NULL);
821
822 cl_git_pass(git_index_remove(index, "hello", 0));
823 cl_git_pass(git_index_write(index));
824
825 cl_git_pass(git_index_read(index, true)); /* reload */
826 cl_assert(git_index_entrycount(index) == 0);
827 cl_assert(git_index_get_bypath(index, "hello", 0) == NULL);
828
829 git_index_free(index);
830 git_repository_free(repo);
831 cl_fixture_cleanup("index_test");
832 }
833
834 void test_index_tests__remove_directory(void)
835 {
836 git_repository *repo;
837 git_index *index;
838
839 p_mkdir("index_test", 0770);
840
841 cl_git_pass(git_repository_init(&repo, "index_test", 0));
842 cl_git_pass(git_repository_index(&index, repo));
843 cl_assert_equal_i(0, (int)git_index_entrycount(index));
844
845 p_mkdir("index_test/a", 0770);
846 cl_git_mkfile("index_test/a/1.txt", NULL);
847 cl_git_mkfile("index_test/a/2.txt", NULL);
848 cl_git_mkfile("index_test/a/3.txt", NULL);
849 cl_git_mkfile("index_test/b.txt", NULL);
850
851 cl_git_pass(git_index_add_bypath(index, "a/1.txt"));
852 cl_git_pass(git_index_add_bypath(index, "a/2.txt"));
853 cl_git_pass(git_index_add_bypath(index, "a/3.txt"));
854 cl_git_pass(git_index_add_bypath(index, "b.txt"));
855 cl_git_pass(git_index_write(index));
856
857 cl_git_pass(git_index_read(index, true)); /* reload */
858 cl_assert_equal_i(4, (int)git_index_entrycount(index));
859 cl_assert(git_index_get_bypath(index, "a/1.txt", 0) != NULL);
860 cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
861 cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
862
863 cl_git_pass(git_index_remove(index, "a/1.txt", 0));
864 cl_git_pass(git_index_write(index));
865
866 cl_git_pass(git_index_read(index, true)); /* reload */
867 cl_assert_equal_i(3, (int)git_index_entrycount(index));
868 cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
869 cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
870 cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
871
872 cl_git_pass(git_index_remove_directory(index, "a", 0));
873 cl_git_pass(git_index_write(index));
874
875 cl_git_pass(git_index_read(index, true)); /* reload */
876 cl_assert_equal_i(1, (int)git_index_entrycount(index));
877 cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
878 cl_assert(git_index_get_bypath(index, "a/2.txt", 0) == NULL);
879 cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
880
881 git_index_free(index);
882 git_repository_free(repo);
883 cl_fixture_cleanup("index_test");
884 }
885
886 void test_index_tests__preserves_case(void)
887 {
888 git_repository *repo;
889 git_index *index;
890 const git_index_entry *entry;
891 int index_caps;
892
893 cl_set_cleanup(&cleanup_myrepo, NULL);
894
895 cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
896 cl_git_pass(git_repository_index(&index, repo));
897
898 index_caps = git_index_caps(index);
899
900 cl_git_rewritefile("myrepo/test.txt", "hey there\n");
901 cl_git_pass(git_index_add_bypath(index, "test.txt"));
902
903 cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt"));
904 cl_git_rewritefile("myrepo/TEST.txt", "hello again\n");
905 cl_git_pass(git_index_add_bypath(index, "TEST.txt"));
906
907 if (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE)
908 cl_assert_equal_i(1, (int)git_index_entrycount(index));
909 else
910 cl_assert_equal_i(2, (int)git_index_entrycount(index));
911
912 /* Test access by path instead of index */
913 cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
914 /* The path should *not* have changed without an explicit remove */
915 cl_assert(git__strcmp(entry->path, "test.txt") == 0);
916
917 cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL);
918 if (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE)
919 /* The path should *not* have changed without an explicit remove */
920 cl_assert(git__strcmp(entry->path, "test.txt") == 0);
921 else
922 cl_assert(git__strcmp(entry->path, "TEST.txt") == 0);
923
924 git_index_free(index);
925 git_repository_free(repo);
926 }
927
928 void test_index_tests__elocked(void)
929 {
930 git_repository *repo;
931 git_index *index;
932 git_filebuf file = GIT_FILEBUF_INIT;
933 const git_error *err;
934 int error;
935
936 cl_set_cleanup(&cleanup_myrepo, NULL);
937
938 cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
939 cl_git_pass(git_repository_index(&index, repo));
940
941 /* Lock the index file so we fail to lock it */
942 cl_git_pass(git_filebuf_open(&file, index->index_file_path, 0, 0666));
943 error = git_index_write(index);
944 cl_assert_equal_i(GIT_ELOCKED, error);
945
946 err = git_error_last();
947 cl_assert_equal_i(err->klass, GIT_ERROR_INDEX);
948
949 git_filebuf_cleanup(&file);
950 git_index_free(index);
951 git_repository_free(repo);
952 }
953
954 void test_index_tests__reload_from_disk(void)
955 {
956 git_repository *repo;
957 git_index *read_index;
958 git_index *write_index;
959
960 cl_set_cleanup(&cleanup_myrepo, NULL);
961
962 cl_git_pass(git_futils_mkdir("./myrepo", 0777, GIT_MKDIR_PATH));
963 cl_git_mkfile("./myrepo/a.txt", "a\n");
964 cl_git_mkfile("./myrepo/b.txt", "b\n");
965
966 cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
967 cl_git_pass(git_repository_index(&write_index, repo));
968 cl_assert_equal_i(false, write_index->on_disk);
969
970 cl_git_pass(git_index_open(&read_index, write_index->index_file_path));
971 cl_assert_equal_i(false, read_index->on_disk);
972
973 /* Stage two new files against the write_index */
974 cl_git_pass(git_index_add_bypath(write_index, "a.txt"));
975 cl_git_pass(git_index_add_bypath(write_index, "b.txt"));
976
977 cl_assert_equal_sz(2, git_index_entrycount(write_index));
978
979 /* Persist the index changes to disk */
980 cl_git_pass(git_index_write(write_index));
981 cl_assert_equal_i(true, write_index->on_disk);
982
983 /* Sync the changes back into the read_index */
984 cl_assert_equal_sz(0, git_index_entrycount(read_index));
985
986 cl_git_pass(git_index_read(read_index, true));
987 cl_assert_equal_i(true, read_index->on_disk);
988
989 cl_assert_equal_sz(2, git_index_entrycount(read_index));
990
991 /* Remove the index file from the filesystem */
992 cl_git_pass(p_unlink(write_index->index_file_path));
993
994 /* Sync the changes back into the read_index */
995 cl_git_pass(git_index_read(read_index, true));
996 cl_assert_equal_i(false, read_index->on_disk);
997 cl_assert_equal_sz(0, git_index_entrycount(read_index));
998
999 git_index_free(read_index);
1000 git_index_free(write_index);
1001 git_repository_free(repo);
1002 }
1003
1004 void test_index_tests__corrupted_extension(void)
1005 {
1006 git_index *index;
1007
1008 cl_git_fail_with(git_index_open(&index, TEST_INDEXBAD_PATH), GIT_ERROR);
1009 }
1010
1011 void test_index_tests__reload_while_ignoring_case(void)
1012 {
1013 git_index *index;
1014 unsigned int caps;
1015
1016 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
1017 cl_git_pass(git_vector_verify_sorted(&index->entries));
1018
1019 caps = git_index_caps(index);
1020 cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
1021 cl_git_pass(git_index_read(index, true));
1022 cl_git_pass(git_vector_verify_sorted(&index->entries));
1023 cl_assert(git_index_get_bypath(index, ".HEADER", 0));
1024 cl_assert_equal_p(NULL, git_index_get_bypath(index, ".header", 0));
1025
1026 cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
1027 cl_git_pass(git_index_read(index, true));
1028 cl_git_pass(git_vector_verify_sorted(&index->entries));
1029 cl_assert(git_index_get_bypath(index, ".HEADER", 0));
1030 cl_assert(git_index_get_bypath(index, ".header", 0));
1031
1032 git_index_free(index);
1033 }
1034
1035 void test_index_tests__change_icase_on_instance(void)
1036 {
1037 git_index *index;
1038 unsigned int caps;
1039 const git_index_entry *e;
1040
1041 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
1042 cl_git_pass(git_vector_verify_sorted(&index->entries));
1043
1044 caps = git_index_caps(index);
1045 cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
1046 cl_assert_equal_i(false, index->ignore_case);
1047 cl_git_pass(git_vector_verify_sorted(&index->entries));
1048 cl_assert(e = git_index_get_bypath(index, "src/common.h", 0));
1049 cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "SRC/Common.h", 0));
1050 cl_assert(e = git_index_get_bypath(index, "COPYING", 0));
1051 cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "copying", 0));
1052
1053 cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
1054 cl_assert_equal_i(true, index->ignore_case);
1055 cl_git_pass(git_vector_verify_sorted(&index->entries));
1056 cl_assert(e = git_index_get_bypath(index, "COPYING", 0));
1057 cl_assert_equal_s("COPYING", e->path);
1058 cl_assert(e = git_index_get_bypath(index, "copying", 0));
1059 cl_assert_equal_s("COPYING", e->path);
1060
1061 git_index_free(index);
1062 }
1063
1064 void test_index_tests__can_lock_index(void)
1065 {
1066 git_repository *repo;
1067 git_index *index;
1068 git_indexwriter one = GIT_INDEXWRITER_INIT,
1069 two = GIT_INDEXWRITER_INIT;
1070
1071 repo = cl_git_sandbox_init("testrepo.git");
1072
1073 cl_git_pass(git_repository_index(&index, repo));
1074 cl_git_pass(git_indexwriter_init(&one, index));
1075
1076 cl_git_fail_with(GIT_ELOCKED, git_indexwriter_init(&two, index));
1077 cl_git_fail_with(GIT_ELOCKED, git_index_write(index));
1078
1079 cl_git_pass(git_indexwriter_commit(&one));
1080
1081 cl_git_pass(git_index_write(index));
1082
1083 git_indexwriter_cleanup(&one);
1084 git_indexwriter_cleanup(&two);
1085 git_index_free(index);
1086 cl_git_sandbox_cleanup();
1087 }
1088
1089 void test_index_tests__can_iterate(void)
1090 {
1091 git_index *index;
1092 git_index_iterator *iterator;
1093 const git_index_entry *entry;
1094 size_t i, iterator_idx = 0, found = 0;
1095 int ret;
1096
1097 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
1098 cl_git_pass(git_index_iterator_new(&iterator, index));
1099
1100 cl_assert(git_vector_is_sorted(&iterator->snap));
1101
1102 for (i = 0; i < ARRAY_SIZE(test_entries); i++) {
1103 /* Advance iterator to next test entry index */
1104 do {
1105 ret = git_index_iterator_next(&entry, iterator);
1106
1107 if (ret == GIT_ITEROVER)
1108 cl_fail("iterator did not contain all test entries");
1109
1110 cl_git_pass(ret);
1111 } while (iterator_idx++ < test_entries[i].index);
1112
1113 cl_assert_equal_s(entry->path, test_entries[i].path);
1114 cl_assert_equal_i(entry->mtime.seconds, test_entries[i].mtime);
1115 cl_assert_equal_i(entry->file_size, test_entries[i].file_size);
1116 found++;
1117 }
1118
1119 while ((ret = git_index_iterator_next(&entry, iterator)) == 0)
1120 ;
1121
1122 if (ret != GIT_ITEROVER)
1123 cl_git_fail(ret);
1124
1125 cl_assert_equal_i(found, ARRAY_SIZE(test_entries));
1126
1127 git_index_iterator_free(iterator);
1128 git_index_free(index);
1129 }
1130
1131 void test_index_tests__can_modify_while_iterating(void)
1132 {
1133 git_index *index;
1134 git_index_iterator *iterator;
1135 const git_index_entry *entry;
1136 git_index_entry new_entry = {{0}};
1137 size_t expected = 0, seen = 0;
1138 int ret;
1139
1140 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
1141 cl_git_pass(git_index_iterator_new(&iterator, index));
1142
1143 expected = git_index_entrycount(index);
1144 cl_assert(git_vector_is_sorted(&iterator->snap));
1145
1146 /*
1147 * After we've counted the entries, add a new one and change another;
1148 * ensure that our iterator is backed by a snapshot and thus returns
1149 * the number of entries from when the iterator was created.
1150 */
1151 cl_git_pass(git_oid_fromstr(&new_entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318"));
1152 new_entry.path = "newfile";
1153 new_entry.mode = GIT_FILEMODE_BLOB;
1154 cl_git_pass(git_index_add(index, &new_entry));
1155
1156 cl_git_pass(git_oid_fromstr(&new_entry.id, "4141414141414141414141414141414141414141"));
1157 new_entry.path = "Makefile";
1158 new_entry.mode = GIT_FILEMODE_BLOB;
1159 cl_git_pass(git_index_add(index, &new_entry));
1160
1161 while (true) {
1162 ret = git_index_iterator_next(&entry, iterator);
1163
1164 if (ret == GIT_ITEROVER)
1165 break;
1166
1167 seen++;
1168 }
1169
1170 cl_assert_equal_i(expected, seen);
1171
1172 git_index_iterator_free(iterator);
1173 git_index_free(index);
1174 }