summaryrefslogtreecommitdiff
path: root/tests/index
diff options
context:
space:
mode:
authorBen Straub <bs@github.com>2013-11-14 14:05:52 -0800
committerBen Straub <bs@github.com>2013-11-14 14:05:52 -0800
commit1782038144ef3413831801bb9c2f3038a84ac6f4 (patch)
treef074cc30890a20f5418c10fae1815ca516588a27 /tests/index
parent7b947bf5cc59eefa83c28eb5f5fd8434207ebb8b (diff)
downloadlibgit2-1782038144ef3413831801bb9c2f3038a84ac6f4.tar.gz
Rename tests-clar to tests
Diffstat (limited to 'tests/index')
-rw-r--r--tests/index/addall.c258
-rw-r--r--tests/index/conflicts.c287
-rw-r--r--tests/index/filemodes.c154
-rw-r--r--tests/index/inmemory.c22
-rw-r--r--tests/index/names.c148
-rw-r--r--tests/index/read_tree.c46
-rw-r--r--tests/index/rename.c50
-rw-r--r--tests/index/reuc.c372
-rw-r--r--tests/index/stage.c62
-rw-r--r--tests/index/tests.c537
10 files changed, 1936 insertions, 0 deletions
diff --git a/tests/index/addall.c b/tests/index/addall.c
new file mode 100644
index 000000000..44c51279d
--- /dev/null
+++ b/tests/index/addall.c
@@ -0,0 +1,258 @@
+#include "clar_libgit2.h"
+#include "../status/status_helpers.h"
+#include "posix.h"
+#include "fileops.h"
+
+git_repository *g_repo = NULL;
+
+void test_index_addall__initialize(void)
+{
+}
+
+void test_index_addall__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+#define STATUS_INDEX_FLAGS \
+ (GIT_STATUS_INDEX_NEW | GIT_STATUS_INDEX_MODIFIED | \
+ GIT_STATUS_INDEX_DELETED | GIT_STATUS_INDEX_RENAMED | \
+ GIT_STATUS_INDEX_TYPECHANGE)
+
+#define STATUS_WT_FLAGS \
+ (GIT_STATUS_WT_NEW | GIT_STATUS_WT_MODIFIED | \
+ GIT_STATUS_WT_DELETED | GIT_STATUS_WT_TYPECHANGE | \
+ GIT_STATUS_WT_RENAMED)
+
+typedef struct {
+ size_t index_adds;
+ size_t index_dels;
+ size_t index_mods;
+ size_t wt_adds;
+ size_t wt_dels;
+ size_t wt_mods;
+ size_t ignores;
+} index_status_counts;
+
+static int index_status_cb(
+ const char *path, unsigned int status_flags, void *payload)
+{
+ index_status_counts *vals = payload;
+
+ /* cb_status__print(path, status_flags, NULL); */
+
+ GIT_UNUSED(path);
+
+ if (status_flags & GIT_STATUS_INDEX_NEW)
+ vals->index_adds++;
+ if (status_flags & GIT_STATUS_INDEX_MODIFIED)
+ vals->index_mods++;
+ if (status_flags & GIT_STATUS_INDEX_DELETED)
+ vals->index_dels++;
+ if (status_flags & GIT_STATUS_INDEX_TYPECHANGE)
+ vals->index_mods++;
+
+ if (status_flags & GIT_STATUS_WT_NEW)
+ vals->wt_adds++;
+ if (status_flags & GIT_STATUS_WT_MODIFIED)
+ vals->wt_mods++;
+ if (status_flags & GIT_STATUS_WT_DELETED)
+ vals->wt_dels++;
+ if (status_flags & GIT_STATUS_WT_TYPECHANGE)
+ vals->wt_mods++;
+
+ if (status_flags & GIT_STATUS_IGNORED)
+ vals->ignores++;
+
+ return 0;
+}
+
+static void check_status_at_line(
+ git_repository *repo,
+ size_t index_adds, size_t index_dels, size_t index_mods,
+ size_t wt_adds, size_t wt_dels, size_t wt_mods, size_t ignores,
+ const char *file, int line)
+{
+ index_status_counts vals;
+
+ memset(&vals, 0, sizeof(vals));
+
+ cl_git_pass(git_status_foreach(repo, index_status_cb, &vals));
+
+ clar__assert_equal(
+ file,line,"wrong index adds", 1, "%"PRIuZ, index_adds, vals.index_adds);
+ clar__assert_equal(
+ file,line,"wrong index dels", 1, "%"PRIuZ, index_dels, vals.index_dels);
+ clar__assert_equal(
+ file,line,"wrong index mods", 1, "%"PRIuZ, index_mods, vals.index_mods);
+ clar__assert_equal(
+ file,line,"wrong workdir adds", 1, "%"PRIuZ, wt_adds, vals.wt_adds);
+ clar__assert_equal(
+ file,line,"wrong workdir dels", 1, "%"PRIuZ, wt_dels, vals.wt_dels);
+ clar__assert_equal(
+ file,line,"wrong workdir mods", 1, "%"PRIuZ, wt_mods, vals.wt_mods);
+ clar__assert_equal(
+ file,line,"wrong ignores", 1, "%"PRIuZ, ignores, vals.ignores);
+}
+
+#define check_status(R,IA,ID,IM,WA,WD,WM,IG) \
+ check_status_at_line(R,IA,ID,IM,WA,WD,WM,IG,__FILE__,__LINE__)
+
+static void check_stat_data(git_index *index, const char *path, bool match)
+{
+ const git_index_entry *entry;
+ struct stat st;
+
+ cl_must_pass(p_lstat(path, &st));
+
+ /* skip repo base dir name */
+ while (*path != '/')
+ ++path;
+ ++path;
+
+ entry = git_index_get_bypath(index, path, 0);
+ cl_assert(entry);
+
+ if (match) {
+ cl_assert(st.st_ctime == entry->ctime.seconds);
+ cl_assert(st.st_mtime == entry->mtime.seconds);
+ cl_assert(st.st_size == entry->file_size);
+ cl_assert(st.st_uid == entry->uid);
+ cl_assert(st.st_gid == entry->gid);
+ cl_assert_equal_i_fmt(
+ GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o");
+ if (cl_is_chmod_supported())
+ cl_assert_equal_b(
+ GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode));
+ } else {
+ /* most things will still match */
+ cl_assert(st.st_size != entry->file_size);
+ /* would check mtime, but with second resolution it won't work :( */
+ }
+}
+
+void test_index_addall__repo_lifecycle(void)
+{
+ int error;
+ git_index *index;
+ git_strarray paths = { NULL, 0 };
+ char *strs[1];
+
+ cl_git_pass(git_repository_init(&g_repo, "addall", false));
+ check_status(g_repo, 0, 0, 0, 0, 0, 0, 0);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_mkfile("addall/file.foo", "a file");
+ check_status(g_repo, 0, 0, 0, 1, 0, 0, 0);
+
+ cl_git_mkfile("addall/.gitignore", "*.foo\n");
+ check_status(g_repo, 0, 0, 0, 1, 0, 0, 1);
+
+ cl_git_mkfile("addall/file.bar", "another file");
+ check_status(g_repo, 0, 0, 0, 2, 0, 0, 1);
+
+ strs[0] = "file.*";
+ paths.strings = strs;
+ paths.count = 1;
+
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_stat_data(index, "addall/file.bar", true);
+ check_status(g_repo, 1, 0, 0, 1, 0, 0, 1);
+
+ cl_git_rewritefile("addall/file.bar", "new content for file");
+ check_stat_data(index, "addall/file.bar", false);
+ check_status(g_repo, 1, 0, 0, 1, 0, 1, 1);
+
+ cl_git_mkfile("addall/file.zzz", "yet another one");
+ cl_git_mkfile("addall/other.zzz", "yet another one");
+ cl_git_mkfile("addall/more.zzz", "yet another one");
+ check_status(g_repo, 1, 0, 0, 4, 0, 1, 1);
+
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_stat_data(index, "addall/file.bar", true);
+ check_status(g_repo, 1, 0, 0, 4, 0, 0, 1);
+
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_stat_data(index, "addall/file.zzz", true);
+ check_status(g_repo, 2, 0, 0, 3, 0, 0, 1);
+
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "first commit");
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1);
+
+ /* attempt to add an ignored file - does nothing */
+ strs[0] = "file.foo";
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1);
+
+ /* add with check - should generate error */
+ error = git_index_add_all(
+ index, &paths, GIT_INDEX_ADD_CHECK_PATHSPEC, NULL, NULL);
+ cl_assert_equal_i(GIT_EINVALIDSPEC, error);
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1);
+
+ /* add with force - should allow */
+ cl_git_pass(git_index_add_all(
+ index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL));
+ check_stat_data(index, "addall/file.foo", true);
+ check_status(g_repo, 1, 0, 0, 3, 0, 0, 0);
+
+ /* now it's in the index, so regular add should work */
+ cl_git_rewritefile("addall/file.foo", "new content for file");
+ check_stat_data(index, "addall/file.foo", false);
+ check_status(g_repo, 1, 0, 0, 3, 0, 1, 0);
+
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_stat_data(index, "addall/file.foo", true);
+ check_status(g_repo, 1, 0, 0, 3, 0, 0, 0);
+
+ cl_git_pass(git_index_add_bypath(index, "more.zzz"));
+ check_stat_data(index, "addall/more.zzz", true);
+ check_status(g_repo, 2, 0, 0, 2, 0, 0, 0);
+
+ cl_git_rewritefile("addall/file.zzz", "new content for file");
+ check_status(g_repo, 2, 0, 0, 2, 0, 1, 0);
+
+ cl_git_pass(git_index_add_bypath(index, "file.zzz"));
+ check_stat_data(index, "addall/file.zzz", true);
+ check_status(g_repo, 2, 0, 1, 2, 0, 0, 0);
+
+ strs[0] = "*.zzz";
+ cl_git_pass(git_index_remove_all(index, &paths, NULL, NULL));
+ check_status(g_repo, 1, 1, 0, 4, 0, 0, 0);
+
+ cl_git_pass(git_index_add_bypath(index, "file.zzz"));
+ check_status(g_repo, 1, 0, 1, 3, 0, 0, 0);
+
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "second commit");
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 0);
+
+ cl_must_pass(p_unlink("addall/file.zzz"));
+ check_status(g_repo, 0, 0, 0, 3, 1, 0, 0);
+
+ /* update_all should be able to remove entries */
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_status(g_repo, 0, 1, 0, 3, 0, 0, 0);
+
+ strs[0] = "*";
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_status(g_repo, 3, 1, 0, 0, 0, 0, 0);
+
+ /* must be able to remove at any position while still updating other files */
+ cl_must_pass(p_unlink("addall/.gitignore"));
+ cl_git_rewritefile("addall/file.zzz", "reconstructed file");
+ cl_git_rewritefile("addall/more.zzz", "altered file reality");
+ check_status(g_repo, 3, 1, 0, 1, 1, 1, 0);
+
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_status(g_repo, 2, 1, 0, 1, 0, 0, 0);
+ /* this behavior actually matches 'git add -u' where "file.zzz" has
+ * been removed from the index, so when you go to update, even though
+ * it exists in the HEAD, it is not re-added to the index, leaving it
+ * as a DELETE when comparing HEAD to index and as an ADD comparing
+ * index to worktree
+ */
+
+ git_index_free(index);
+}
diff --git a/tests/index/conflicts.c b/tests/index/conflicts.c
new file mode 100644
index 000000000..6311b3a75
--- /dev/null
+++ b/tests/index/conflicts.c
@@ -0,0 +1,287 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/repository.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define CONFLICTS_ONE_ANCESTOR_OID "1f85ca51b8e0aac893a621b61a9c2661d6aa6d81"
+#define CONFLICTS_ONE_OUR_OID "6aea5f295304c36144ad6e9247a291b7f8112399"
+#define CONFLICTS_ONE_THEIR_OID "516bd85f78061e09ccc714561d7b504672cb52da"
+
+#define CONFLICTS_TWO_ANCESTOR_OID "84af62840be1b1c47b778a8a249f3ff45155038c"
+#define CONFLICTS_TWO_OUR_OID "8b3f43d2402825c200f835ca1762413e386fd0b2"
+#define CONFLICTS_TWO_THEIR_OID "220bd62631c8cf7a83ef39c6b94595f00517211e"
+
+#define TEST_ANCESTOR_OID "f00ff00ff00ff00ff00ff00ff00ff00ff00ff00f"
+#define TEST_OUR_OID "b44bb44bb44bb44bb44bb44bb44bb44bb44bb44b"
+#define TEST_THEIR_OID "0123456789abcdef0123456789abcdef01234567"
+
+// Fixture setup and teardown
+void test_index_conflicts__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_conflicts__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_conflicts__add(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID);
+
+ our_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&our_entry.oid, TEST_OUR_OID);
+
+ their_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&their_entry.oid, TEST_THEIR_OID);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
+
+ cl_assert(git_index_entrycount(repo_index) == 11);
+}
+
+void test_index_conflicts__add_fixes_incorrect_stage(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ const git_index_entry *conflict_entry[3];
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID);
+
+ our_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&our_entry.oid, TEST_OUR_OID);
+
+ their_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&their_entry.oid, TEST_THEIR_OID);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
+
+ cl_assert(git_index_entrycount(repo_index) == 11);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], repo_index, "test-one.txt"));
+
+ cl_assert(git_index_entry_stage(conflict_entry[0]) == 1);
+ cl_assert(git_index_entry_stage(conflict_entry[1]) == 2);
+ cl_assert(git_index_entry_stage(conflict_entry[2]) == 3);
+}
+
+void test_index_conflicts__get(void)
+{
+ const git_index_entry *conflict_entry[3];
+ git_oid oid;
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "conflicts-one.txt"));
+
+ cl_assert_equal_s("conflicts-one.txt", conflict_entry[0]->path);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "conflicts-two.txt"));
+
+ cl_assert_equal_s("conflicts-two.txt", conflict_entry[0]->path);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0);
+}
+
+void test_index_conflicts__iterate(void)
+{
+ git_index_conflict_iterator *iterator;
+ const git_index_entry *conflict_entry[3];
+ git_oid oid;
+
+ cl_git_pass(git_index_conflict_iterator_new(&iterator, repo_index));
+
+ cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator));
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
+
+ cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator));
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
+
+ cl_assert(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator) == GIT_ITEROVER);
+
+ cl_assert(conflict_entry[0] == NULL);
+ cl_assert(conflict_entry[2] == NULL);
+ cl_assert(conflict_entry[2] == NULL);
+
+ git_index_conflict_iterator_free(iterator);
+}
+
+void test_index_conflicts__remove(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_pass(git_index_conflict_remove(repo_index, "conflicts-one.txt"));
+ cl_assert(git_index_entrycount(repo_index) == 5);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-one.txt") != 0);
+ }
+
+ cl_git_pass(git_index_conflict_remove(repo_index, "conflicts-two.txt"));
+ cl_assert(git_index_entrycount(repo_index) == 2);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-two.txt") != 0);
+ }
+}
+
+void test_index_conflicts__moved_to_reuc_on_add(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_mkfile("./mergedrepo/conflicts-one.txt", "new-file\n");
+
+ cl_git_pass(git_index_add_bypath(repo_index, "conflicts-one.txt"));
+
+ cl_assert(git_index_entrycount(repo_index) == 6);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+
+ if (strcmp(entry->path, "conflicts-one.txt") == 0)
+ cl_assert(git_index_entry_stage(entry) == 0);
+ }
+}
+
+void test_index_conflicts__moved_to_reuc_on_remove(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_pass(p_unlink("./mergedrepo/conflicts-one.txt"));
+
+ cl_git_pass(git_index_remove_bypath(repo_index, "conflicts-one.txt"));
+
+ cl_assert(git_index_entrycount(repo_index) == 5);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-one.txt") != 0);
+ }
+}
+
+void test_index_conflicts__remove_all_conflicts(void)
+{
+ size_t i;
+ const git_index_entry *entry;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_assert_equal_i(true, git_index_has_conflicts(repo_index));
+
+ git_index_conflict_cleanup(repo_index);
+
+ cl_assert_equal_i(false, git_index_has_conflicts(repo_index));
+
+ cl_assert(git_index_entrycount(repo_index) == 2);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(git_index_entry_stage(entry) == 0);
+ }
+}
+
+void test_index_conflicts__partial(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ const git_index_entry *conflict_entry[3];
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, NULL, NULL));
+ cl_assert(git_index_entrycount(repo_index) == 9);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "test-one.txt"));
+
+ cl_assert(git_oid_cmp(&ancestor_entry.oid, &conflict_entry[0]->oid) == 0);
+ cl_assert(conflict_entry[1] == NULL);
+ cl_assert(conflict_entry[2] == NULL);
+}
diff --git a/tests/index/filemodes.c b/tests/index/filemodes.c
new file mode 100644
index 000000000..013932696
--- /dev/null
+++ b/tests/index/filemodes.c
@@ -0,0 +1,154 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "posix.h"
+#include "index.h"
+
+static git_repository *g_repo = NULL;
+
+void test_index_filemodes__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("filemodes");
+}
+
+void test_index_filemodes__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_filemodes__read(void)
+{
+ git_index *index;
+ unsigned int i;
+ static bool expected[6] = { 0, 1, 0, 1, 0, 1 };
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert_equal_i(6, (int)git_index_entrycount(index));
+
+ for (i = 0; i < 6; ++i) {
+ const git_index_entry *entry = git_index_get_byindex(index, i);
+ cl_assert(entry != NULL);
+ cl_assert(((entry->mode & 0100) ? 1 : 0) == expected[i]);
+ }
+
+ git_index_free(index);
+}
+
+static void replace_file_with_mode(
+ const char *filename, const char *backup, unsigned int create_mode)
+{
+ git_buf path = GIT_BUF_INIT, content = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&path, "filemodes", filename));
+ cl_git_pass(git_buf_printf(&content, "%s as %08u (%d)",
+ filename, create_mode, rand()));
+
+ cl_git_pass(p_rename(path.ptr, backup));
+ cl_git_write2file(
+ path.ptr, content.ptr, content.size,
+ O_WRONLY|O_CREAT|O_TRUNC, create_mode);
+
+ git_buf_free(&path);
+ git_buf_free(&content);
+}
+
+#define add_and_check_mode(I,F,X) add_and_check_mode_(I,F,X,__FILE__,__LINE__)
+
+static void add_and_check_mode_(
+ git_index *index, const char *filename, unsigned int expect_mode,
+ const char *file, int line)
+{
+ size_t pos;
+ const git_index_entry *entry;
+
+ cl_git_pass(git_index_add_bypath(index, filename));
+
+ clar__assert(!git_index_find(&pos, index, filename),
+ file, line, "Cannot find index entry", NULL, 1);
+
+ entry = git_index_get_byindex(index, pos);
+
+ clar__assert_equal(file, line, "Expected mode does not match index",
+ 1, "%07o", (unsigned int)entry->mode, (unsigned int)expect_mode);
+}
+
+void test_index_filemodes__untrusted(void)
+{
+ git_index *index;
+
+ cl_repo_set_bool(g_repo, "core.filemode", false);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert((git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE) != 0);
+
+ /* 1 - add 0644 over existing 0644 -> expect 0644 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.0", 0644);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
+
+ /* 2 - add 0644 over existing 0755 -> expect 0755 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.0", 0644);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 3 - add 0755 over existing 0644 -> expect 0644 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.1", 0755);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
+
+ /* 4 - add 0755 over existing 0755 -> expect 0755 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 5 - add new 0644 -> expect 0644 */
+ cl_git_write2file("filemodes/new_off", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
+
+ /* 6 - add new 0755 -> expect 0644 if core.filemode == false */
+ cl_git_write2file("filemodes/new_on", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0755);
+ add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB);
+
+ git_index_free(index);
+}
+
+void test_index_filemodes__trusted(void)
+{
+ git_index *index;
+
+ /* Only run these tests on platforms where I can actually
+ * chmod a file and get the stat results I expect!
+ */
+ if (!cl_is_chmod_supported())
+ return;
+
+ cl_repo_set_bool(g_repo, "core.filemode", true);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert((git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE) == 0);
+
+ /* 1 - add 0644 over existing 0644 -> expect 0644 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.0", 0644);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
+
+ /* 2 - add 0644 over existing 0755 -> expect 0644 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.0", 0644);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB);
+
+ /* 3 - add 0755 over existing 0644 -> expect 0755 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.1", 0755);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 4 - add 0755 over existing 0755 -> expect 0755 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 5 - add new 0644 -> expect 0644 */
+ cl_git_write2file("filemodes/new_off", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
+
+ /* 6 - add 0755 -> expect 0755 */
+ cl_git_write2file("filemodes/new_on", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0755);
+ add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ git_index_free(index);
+}
diff --git a/tests/index/inmemory.c b/tests/index/inmemory.c
new file mode 100644
index 000000000..38e91e0fd
--- /dev/null
+++ b/tests/index/inmemory.c
@@ -0,0 +1,22 @@
+#include "clar_libgit2.h"
+
+void test_index_inmemory__can_create_an_inmemory_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index_new(&index));
+ cl_assert_equal_i(0, (int)git_index_entrycount(index));
+
+ git_index_free(index);
+}
+
+void test_index_inmemory__cannot_add_bypath_to_an_inmemory_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index_new(&index));
+
+ cl_assert_equal_i(GIT_ERROR, git_index_add_bypath(index, "test.txt"));
+
+ git_index_free(index);
+}
diff --git a/tests/index/names.c b/tests/index/names.c
new file mode 100644
index 000000000..9007b1b15
--- /dev/null
+++ b/tests/index/names.c
@@ -0,0 +1,148 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/sys/index.h"
+#include "git2/repository.h"
+#include "../reset/reset_helpers.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+// Fixture setup and teardown
+void test_index_names__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_names__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_names__add(void)
+{
+ const git_index_name_entry *conflict_name;
+
+ cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs"));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3"));
+
+ cl_assert(git_index_name_entrycount(repo_index) == 3);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 0);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours") == 0);
+ cl_assert(strcmp(conflict_name->theirs, "theirs") == 0);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 1);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours2") == 0);
+ cl_assert(conflict_name->theirs == NULL);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 2);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
+ cl_assert(conflict_name->ours == NULL);
+ cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
+}
+
+void test_index_names__roundtrip(void)
+{
+ const git_index_name_entry *conflict_name;
+
+ cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs"));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3"));
+
+ cl_git_pass(git_index_write(repo_index));
+ git_index_clear(repo_index);
+ cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+ cl_git_pass(git_index_read(repo_index, true));
+ cl_assert(git_index_name_entrycount(repo_index) == 3);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 0);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours") == 0);
+ cl_assert(strcmp(conflict_name->theirs, "theirs") == 0);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 1);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours2") == 0);
+ cl_assert(conflict_name->theirs == NULL);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 2);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
+ cl_assert(conflict_name->ours == NULL);
+ cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
+}
+
+void test_index_names__cleaned_on_reset_hard(void)
+{
+ git_object *target;
+
+ retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+ test_index_names__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
+ cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+ git_object_free(target);
+}
+
+void test_index_names__cleaned_on_reset_mixed(void)
+{
+ git_object *target;
+
+ retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+ test_index_names__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED));
+ cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+ git_object_free(target);
+}
+
+void test_index_names__cleaned_on_checkout_tree(void)
+{
+ git_oid oid;
+ git_object *obj;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_names__add();
+ git_reference_name_to_id(&oid, repo, "refs/heads/master");
+ git_object_lookup(&obj, repo, &oid, GIT_OBJ_ANY);
+ git_checkout_tree(repo, obj, &opts);
+ cl_assert_equal_sz(0, git_index_name_entrycount(repo_index));
+
+ git_object_free(obj);
+}
+
+void test_index_names__cleaned_on_checkout_head(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_names__add();
+ git_checkout_head(repo, &opts);
+ cl_assert_equal_sz(0, git_index_name_entrycount(repo_index));
+}
+
+void test_index_names__retained_on_checkout_index(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_names__add();
+ git_checkout_index(repo, repo_index, &opts);
+ cl_assert(git_index_name_entrycount(repo_index) > 0);
+}
diff --git a/tests/index/read_tree.c b/tests/index/read_tree.c
new file mode 100644
index 000000000..6c6b40121
--- /dev/null
+++ b/tests/index/read_tree.c
@@ -0,0 +1,46 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+/* Test that reading and writing a tree is a no-op */
+void test_index_read_tree__read_write_involution(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_oid tree_oid;
+ git_tree *tree;
+ git_oid expected;
+
+ p_mkdir("read_tree", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./read_tree", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ p_mkdir("./read_tree/abc", 0700);
+
+ /* Sort order: '-' < '/' < '_' */
+ cl_git_mkfile("./read_tree/abc-d", NULL);
+ cl_git_mkfile("./read_tree/abc/d", NULL);
+ cl_git_mkfile("./read_tree/abc_d", NULL);
+
+ cl_git_pass(git_index_add_bypath(index, "abc-d"));
+ cl_git_pass(git_index_add_bypath(index, "abc_d"));
+ cl_git_pass(git_index_add_bypath(index, "abc/d"));
+
+ /* write-tree */
+ cl_git_pass(git_index_write_tree(&expected, index));
+
+ /* read-tree */
+ git_tree_lookup(&tree, repo, &expected);
+ cl_git_pass(git_index_read_tree(index, tree));
+ git_tree_free(tree);
+
+ cl_git_pass(git_index_write_tree(&tree_oid, index));
+ cl_assert(git_oid_cmp(&expected, &tree_oid) == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("read_tree");
+}
diff --git a/tests/index/rename.c b/tests/index/rename.c
new file mode 100644
index 000000000..4deef1332
--- /dev/null
+++ b/tests/index/rename.c
@@ -0,0 +1,50 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+void test_index_rename__single_file(void)
+{
+ git_repository *repo;
+ git_index *index;
+ size_t position;
+ git_oid expected;
+ const git_index_entry *entry;
+
+ p_mkdir("rename", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./rename", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ cl_git_mkfile("./rename/lame.name.txt", "new_file\n");
+
+ /* This should add a new blob to the object database in 'd4/fa8600b4f37d7516bef4816ae2c64dbf029e3a' */
+ cl_git_pass(git_index_add_bypath(index, "lame.name.txt"));
+ cl_assert(git_index_entrycount(index) == 1);
+
+ cl_git_pass(git_oid_fromstr(&expected, "d4fa8600b4f37d7516bef4816ae2c64dbf029e3a"));
+
+ cl_assert(!git_index_find(&position, index, "lame.name.txt"));
+
+ entry = git_index_get_byindex(index, position);
+ cl_assert(git_oid_cmp(&expected, &entry->oid) == 0);
+
+ /* This removes the entry from the index, but not from the object database */
+ cl_git_pass(git_index_remove(index, "lame.name.txt", 0));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ p_rename("./rename/lame.name.txt", "./rename/fancy.name.txt");
+
+ cl_git_pass(git_index_add_bypath(index, "fancy.name.txt"));
+ cl_assert(git_index_entrycount(index) == 1);
+
+ cl_assert(!git_index_find(&position, index, "fancy.name.txt"));
+
+ entry = git_index_get_byindex(index, position);
+ cl_assert(git_oid_cmp(&expected, &entry->oid) == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("rename");
+}
diff --git a/tests/index/reuc.c b/tests/index/reuc.c
new file mode 100644
index 000000000..a18d5602e
--- /dev/null
+++ b/tests/index/reuc.c
@@ -0,0 +1,372 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/sys/index.h"
+#include "git2/repository.h"
+#include "../reset/reset_helpers.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define ONE_ANCESTOR_OID "478871385b9cd03908c5383acfd568bef023c6b3"
+#define ONE_OUR_OID "4458b8bc9e72b6c8755ae456f60e9844d0538d8c"
+#define ONE_THEIR_OID "8b72416545c7e761b64cecad4f1686eae4078aa8"
+
+#define TWO_ANCESTOR_OID "9d81f82fccc7dcd7de7a1ffead1815294c2e092c"
+#define TWO_OUR_OID "8f3c06cff9a83757cec40c80bc9bf31a2582bde9"
+#define TWO_THEIR_OID "887b153b165d32409c70163e0f734c090f12f673"
+
+// Fixture setup and teardown
+void test_index_reuc__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_reuc__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_reuc__add(void)
+{
+ git_oid ancestor_oid, our_oid, their_oid;
+ const git_index_reuc_entry *reuc;
+
+ git_oid_fromstr(&ancestor_oid, ONE_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, ONE_OUR_OID);
+ git_oid_fromstr(&their_oid, ONE_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "newfile.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "newfile.txt"));
+
+ cl_assert_equal_s("newfile.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &ancestor_oid) == 0);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &our_oid) == 0);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &their_oid) == 0);
+}
+
+void test_index_reuc__add_no_ancestor(void)
+{
+ git_oid ancestor_oid, our_oid, their_oid;
+ const git_index_reuc_entry *reuc;
+
+ memset(&ancestor_oid, 0x0, sizeof(git_oid));
+ git_oid_fromstr(&our_oid, ONE_OUR_OID);
+ git_oid_fromstr(&their_oid, ONE_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "newfile.txt",
+ 0, NULL,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "newfile.txt"));
+
+ cl_assert_equal_s("newfile.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &ancestor_oid) == 0);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &our_oid) == 0);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &their_oid) == 0);
+}
+
+void test_index_reuc__read_bypath(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "two.txt"));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "one.txt"));
+
+ cl_assert_equal_s("one.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__ignore_case(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+ int index_caps;
+
+ index_caps = git_index_caps(repo_index);
+
+ index_caps &= ~GIT_INDEXCAP_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ cl_assert(!git_index_reuc_get_bypath(repo_index, "TWO.txt"));
+
+ index_caps |= GIT_INDEXCAP_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "TWO.txt"));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__read_byindex(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert_equal_s("one.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__updates_existing(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid ancestor_oid, our_oid, their_oid, oid;
+ int index_caps;
+
+ git_index_clear(repo_index);
+
+ index_caps = git_index_caps(repo_index);
+
+ index_caps |= GIT_INDEXCAP_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ git_oid_fromstr(&ancestor_oid, TWO_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, TWO_OUR_OID);
+ git_oid_fromstr(&their_oid, TWO_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "two.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_git_pass(git_index_reuc_add(repo_index, "TWO.txt",
+ 0100644, &our_oid,
+ 0100644, &their_oid,
+ 0100644, &ancestor_oid));
+
+ cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert_equal_s("TWO.txt", reuc->path);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__remove(void)
+{
+ git_oid oid;
+ const git_index_reuc_entry *reuc;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_git_pass(git_index_reuc_remove(repo_index, 0));
+ cl_git_fail(git_index_reuc_remove(repo_index, 1));
+
+ cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__write(void)
+{
+ git_oid ancestor_oid, our_oid, their_oid;
+ const git_index_reuc_entry *reuc;
+
+ git_index_clear(repo_index);
+
+ /* Write out of order to ensure sorting is correct */
+ git_oid_fromstr(&ancestor_oid, TWO_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, TWO_OUR_OID);
+ git_oid_fromstr(&their_oid, TWO_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "two.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ git_oid_fromstr(&ancestor_oid, ONE_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, ONE_OUR_OID);
+ git_oid_fromstr(&their_oid, ONE_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "one.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_git_pass(git_index_write(repo_index));
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ /* ensure sort order was round-tripped correct */
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+ cl_assert_equal_s("one.txt", reuc->path);
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
+ cl_assert_equal_s("two.txt", reuc->path);
+}
+
+static int reuc_entry_exists(void)
+{
+ return (git_index_reuc_get_bypath(repo_index, "newfile.txt") != NULL);
+}
+
+void test_index_reuc__cleaned_on_reset_hard(void)
+{
+ git_object *target;
+
+ retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+ test_index_reuc__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
+ cl_assert(reuc_entry_exists() == false);
+
+ git_object_free(target);
+}
+
+void test_index_reuc__cleaned_on_reset_mixed(void)
+{
+ git_object *target;
+
+ retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+ test_index_reuc__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED));
+ cl_assert(reuc_entry_exists() == false);
+
+ git_object_free(target);
+}
+
+void test_index_reuc__retained_on_reset_soft(void)
+{
+ git_object *target;
+
+ retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+ git_reset(repo, target, GIT_RESET_HARD);
+
+ test_index_reuc__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
+ cl_assert(reuc_entry_exists() == true);
+
+ git_object_free(target);
+}
+
+void test_index_reuc__cleaned_on_checkout_tree(void)
+{
+ git_oid oid;
+ git_object *obj;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_reuc__add();
+ git_reference_name_to_id(&oid, repo, "refs/heads/master");
+ git_object_lookup(&obj, repo, &oid, GIT_OBJ_ANY);
+ git_checkout_tree(repo, obj, &opts);
+ cl_assert(reuc_entry_exists() == false);
+
+ git_object_free(obj);
+}
+
+void test_index_reuc__cleaned_on_checkout_head(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_reuc__add();
+ git_checkout_head(repo, &opts);
+ cl_assert(reuc_entry_exists() == false);
+}
+
+void test_index_reuc__retained_on_checkout_index(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_reuc__add();
+ git_checkout_index(repo, repo_index, &opts);
+ cl_assert(reuc_entry_exists() == true);
+}
diff --git a/tests/index/stage.c b/tests/index/stage.c
new file mode 100644
index 000000000..58dc1fb5e
--- /dev/null
+++ b/tests/index/stage.c
@@ -0,0 +1,62 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/repository.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+// Fixture setup and teardown
+void test_index_stage__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_stage__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_index_stage__add_always_adds_stage_0(void)
+{
+ size_t entry_idx;
+ const git_index_entry *entry;
+
+ cl_git_mkfile("./mergedrepo/new-file.txt", "new-file\n");
+
+ cl_git_pass(git_index_add_bypath(repo_index, "new-file.txt"));
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "new-file.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+}
+
+void test_index_stage__find_gets_first_stage(void)
+{
+ size_t entry_idx;
+ const git_index_entry *entry;
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "one.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "two.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "conflicts-one.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 1);
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "conflicts-two.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 1);
+}
+
diff --git a/tests/index/tests.c b/tests/index/tests.c
new file mode 100644
index 000000000..e5202980c
--- /dev/null
+++ b/tests/index/tests.c
@@ -0,0 +1,537 @@
+#include "clar_libgit2.h"
+#include "index.h"
+
+static const size_t index_entry_count = 109;
+static const size_t index_entry_count_2 = 1437;
+#define TEST_INDEX_PATH cl_fixture("testrepo.git/index")
+#define TEST_INDEX2_PATH cl_fixture("gitgit.index")
+#define TEST_INDEXBIG_PATH cl_fixture("big.index")
+
+
+/* Suite data */
+struct test_entry {
+ size_t index;
+ char path[128];
+ git_off_t file_size;
+ git_time_t mtime;
+};
+
+static struct test_entry test_entries[] = {
+ {4, "Makefile", 5064, 0x4C3F7F33},
+ {62, "tests/Makefile", 2631, 0x4C3F7F33},
+ {36, "src/index.c", 10014, 0x4C43368D},
+ {6, "git.git-authors", 2709, 0x4C3F7F33},
+ {48, "src/revobject.h", 1448, 0x4C3F7FE2}
+};
+
+/* Helpers */
+static void copy_file(const char *src, const char *dst)
+{
+ git_buf source_buf = GIT_BUF_INIT;
+ git_file dst_fd;
+
+ cl_git_pass(git_futils_readbuffer(&source_buf, src));
+
+ dst_fd = git_futils_creat_withpath(dst, 0777, 0666); /* -V536 */
+ if (dst_fd < 0)
+ goto cleanup;
+
+ cl_git_pass(p_write(dst_fd, source_buf.ptr, source_buf.size));
+
+cleanup:
+ git_buf_free(&source_buf);
+ p_close(dst_fd);
+}
+
+static void files_are_equal(const char *a, const char *b)
+{
+ git_buf buf_a = GIT_BUF_INIT;
+ git_buf buf_b = GIT_BUF_INIT;
+ int pass;
+
+ if (git_futils_readbuffer(&buf_a, a) < 0)
+ cl_assert(0);
+
+ if (git_futils_readbuffer(&buf_b, b) < 0) {
+ git_buf_free(&buf_a);
+ cl_assert(0);
+ }
+
+ pass = (buf_a.size == buf_b.size && !memcmp(buf_a.ptr, buf_b.ptr, buf_a.size));
+
+ git_buf_free(&buf_a);
+ git_buf_free(&buf_b);
+
+ cl_assert(pass);
+}
+
+
+/* Fixture setup and teardown */
+void test_index_tests__initialize(void)
+{
+}
+
+void test_index_tests__empty_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index_open(&index, "in-memory-index"));
+ cl_assert(index->on_disk == 0);
+
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(index->entries.sorted);
+
+ git_index_free(index);
+}
+
+void test_index_tests__default_test_index(void)
+{
+ git_index *index;
+ unsigned int i;
+ git_index_entry **entries;
+
+ cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_assert(index->on_disk);
+
+ cl_assert(git_index_entrycount(index) == index_entry_count);
+ cl_assert(index->entries.sorted);
+
+ entries = (git_index_entry **)index->entries.contents;
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ git_index_entry *e = entries[test_entries[i].index];
+
+ cl_assert_equal_s(e->path, test_entries[i].path);
+ cl_assert(e->mtime.seconds == test_entries[i].mtime);
+ cl_assert(e->file_size == test_entries[i].file_size);
+ }
+
+ git_index_free(index);
+}
+
+void test_index_tests__gitgit_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index_open(&index, TEST_INDEX2_PATH));
+ cl_assert(index->on_disk);
+
+ cl_assert(git_index_entrycount(index) == index_entry_count_2);
+ cl_assert(index->entries.sorted);
+ cl_assert(index->tree != NULL);
+
+ git_index_free(index);
+}
+
+void test_index_tests__find_in_existing(void)
+{
+ git_index *index;
+ unsigned int i;
+
+ cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ size_t idx;
+
+ cl_assert(!git_index_find(&idx, index, test_entries[i].path));
+ cl_assert(idx == test_entries[i].index);
+ }
+
+ git_index_free(index);
+}
+
+void test_index_tests__find_in_empty(void)
+{
+ git_index *index;
+ unsigned int i;
+
+ cl_git_pass(git_index_open(&index, "fake-index"));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ cl_assert(GIT_ENOTFOUND == git_index_find(NULL, index, test_entries[i].path));
+ }
+
+ git_index_free(index);
+}
+
+void test_index_tests__write(void)
+{
+ git_index *index;
+
+ copy_file(TEST_INDEXBIG_PATH, "index_rewrite");
+
+ cl_git_pass(git_index_open(&index, "index_rewrite"));
+ cl_assert(index->on_disk);
+
+ cl_git_pass(git_index_write(index));
+ files_are_equal(TEST_INDEXBIG_PATH, "index_rewrite");
+
+ git_index_free(index);
+
+ p_unlink("index_rewrite");
+}
+
+void test_index_tests__sort0(void)
+{
+ /* sort the entires in an index */
+
+ /*
+ * TODO: This no longer applies:
+ * index sorting in Git uses some specific changes to the way
+ * directories are sorted.
+ *
+ * We need to specificially check for this by creating a new
+ * index, adding entries in random order and then
+ * checking for consistency
+ */
+}
+
+void test_index_tests__sort1(void)
+{
+ /* sort the entires in an empty index */
+ git_index *index;
+
+ cl_git_pass(git_index_open(&index, "fake-index"));
+
+ /* FIXME: this test is slightly dumb */
+ cl_assert(index->entries.sorted);
+
+ git_index_free(index);
+}
+
+static void cleanup_myrepo(void *opaque)
+{
+ GIT_UNUSED(opaque);
+ cl_fixture_cleanup("myrepo");
+}
+
+void test_index_tests__add(void)
+{
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_repository *repo;
+ const git_index_entry *entry;
+ git_oid id1;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Intialize a new repository */
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /* Create a new file in the working directory */
+ cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
+ cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, "hey there\n", 10));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid_fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"));
+
+ /* Add the new file to the index */
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert(git_oid_cmp(&id1, &entry->oid) == 0);
+
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert(git_oid_cmp(&id1, &entry->oid) == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+static void cleanup_1397(void *opaque)
+{
+ GIT_UNUSED(opaque);
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_tests__add_issue_1397(void)
+{
+ git_index *index;
+ git_repository *repo;
+ const git_index_entry *entry;
+ git_oid id1;
+
+ cl_set_cleanup(&cleanup_1397, NULL);
+
+ repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ git hash-object crlf_file.txt
+ */
+ cl_git_pass(git_oid_fromstr(&id1, "8312e0889a9cbab77c732b6bc39b51a683e3a318"));
+
+ /* Make sure the initial SHA-1 is correct */
+ cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
+ cl_assert_(git_oid_cmp(&id1, &entry->oid) == 0, "first oid check");
+
+ /* Update the index */
+ cl_git_pass(git_index_add_bypath(index, "crlf_file.txt"));
+
+ /* Check the new SHA-1 */
+ cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
+ cl_assert_(git_oid_cmp(&id1, &entry->oid) == 0, "second oid check");
+
+ git_index_free(index);
+}
+
+void test_index_tests__add_bypath_to_a_bare_repository_returns_EBAREPO(void)
+{
+ git_repository *bare_repo;
+ git_index *index;
+
+ cl_git_pass(git_repository_open(&bare_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_index(&index, bare_repo));
+
+ cl_assert_equal_i(GIT_EBAREREPO, git_index_add_bypath(index, "test.txt"));
+
+ git_index_free(index);
+ git_repository_free(bare_repo);
+}
+
+/* Test that writing an invalid filename fails */
+void test_index_tests__write_invalid_filename(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_oid expected;
+
+ p_mkdir("read_tree", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./read_tree", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ cl_git_mkfile("./read_tree/.git/hello", NULL);
+
+ cl_git_pass(git_index_add_bypath(index, ".git/hello"));
+
+ /* write-tree */
+ cl_git_fail(git_index_write_tree(&expected, index));
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("read_tree");
+}
+
+void test_index_tests__remove_entry(void)
+{
+ git_repository *repo;
+ git_index *index;
+
+ p_mkdir("index_test", 0770);
+
+ cl_git_pass(git_repository_init(&repo, "index_test", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ cl_git_mkfile("index_test/hello", NULL);
+ cl_git_pass(git_index_add_bypath(index, "hello"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_get_bypath(index, "hello", 0) != NULL);
+
+ cl_git_pass(git_index_remove(index, "hello", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_index_get_bypath(index, "hello", 0) == NULL);
+
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_fixture_cleanup("index_test");
+}
+
+void test_index_tests__remove_directory(void)
+{
+ git_repository *repo;
+ git_index *index;
+
+ p_mkdir("index_test", 0770);
+
+ cl_git_pass(git_repository_init(&repo, "index_test", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert_equal_i(0, (int)git_index_entrycount(index));
+
+ p_mkdir("index_test/a", 0770);
+ cl_git_mkfile("index_test/a/1.txt", NULL);
+ cl_git_mkfile("index_test/a/2.txt", NULL);
+ cl_git_mkfile("index_test/a/3.txt", NULL);
+ cl_git_mkfile("index_test/b.txt", NULL);
+
+ cl_git_pass(git_index_add_bypath(index, "a/1.txt"));
+ cl_git_pass(git_index_add_bypath(index, "a/2.txt"));
+ cl_git_pass(git_index_add_bypath(index, "a/3.txt"));
+ cl_git_pass(git_index_add_bypath(index, "b.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(4, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ cl_git_pass(git_index_remove(index, "a/1.txt", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(3, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ cl_git_pass(git_index_remove_directory(index, "a", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(1, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_fixture_cleanup("index_test");
+}
+
+void test_index_tests__preserves_case(void)
+{
+ git_repository *repo;
+ git_index *index;
+ const git_index_entry *entry;
+ int index_caps;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ index_caps = git_index_caps(index);
+
+ cl_git_rewritefile("myrepo/test.txt", "hey there\n");
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt"));
+ cl_git_rewritefile("myrepo/TEST.txt", "hello again\n");
+ cl_git_pass(git_index_add_bypath(index, "TEST.txt"));
+
+ if (index_caps & GIT_INDEXCAP_IGNORE_CASE)
+ cl_assert_equal_i(1, (int)git_index_entrycount(index));
+ else
+ cl_assert_equal_i(2, (int)git_index_entrycount(index));
+
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ /* The path should *not* have changed without an explicit remove */
+ cl_assert(git__strcmp(entry->path, "test.txt") == 0);
+
+ cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL);
+ if (index_caps & GIT_INDEXCAP_IGNORE_CASE)
+ /* The path should *not* have changed without an explicit remove */
+ cl_assert(git__strcmp(entry->path, "test.txt") == 0);
+ else
+ cl_assert(git__strcmp(entry->path, "TEST.txt") == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__elocked(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const git_error *err;
+ int error;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Lock the index file so we fail to lock it */
+ cl_git_pass(git_filebuf_open(&file, index->index_file_path, 0, 0666));
+ error = git_index_write(index);
+ cl_assert_equal_i(GIT_ELOCKED, error);
+
+ err = giterr_last();
+ cl_assert_equal_i(err->klass, GITERR_INDEX);
+
+ git_filebuf_cleanup(&file);
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__reload_from_disk(void)
+{
+ git_repository *repo;
+ git_index *read_index;
+ git_index *write_index;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_futils_mkdir("./myrepo", NULL, 0777, GIT_MKDIR_PATH));
+ cl_git_mkfile("./myrepo/a.txt", "a\n");
+ cl_git_mkfile("./myrepo/b.txt", "b\n");
+
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&write_index, repo));
+ cl_assert_equal_i(false, write_index->on_disk);
+
+ cl_git_pass(git_index_open(&read_index, write_index->index_file_path));
+ cl_assert_equal_i(false, read_index->on_disk);
+
+ /* Stage two new files agaisnt the write_index */
+ cl_git_pass(git_index_add_bypath(write_index, "a.txt"));
+ cl_git_pass(git_index_add_bypath(write_index, "b.txt"));
+
+ cl_assert_equal_sz(2, git_index_entrycount(write_index));
+
+ /* Persist the index changes to disk */
+ cl_git_pass(git_index_write(write_index));
+ cl_assert_equal_i(true, write_index->on_disk);
+
+ /* Sync the changes back into the read_index */
+ cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+ cl_git_pass(git_index_read(read_index, true));
+ cl_assert_equal_i(true, read_index->on_disk);
+
+ cl_assert_equal_sz(2, git_index_entrycount(read_index));
+
+ /* Remove the index file from the filesystem */
+ cl_git_pass(p_unlink(write_index->index_file_path));
+
+ /* Sync the changes back into the read_index */
+ cl_git_pass(git_index_read(read_index, true));
+ cl_assert_equal_i(false, read_index->on_disk);
+ cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+ git_index_free(read_index);
+ git_index_free(write_index);
+ git_repository_free(repo);
+}