]> git.proxmox.com Git - libgit2.git/blob - tests/index/tests.c
New upstream version 0.28.1+dfsg.1
[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 git_off_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_frombuffer(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_frombuffer(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_frombuffer(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_frombuffer(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_frombuffer(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_frombuffer(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 add_invalid_filename(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__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 add_invalid_filename(repo, ".git/hello");
581 add_invalid_filename(repo, ".GIT/hello");
582 add_invalid_filename(repo, ".GiT/hello");
583 add_invalid_filename(repo, "./.git/hello");
584 add_invalid_filename(repo, "./foo");
585 add_invalid_filename(repo, "./bar");
586 add_invalid_filename(repo, "subdir/../bar");
587
588 git_repository_free(repo);
589
590 cl_fixture_cleanup("invalid");
591 }
592
593 static void replace_char(char *str, char in, char out)
594 {
595 char *c = str;
596
597 while (*c++)
598 if (*c == in)
599 *c = out;
600 }
601
602 static void write_invalid_filename(git_repository *repo, const char *fn_orig)
603 {
604 git_index *index;
605 git_oid expected;
606 const git_index_entry *entry;
607 git_buf path = GIT_BUF_INIT;
608 char *fn;
609
610 cl_git_pass(git_repository_index(&index, repo));
611 cl_assert(git_index_entrycount(index) == 0);
612
613 /*
614 * Sneak a valid path into the index, we'll update it
615 * to an invalid path when we try to write the index.
616 */
617 fn = git__strdup(fn_orig);
618 replace_char(fn, '/', '_');
619
620 git_buf_joinpath(&path, "./invalid", fn);
621
622 cl_git_mkfile(path.ptr, NULL);
623
624 cl_git_pass(git_index_add_bypath(index, fn));
625
626 cl_assert(entry = git_index_get_bypath(index, fn, 0));
627
628 /* kids, don't try this at home */
629 replace_char((char *)entry->path, '_', '/');
630
631 /* write-tree */
632 cl_git_fail(git_index_write_tree(&expected, index));
633
634 p_unlink(path.ptr);
635
636 cl_git_pass(git_index_remove_all(index, NULL, NULL, NULL));
637 git_buf_dispose(&path);
638 git_index_free(index);
639 git__free(fn);
640 }
641
642 void test_index_tests__write_tree_invalid_unowned_index(void)
643 {
644 git_index *idx;
645 git_repository *repo;
646 git_index_entry entry = {{0}};
647 git_oid tree_id;
648
649 cl_git_pass(git_index_new(&idx));
650
651 cl_git_pass(git_oid_fromstr(&entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318"));
652 entry.path = "foo";
653 entry.mode = GIT_FILEMODE_BLOB;
654 cl_git_pass(git_index_add(idx, &entry));
655
656 cl_git_pass(git_repository_init(&repo, "./invalid-id", 0));
657
658 cl_git_fail(git_index_write_tree_to(&tree_id, idx, repo));
659
660 git_index_free(idx);
661 git_repository_free(repo);
662
663 cl_fixture_cleanup("invalid-id");
664 }
665
666 /* Test that writing an invalid filename fails */
667 void test_index_tests__write_invalid_filename(void)
668 {
669 git_repository *repo;
670
671 p_mkdir("invalid", 0700);
672
673 cl_git_pass(git_repository_init(&repo, "./invalid", 0));
674
675 write_invalid_filename(repo, ".git/hello");
676 write_invalid_filename(repo, ".GIT/hello");
677 write_invalid_filename(repo, ".GiT/hello");
678 write_invalid_filename(repo, "./.git/hello");
679 write_invalid_filename(repo, "./foo");
680 write_invalid_filename(repo, "./bar");
681 write_invalid_filename(repo, "foo/../bar");
682
683 git_repository_free(repo);
684
685 cl_fixture_cleanup("invalid");
686 }
687
688 void test_index_tests__honors_protect_filesystems(void)
689 {
690 git_repository *repo;
691
692 p_mkdir("invalid", 0700);
693
694 cl_git_pass(git_repository_init(&repo, "./invalid", 0));
695
696 cl_repo_set_bool(repo, "core.protectHFS", true);
697 cl_repo_set_bool(repo, "core.protectNTFS", true);
698
699 write_invalid_filename(repo, ".git./hello");
700 write_invalid_filename(repo, ".git\xe2\x80\xad/hello");
701 write_invalid_filename(repo, "git~1/hello");
702 write_invalid_filename(repo, ".git\xe2\x81\xaf/hello");
703
704 git_repository_free(repo);
705
706 cl_fixture_cleanup("invalid");
707 }
708
709 void test_index_tests__remove_entry(void)
710 {
711 git_repository *repo;
712 git_index *index;
713
714 p_mkdir("index_test", 0770);
715
716 cl_git_pass(git_repository_init(&repo, "index_test", 0));
717 cl_git_pass(git_repository_index(&index, repo));
718 cl_assert(git_index_entrycount(index) == 0);
719
720 cl_git_mkfile("index_test/hello", NULL);
721 cl_git_pass(git_index_add_bypath(index, "hello"));
722 cl_git_pass(git_index_write(index));
723
724 cl_git_pass(git_index_read(index, true)); /* reload */
725 cl_assert(git_index_entrycount(index) == 1);
726 cl_assert(git_index_get_bypath(index, "hello", 0) != NULL);
727
728 cl_git_pass(git_index_remove(index, "hello", 0));
729 cl_git_pass(git_index_write(index));
730
731 cl_git_pass(git_index_read(index, true)); /* reload */
732 cl_assert(git_index_entrycount(index) == 0);
733 cl_assert(git_index_get_bypath(index, "hello", 0) == NULL);
734
735 git_index_free(index);
736 git_repository_free(repo);
737 cl_fixture_cleanup("index_test");
738 }
739
740 void test_index_tests__remove_directory(void)
741 {
742 git_repository *repo;
743 git_index *index;
744
745 p_mkdir("index_test", 0770);
746
747 cl_git_pass(git_repository_init(&repo, "index_test", 0));
748 cl_git_pass(git_repository_index(&index, repo));
749 cl_assert_equal_i(0, (int)git_index_entrycount(index));
750
751 p_mkdir("index_test/a", 0770);
752 cl_git_mkfile("index_test/a/1.txt", NULL);
753 cl_git_mkfile("index_test/a/2.txt", NULL);
754 cl_git_mkfile("index_test/a/3.txt", NULL);
755 cl_git_mkfile("index_test/b.txt", NULL);
756
757 cl_git_pass(git_index_add_bypath(index, "a/1.txt"));
758 cl_git_pass(git_index_add_bypath(index, "a/2.txt"));
759 cl_git_pass(git_index_add_bypath(index, "a/3.txt"));
760 cl_git_pass(git_index_add_bypath(index, "b.txt"));
761 cl_git_pass(git_index_write(index));
762
763 cl_git_pass(git_index_read(index, true)); /* reload */
764 cl_assert_equal_i(4, (int)git_index_entrycount(index));
765 cl_assert(git_index_get_bypath(index, "a/1.txt", 0) != NULL);
766 cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
767 cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
768
769 cl_git_pass(git_index_remove(index, "a/1.txt", 0));
770 cl_git_pass(git_index_write(index));
771
772 cl_git_pass(git_index_read(index, true)); /* reload */
773 cl_assert_equal_i(3, (int)git_index_entrycount(index));
774 cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
775 cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
776 cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
777
778 cl_git_pass(git_index_remove_directory(index, "a", 0));
779 cl_git_pass(git_index_write(index));
780
781 cl_git_pass(git_index_read(index, true)); /* reload */
782 cl_assert_equal_i(1, (int)git_index_entrycount(index));
783 cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
784 cl_assert(git_index_get_bypath(index, "a/2.txt", 0) == NULL);
785 cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
786
787 git_index_free(index);
788 git_repository_free(repo);
789 cl_fixture_cleanup("index_test");
790 }
791
792 void test_index_tests__preserves_case(void)
793 {
794 git_repository *repo;
795 git_index *index;
796 const git_index_entry *entry;
797 int index_caps;
798
799 cl_set_cleanup(&cleanup_myrepo, NULL);
800
801 cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
802 cl_git_pass(git_repository_index(&index, repo));
803
804 index_caps = git_index_caps(index);
805
806 cl_git_rewritefile("myrepo/test.txt", "hey there\n");
807 cl_git_pass(git_index_add_bypath(index, "test.txt"));
808
809 cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt"));
810 cl_git_rewritefile("myrepo/TEST.txt", "hello again\n");
811 cl_git_pass(git_index_add_bypath(index, "TEST.txt"));
812
813 if (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE)
814 cl_assert_equal_i(1, (int)git_index_entrycount(index));
815 else
816 cl_assert_equal_i(2, (int)git_index_entrycount(index));
817
818 /* Test access by path instead of index */
819 cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
820 /* The path should *not* have changed without an explicit remove */
821 cl_assert(git__strcmp(entry->path, "test.txt") == 0);
822
823 cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL);
824 if (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE)
825 /* The path should *not* have changed without an explicit remove */
826 cl_assert(git__strcmp(entry->path, "test.txt") == 0);
827 else
828 cl_assert(git__strcmp(entry->path, "TEST.txt") == 0);
829
830 git_index_free(index);
831 git_repository_free(repo);
832 }
833
834 void test_index_tests__elocked(void)
835 {
836 git_repository *repo;
837 git_index *index;
838 git_filebuf file = GIT_FILEBUF_INIT;
839 const git_error *err;
840 int error;
841
842 cl_set_cleanup(&cleanup_myrepo, NULL);
843
844 cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
845 cl_git_pass(git_repository_index(&index, repo));
846
847 /* Lock the index file so we fail to lock it */
848 cl_git_pass(git_filebuf_open(&file, index->index_file_path, 0, 0666));
849 error = git_index_write(index);
850 cl_assert_equal_i(GIT_ELOCKED, error);
851
852 err = git_error_last();
853 cl_assert_equal_i(err->klass, GIT_ERROR_INDEX);
854
855 git_filebuf_cleanup(&file);
856 git_index_free(index);
857 git_repository_free(repo);
858 }
859
860 void test_index_tests__reload_from_disk(void)
861 {
862 git_repository *repo;
863 git_index *read_index;
864 git_index *write_index;
865
866 cl_set_cleanup(&cleanup_myrepo, NULL);
867
868 cl_git_pass(git_futils_mkdir("./myrepo", 0777, GIT_MKDIR_PATH));
869 cl_git_mkfile("./myrepo/a.txt", "a\n");
870 cl_git_mkfile("./myrepo/b.txt", "b\n");
871
872 cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
873 cl_git_pass(git_repository_index(&write_index, repo));
874 cl_assert_equal_i(false, write_index->on_disk);
875
876 cl_git_pass(git_index_open(&read_index, write_index->index_file_path));
877 cl_assert_equal_i(false, read_index->on_disk);
878
879 /* Stage two new files against the write_index */
880 cl_git_pass(git_index_add_bypath(write_index, "a.txt"));
881 cl_git_pass(git_index_add_bypath(write_index, "b.txt"));
882
883 cl_assert_equal_sz(2, git_index_entrycount(write_index));
884
885 /* Persist the index changes to disk */
886 cl_git_pass(git_index_write(write_index));
887 cl_assert_equal_i(true, write_index->on_disk);
888
889 /* Sync the changes back into the read_index */
890 cl_assert_equal_sz(0, git_index_entrycount(read_index));
891
892 cl_git_pass(git_index_read(read_index, true));
893 cl_assert_equal_i(true, read_index->on_disk);
894
895 cl_assert_equal_sz(2, git_index_entrycount(read_index));
896
897 /* Remove the index file from the filesystem */
898 cl_git_pass(p_unlink(write_index->index_file_path));
899
900 /* Sync the changes back into the read_index */
901 cl_git_pass(git_index_read(read_index, true));
902 cl_assert_equal_i(false, read_index->on_disk);
903 cl_assert_equal_sz(0, git_index_entrycount(read_index));
904
905 git_index_free(read_index);
906 git_index_free(write_index);
907 git_repository_free(repo);
908 }
909
910 void test_index_tests__corrupted_extension(void)
911 {
912 git_index *index;
913
914 cl_git_fail_with(git_index_open(&index, TEST_INDEXBAD_PATH), GIT_ERROR);
915 }
916
917 void test_index_tests__reload_while_ignoring_case(void)
918 {
919 git_index *index;
920 unsigned int caps;
921
922 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
923 cl_git_pass(git_vector_verify_sorted(&index->entries));
924
925 caps = git_index_caps(index);
926 cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
927 cl_git_pass(git_index_read(index, true));
928 cl_git_pass(git_vector_verify_sorted(&index->entries));
929 cl_assert(git_index_get_bypath(index, ".HEADER", 0));
930 cl_assert_equal_p(NULL, git_index_get_bypath(index, ".header", 0));
931
932 cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
933 cl_git_pass(git_index_read(index, true));
934 cl_git_pass(git_vector_verify_sorted(&index->entries));
935 cl_assert(git_index_get_bypath(index, ".HEADER", 0));
936 cl_assert(git_index_get_bypath(index, ".header", 0));
937
938 git_index_free(index);
939 }
940
941 void test_index_tests__change_icase_on_instance(void)
942 {
943 git_index *index;
944 unsigned int caps;
945 const git_index_entry *e;
946
947 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
948 cl_git_pass(git_vector_verify_sorted(&index->entries));
949
950 caps = git_index_caps(index);
951 cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
952 cl_assert_equal_i(false, index->ignore_case);
953 cl_git_pass(git_vector_verify_sorted(&index->entries));
954 cl_assert(e = git_index_get_bypath(index, "src/common.h", 0));
955 cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "SRC/Common.h", 0));
956 cl_assert(e = git_index_get_bypath(index, "COPYING", 0));
957 cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "copying", 0));
958
959 cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
960 cl_assert_equal_i(true, index->ignore_case);
961 cl_git_pass(git_vector_verify_sorted(&index->entries));
962 cl_assert(e = git_index_get_bypath(index, "COPYING", 0));
963 cl_assert_equal_s("COPYING", e->path);
964 cl_assert(e = git_index_get_bypath(index, "copying", 0));
965 cl_assert_equal_s("COPYING", e->path);
966
967 git_index_free(index);
968 }
969
970 void test_index_tests__can_lock_index(void)
971 {
972 git_repository *repo;
973 git_index *index;
974 git_indexwriter one = GIT_INDEXWRITER_INIT,
975 two = GIT_INDEXWRITER_INIT;
976
977 repo = cl_git_sandbox_init("testrepo.git");
978
979 cl_git_pass(git_repository_index(&index, repo));
980 cl_git_pass(git_indexwriter_init(&one, index));
981
982 cl_git_fail_with(GIT_ELOCKED, git_indexwriter_init(&two, index));
983 cl_git_fail_with(GIT_ELOCKED, git_index_write(index));
984
985 cl_git_pass(git_indexwriter_commit(&one));
986
987 cl_git_pass(git_index_write(index));
988
989 git_indexwriter_cleanup(&one);
990 git_indexwriter_cleanup(&two);
991 git_index_free(index);
992 cl_git_sandbox_cleanup();
993 }
994
995 void test_index_tests__can_iterate(void)
996 {
997 git_index *index;
998 git_index_iterator *iterator;
999 const git_index_entry *entry;
1000 size_t i, iterator_idx = 0, found = 0;
1001 int ret;
1002
1003 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
1004 cl_git_pass(git_index_iterator_new(&iterator, index));
1005
1006 cl_assert(git_vector_is_sorted(&iterator->snap));
1007
1008 for (i = 0; i < ARRAY_SIZE(test_entries); i++) {
1009 /* Advance iterator to next test entry index */
1010 do {
1011 ret = git_index_iterator_next(&entry, iterator);
1012
1013 if (ret == GIT_ITEROVER)
1014 cl_fail("iterator did not contain all test entries");
1015
1016 cl_git_pass(ret);
1017 } while (iterator_idx++ < test_entries[i].index);
1018
1019 cl_assert_equal_s(entry->path, test_entries[i].path);
1020 cl_assert_equal_i(entry->mtime.seconds, test_entries[i].mtime);
1021 cl_assert_equal_i(entry->file_size, test_entries[i].file_size);
1022 found++;
1023 }
1024
1025 while ((ret = git_index_iterator_next(&entry, iterator)) == 0)
1026 ;
1027
1028 if (ret != GIT_ITEROVER)
1029 cl_git_fail(ret);
1030
1031 cl_assert_equal_i(found, ARRAY_SIZE(test_entries));
1032
1033 git_index_iterator_free(iterator);
1034 git_index_free(index);
1035 }
1036
1037 void test_index_tests__can_modify_while_iterating(void)
1038 {
1039 git_index *index;
1040 git_index_iterator *iterator;
1041 const git_index_entry *entry;
1042 git_index_entry new_entry = {{0}};
1043 size_t expected = 0, seen = 0;
1044 int ret;
1045
1046 cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
1047 cl_git_pass(git_index_iterator_new(&iterator, index));
1048
1049 expected = git_index_entrycount(index);
1050 cl_assert(git_vector_is_sorted(&iterator->snap));
1051
1052 /*
1053 * After we've counted the entries, add a new one and change another;
1054 * ensure that our iterator is backed by a snapshot and thus returns
1055 * the number of entries from when the iterator was created.
1056 */
1057 cl_git_pass(git_oid_fromstr(&new_entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318"));
1058 new_entry.path = "newfile";
1059 new_entry.mode = GIT_FILEMODE_BLOB;
1060 cl_git_pass(git_index_add(index, &new_entry));
1061
1062 cl_git_pass(git_oid_fromstr(&new_entry.id, "4141414141414141414141414141414141414141"));
1063 new_entry.path = "Makefile";
1064 new_entry.mode = GIT_FILEMODE_BLOB;
1065 cl_git_pass(git_index_add(index, &new_entry));
1066
1067 while (true) {
1068 ret = git_index_iterator_next(&entry, iterator);
1069
1070 if (ret == GIT_ITEROVER)
1071 break;
1072
1073 seen++;
1074 }
1075
1076 cl_assert_equal_i(expected, seen);
1077
1078 git_index_iterator_free(iterator);
1079 git_index_free(index);
1080 }