Initial commit

This commit is contained in:
jiapengyu
2026-07-27 13:37:48 +08:00
commit ecba71e2a5
39123 changed files with 5989154 additions and 0 deletions
+721
View File
@@ -0,0 +1,721 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "path.h"
#include "posix.h"
#include "status_helpers.h"
#include "util.h"
#include "status.h"
static git_repository *g_repo = NULL;
void test_status_renames__initialize(void)
{
g_repo = cl_git_sandbox_init("renames");
cl_repo_set_bool(g_repo, "core.autocrlf", false);
}
void test_status_renames__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void _rename_helper(
git_repository *repo, const char *from, const char *to, const char *extra)
{
git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(
&oldpath, git_repository_workdir(repo), from));
cl_git_pass(git_buf_joinpath(
&newpath, git_repository_workdir(repo), to));
cl_git_pass(p_rename(oldpath.ptr, newpath.ptr));
if (extra)
cl_git_append2file(newpath.ptr, extra);
git_buf_dispose(&oldpath);
git_buf_dispose(&newpath);
}
#define rename_file(R,O,N) _rename_helper((R), (O), (N), NULL)
#define rename_and_edit_file(R,O,N) \
_rename_helper((R), (O), (N), "Added at the end to keep similarity!")
struct status_entry {
git_status_t status;
const char *oldname;
const char *newname;
};
static void check_status(
git_status_list *status_list,
struct status_entry *expected_list,
size_t expected_len)
{
const git_status_entry *actual;
const struct status_entry *expected;
const char *oldname, *newname;
size_t i, files_in_status = git_status_list_entrycount(status_list);
cl_assert_equal_sz(expected_len, files_in_status);
for (i = 0; i < expected_len; i++) {
actual = git_status_byindex(status_list, i);
expected = &expected_list[i];
oldname = actual->head_to_index ? actual->head_to_index->old_file.path :
actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL;
newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path :
actual->head_to_index ? actual->head_to_index->new_file.path : NULL;
cl_assert_equal_i_fmt(expected->status, actual->status, "%04x");
if (expected->oldname) {
cl_assert(oldname != NULL);
cl_assert_equal_s(oldname, expected->oldname);
} else {
cl_assert(oldname == NULL);
}
if (actual->status & (GIT_STATUS_INDEX_RENAMED|GIT_STATUS_WT_RENAMED)) {
if (expected->newname) {
cl_assert(newname != NULL);
cl_assert_equal_s(newname, expected->newname);
} else {
cl_assert(newname == NULL);
}
}
}
}
void test_status_renames__head2index_one(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "newname.txt" },
};
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
cl_git_pass(git_repository_index(&index, g_repo));
rename_file(g_repo, "ikeepsix.txt", "newname.txt");
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
cl_git_pass(git_index_add_bypath(index, "newname.txt"));
cl_git_pass(git_index_write(index));
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 1);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__head2index_two(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
"sixserving.txt", "aaa.txt" },
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
"untimely.txt", "bbb.txt" },
{ GIT_STATUS_INDEX_RENAMED, "songof7cities.txt", "ccc.txt" },
{ GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "ddd.txt" },
};
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
cl_git_pass(git_repository_index(&index, g_repo));
rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
rename_file(g_repo, "songof7cities.txt", "ccc.txt");
rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
cl_git_pass(git_index_add_bypath(index, "ddd.txt"));
cl_git_pass(git_index_add_bypath(index, "aaa.txt"));
cl_git_pass(git_index_add_bypath(index, "ccc.txt"));
cl_git_pass(git_index_add_bypath(index, "bbb.txt"));
cl_git_pass(git_index_write(index));
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 4);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__head2index_no_rename_from_rewrite(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_INDEX_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
{ GIT_STATUS_INDEX_MODIFIED, "sixserving.txt", "sixserving.txt" },
};
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
cl_git_pass(git_repository_index(&index, g_repo));
rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
rename_file(g_repo, "_temp_.txt", "sixserving.txt");
cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
cl_git_pass(git_index_write(index));
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 2);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__head2index_rename_from_rewrite(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "ikeepsix.txt" },
{ GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "sixserving.txt" },
};
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
cl_git_pass(git_repository_index(&index, g_repo));
rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
rename_file(g_repo, "_temp_.txt", "sixserving.txt");
cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
cl_git_pass(git_index_write(index));
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 2);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__index2workdir_one(void)
{
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "newname.txt" },
};
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
rename_file(g_repo, "ikeepsix.txt", "newname.txt");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 1);
git_status_list_free(statuslist);
}
void test_status_renames__index2workdir_two(void)
{
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
"sixserving.txt", "aaa.txt" },
{ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
"untimely.txt", "bbb.txt" },
{ GIT_STATUS_WT_RENAMED, "songof7cities.txt", "ccc.txt" },
{ GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ddd.txt" },
};
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
rename_file(g_repo, "songof7cities.txt", "ccc.txt");
rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 4);
git_status_list_free(statuslist);
}
void test_status_renames__index2workdir_rename_from_rewrite(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_WT_RENAMED, "sixserving.txt", "ikeepsix.txt" },
{ GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "sixserving.txt" },
};
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
cl_git_pass(git_repository_index(&index, g_repo));
rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
rename_file(g_repo, "_temp_.txt", "sixserving.txt");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 2);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__both_one(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
"ikeepsix.txt", "newname-workdir.txt" },
};
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
cl_git_pass(git_repository_index(&index, g_repo));
rename_file(g_repo, "ikeepsix.txt", "newname-index.txt");
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
cl_git_pass(git_index_add_bypath(index, "newname-index.txt"));
cl_git_pass(git_index_write(index));
rename_file(g_repo, "newname-index.txt", "newname-workdir.txt");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 1);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__both_two(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
"ikeepsix.txt", "ikeepsix-both.txt" },
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
"sixserving.txt", "sixserving-index.txt" },
{ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
"songof7cities.txt", "songof7cities-workdir.txt" },
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
"untimely.txt", "untimely-both.txt" },
};
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
cl_git_pass(git_repository_index(&index, g_repo));
rename_and_edit_file(g_repo, "ikeepsix.txt", "ikeepsix-index.txt");
rename_and_edit_file(g_repo, "sixserving.txt", "sixserving-index.txt");
rename_file(g_repo, "untimely.txt", "untimely-index.txt");
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
cl_git_pass(git_index_add_bypath(index, "ikeepsix-index.txt"));
cl_git_pass(git_index_add_bypath(index, "sixserving-index.txt"));
cl_git_pass(git_index_add_bypath(index, "untimely-index.txt"));
cl_git_pass(git_index_write(index));
rename_and_edit_file(g_repo, "ikeepsix-index.txt", "ikeepsix-both.txt");
rename_and_edit_file(g_repo, "songof7cities.txt", "songof7cities-workdir.txt");
rename_file(g_repo, "untimely-index.txt", "untimely-both.txt");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 4);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__both_rename_from_rewrite(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
"songof7cities.txt", "ikeepsix.txt" },
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
"ikeepsix.txt", "sixserving.txt" },
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
"sixserving.txt", "songof7cities.txt" },
};
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
cl_git_pass(git_repository_index(&index, g_repo));
rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
rename_file(g_repo, "songof7cities.txt", "sixserving.txt");
rename_file(g_repo, "_temp_.txt", "songof7cities.txt");
cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
cl_git_pass(git_index_write(index));
rename_file(g_repo, "songof7cities.txt", "_temp_.txt");
rename_file(g_repo, "ikeepsix.txt", "songof7cities.txt");
rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
rename_file(g_repo, "_temp_.txt", "sixserving.txt");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 3);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__rewrites_only_for_renames(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
};
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_rewritefile("renames/ikeepsix.txt",
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n" \
"This is enough content for the file to be rewritten.\n");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 1);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__both_casechange_one(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
int index_caps;
struct status_entry expected_icase[] = {
{ GIT_STATUS_INDEX_RENAMED,
"ikeepsix.txt", "IKeepSix.txt" },
};
struct status_entry expected_case[] = {
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
"ikeepsix.txt", "IKEEPSIX.txt" },
};
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
cl_git_pass(git_repository_index(&index, g_repo));
index_caps = git_index_caps(index);
rename_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
cl_git_pass(git_index_write(index));
/* on a case-insensitive file system, this change won't matter.
* on a case-sensitive one, it will.
*/
rename_file(g_repo, "IKeepSix.txt", "IKEEPSIX.txt");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE) ?
expected_icase : expected_case, 1);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__both_casechange_two(void)
{
git_index *index;
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
int index_caps;
struct status_entry expected_icase[] = {
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
GIT_STATUS_WT_MODIFIED,
"ikeepsix.txt", "IKeepSix.txt" },
{ GIT_STATUS_INDEX_MODIFIED,
"sixserving.txt", "sixserving.txt" },
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED,
"songof7cities.txt", "songof7.txt" },
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
"untimely.txt", "untimeliest.txt" }
};
struct status_entry expected_case[] = {
{ GIT_STATUS_INDEX_RENAMED |
GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED,
"songof7cities.txt", "SONGOF7.txt" },
{ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED,
"sixserving.txt", "SixServing.txt" },
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
"ikeepsix.txt", "ikeepsix.txt" },
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
"untimely.txt", "untimeliest.txt" }
};
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
cl_git_pass(git_repository_index(&index, g_repo));
index_caps = git_index_caps(index);
rename_and_edit_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
rename_and_edit_file(g_repo, "sixserving.txt", "sixserving.txt");
rename_file(g_repo, "songof7cities.txt", "songof7.txt");
rename_file(g_repo, "untimely.txt", "untimelier.txt");
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
cl_git_pass(git_index_add_bypath(index, "songof7.txt"));
cl_git_pass(git_index_add_bypath(index, "untimelier.txt"));
cl_git_pass(git_index_write(index));
rename_and_edit_file(g_repo, "IKeepSix.txt", "ikeepsix.txt");
rename_file(g_repo, "sixserving.txt", "SixServing.txt");
rename_and_edit_file(g_repo, "songof7.txt", "SONGOF7.txt");
rename_file(g_repo, "untimelier.txt", "untimeliest.txt");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE) ?
expected_icase : expected_case, 4);
git_status_list_free(statuslist);
git_index_free(index);
}
void test_status_renames__zero_byte_file_does_not_fail(void)
{
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected[] = {
{ GIT_STATUS_WT_DELETED, "ikeepsix.txt", "ikeepsix.txt" },
{ GIT_STATUS_WT_NEW, "zerobyte.txt", "zerobyte.txt" },
};
opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES |
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR |
GIT_STATUS_OPT_INCLUDE_IGNORED |
GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
GIT_STATUS_SHOW_INDEX_AND_WORKDIR |
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
p_unlink("renames/ikeepsix.txt");
cl_git_mkfile("renames/zerobyte.txt", "");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected, 2);
git_status_list_free(statuslist);
}
#ifdef GIT_USE_ICONV
static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
#endif
/*
* Create a file in NFD (canonically decomposed) format. Ensure
* that when core.precomposeunicode is false that we return paths
* in NFD, but when core.precomposeunicode is true, then we
* return paths precomposed (in NFC).
*/
void test_status_renames__precomposed_unicode_rename(void)
{
#ifdef GIT_USE_ICONV
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected0[] = {
{ GIT_STATUS_WT_NEW, nfd, NULL },
{ GIT_STATUS_WT_DELETED, "sixserving.txt", NULL },
};
struct status_entry expected1[] = {
{ GIT_STATUS_WT_RENAMED, "sixserving.txt", nfd },
};
struct status_entry expected2[] = {
{ GIT_STATUS_WT_DELETED, "sixserving.txt", NULL },
{ GIT_STATUS_WT_NEW, nfc, NULL },
};
struct status_entry expected3[] = {
{ GIT_STATUS_WT_RENAMED, "sixserving.txt", nfc },
};
rename_file(g_repo, "sixserving.txt", nfd);
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
cl_repo_set_bool(g_repo, "core.precomposeunicode", false);
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected0, ARRAY_SIZE(expected0));
git_status_list_free(statuslist);
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected1, ARRAY_SIZE(expected1));
git_status_list_free(statuslist);
cl_repo_set_bool(g_repo, "core.precomposeunicode", true);
opts.flags &= ~GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected2, ARRAY_SIZE(expected2));
git_status_list_free(statuslist);
opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected3, ARRAY_SIZE(expected3));
git_status_list_free(statuslist);
#endif
}
void test_status_renames__precomposed_unicode_toggle_is_rename(void)
{
#ifdef GIT_USE_ICONV
git_status_list *statuslist;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
struct status_entry expected0[] = {
{ GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", nfd },
};
struct status_entry expected1[] = {
{ GIT_STATUS_WT_RENAMED, nfd, nfc },
};
struct status_entry expected2[] = {
{ GIT_STATUS_INDEX_RENAMED, nfd, nfc },
};
struct status_entry expected3[] = {
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, nfd, nfd },
};
cl_repo_set_bool(g_repo, "core.precomposeunicode", false);
rename_file(g_repo, "ikeepsix.txt", nfd);
{
git_index *index;
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
cl_git_pass(git_index_add_bypath(index, nfd));
cl_git_pass(git_index_write(index));
git_index_free(index);
}
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected0, ARRAY_SIZE(expected0));
git_status_list_free(statuslist);
cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "commit nfd");
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
cl_assert_equal_sz(0, git_status_list_entrycount(statuslist));
git_status_list_free(statuslist);
cl_repo_set_bool(g_repo, "core.precomposeunicode", true);
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected1, ARRAY_SIZE(expected1));
git_status_list_free(statuslist);
{
git_index *index;
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_remove_bypath(index, nfd));
cl_git_pass(git_index_add_bypath(index, nfc));
cl_git_pass(git_index_write(index));
git_index_free(index);
}
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected2, ARRAY_SIZE(expected2));
git_status_list_free(statuslist);
cl_repo_set_bool(g_repo, "core.precomposeunicode", false);
cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
check_status(statuslist, expected3, ARRAY_SIZE(expected3));
git_status_list_free(statuslist);
#endif
}
+45
View File
@@ -0,0 +1,45 @@
#include "clar_libgit2.h"
#include "posix.h"
static void
cleanup__remove_file(void *_file)
{
cl_must_pass(p_unlink((char *)_file));
}
/* test retrieving OID from a file apart from the ODB */
void test_status_single__hash_single_file(void)
{
static const char file_name[] = "new_file";
static const char file_contents[] = "new_file\n";
static const char file_hash[] = "d4fa8600b4f37d7516bef4816ae2c64dbf029e3a";
git_oid expected_id, actual_id;
/* initialization */
git_oid_fromstr(&expected_id, file_hash);
cl_git_mkfile(file_name, file_contents);
cl_set_cleanup(&cleanup__remove_file, (void *)file_name);
cl_git_pass(git_odb_hashfile(&actual_id, file_name, GIT_OBJECT_BLOB));
cl_assert_equal_oid(&expected_id, &actual_id);
}
/* test retrieving OID from an empty file apart from the ODB */
void test_status_single__hash_single_empty_file(void)
{
static const char file_name[] = "new_empty_file";
static const char file_contents[] = "";
static const char file_hash[] = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391";
git_oid expected_id, actual_id;
/* initialization */
git_oid_fromstr(&expected_id, file_hash);
cl_git_mkfile(file_name, file_contents);
cl_set_cleanup(&cleanup__remove_file, (void *)file_name);
cl_git_pass(git_odb_hashfile(&actual_id, file_name, GIT_OBJECT_BLOB));
cl_assert_equal_oid(&expected_id, &actual_id);
}
+326
View File
@@ -0,0 +1,326 @@
#include "status_helpers.h"
/* A utf-8 string with 83 characters, but 249 bytes. */
static const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97";
/* entries for a plain copy of tests/resources/status */
static const char *entry_paths0[] = {
"file_deleted",
"ignored_file",
"modified_file",
"new_file",
"staged_changes",
"staged_changes_file_deleted",
"staged_changes_modified_file",
"staged_delete_file_deleted",
"staged_delete_modified_file",
"staged_new_file",
"staged_new_file_deleted_file",
"staged_new_file_modified_file",
"subdir/deleted_file",
"subdir/modified_file",
"subdir/new_file",
"\xe8\xbf\x99",
};
static const unsigned int entry_statuses0[] = {
GIT_STATUS_WT_DELETED,
GIT_STATUS_IGNORED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_DELETED,
GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_DELETED,
GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
};
static const int entry_count0 = 16;
/* entries for a copy of tests/resources/status with all content
* deleted from the working directory
*/
static const char *entry_paths2[] = {
"current_file",
"file_deleted",
"modified_file",
"staged_changes",
"staged_changes_file_deleted",
"staged_changes_modified_file",
"staged_delete_file_deleted",
"staged_delete_modified_file",
"staged_new_file",
"staged_new_file_deleted_file",
"staged_new_file_modified_file",
"subdir.txt",
"subdir/current_file",
"subdir/deleted_file",
"subdir/modified_file",
};
static const unsigned int entry_statuses2[] = {
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
};
static const int entry_count2 = 15;
/* entries for a copy of tests/resources/status with some mods */
static const char *entry_paths3_icase[] = {
".HEADER",
"42-is-not-prime.sigh",
"current_file",
"current_file/",
"file_deleted",
"ignored_file",
"modified_file",
"new_file",
"README.md",
"staged_changes",
"staged_changes_file_deleted",
"staged_changes_modified_file",
"staged_delete_file_deleted",
"staged_delete_modified_file",
"staged_new_file",
"staged_new_file_deleted_file",
"staged_new_file_modified_file",
"subdir",
"subdir/current_file",
"subdir/deleted_file",
"subdir/modified_file",
"\xe8\xbf\x99",
};
static const unsigned int entry_statuses3_icase[] = {
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_IGNORED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_NEW,
};
static const char *entry_paths3[] = {
".HEADER",
"42-is-not-prime.sigh",
"README.md",
"current_file",
"current_file/",
"file_deleted",
"ignored_file",
"modified_file",
"new_file",
"staged_changes",
"staged_changes_file_deleted",
"staged_changes_modified_file",
"staged_delete_file_deleted",
"staged_delete_modified_file",
"staged_new_file",
"staged_new_file_deleted_file",
"staged_new_file_modified_file",
"subdir",
"subdir/current_file",
"subdir/deleted_file",
"subdir/modified_file",
"\xe8\xbf\x99",
};
static const unsigned int entry_statuses3[] = {
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_IGNORED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_NEW,
};
static const int entry_count3 = 22;
/* entries for a copy of tests/resources/status with some mods
* and different options to the status call
*/
static const char *entry_paths4[] = {
".new_file",
"current_file",
"current_file/current_file",
"current_file/modified_file",
"current_file/new_file",
"file_deleted",
"modified_file",
"new_file",
"staged_changes",
"staged_changes_file_deleted",
"staged_changes_modified_file",
"staged_delete_file_deleted",
"staged_delete_modified_file",
"staged_new_file",
"staged_new_file_deleted_file",
"staged_new_file_modified_file",
"subdir",
"subdir/current_file",
"subdir/deleted_file",
"subdir/modified_file",
"zzz_new_dir/new_file",
"zzz_new_file",
"\xe8\xbf\x99",
};
static const unsigned int entry_statuses4[] = {
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
};
static const int entry_count4 = 23;
/* entries for a copy of tests/resources/status with options
* passed to the status call in order to only get the differences
* between the HEAD and the index (changes to be committed)
*/
static const char *entry_paths5[] = {
"staged_changes",
"staged_changes_file_deleted",
"staged_changes_modified_file",
"staged_delete_file_deleted",
"staged_delete_modified_file",
"staged_new_file",
"staged_new_file_deleted_file",
"staged_new_file_modified_file",
};
static const unsigned int entry_statuses5[] = {
GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_INDEX_NEW,
};
static const int entry_count5 = 8;
/* entries for a copy of tests/resources/status with options
* passed to the status call in order to only get the differences
* between the workdir and the index (changes not staged, untracked files)
*/
static const char *entry_paths6[] = {
"file_deleted",
"ignored_file",
"modified_file",
"new_file",
"staged_changes_file_deleted",
"staged_changes_modified_file",
"staged_delete_modified_file",
"staged_new_file_deleted_file",
"staged_new_file_modified_file",
"subdir/deleted_file",
"subdir/modified_file",
"subdir/new_file",
"\xe8\xbf\x99",
};
static const unsigned int entry_statuses6[] = {
GIT_STATUS_WT_DELETED,
GIT_STATUS_IGNORED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_DELETED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_NEW,
};
static const int entry_count6 = 13;
+97
View File
@@ -0,0 +1,97 @@
#include "clar_libgit2.h"
#include "status_helpers.h"
int cb_status__normal(
const char *path, unsigned int status_flags, void *payload)
{
status_entry_counts *counts = payload;
if (counts->debug)
cb_status__print(path, status_flags, NULL);
if (counts->entry_count >= counts->expected_entry_count)
counts->wrong_status_flags_count++;
else if (strcmp(path, counts->expected_paths[counts->entry_count]))
counts->wrong_sorted_path++;
else if (status_flags != counts->expected_statuses[counts->entry_count])
counts->wrong_status_flags_count++;
counts->entry_count++;
return 0;
}
int cb_status__count(const char *p, unsigned int s, void *payload)
{
volatile int *count = (int *)payload;
GIT_UNUSED(p);
GIT_UNUSED(s);
(*count)++;
return 0;
}
int cb_status__single(const char *p, unsigned int s, void *payload)
{
status_entry_single *data = (status_entry_single *)payload;
if (data->debug)
fprintf(stderr, "%02d: %s (%04x)\n", data->count, p, s);
data->count++;
data->status = s;
return 0;
}
int cb_status__print(
const char *path, unsigned int status_flags, void *payload)
{
char istatus = ' ', wstatus = ' ';
int icount = 0, wcount = 0;
if (status_flags & GIT_STATUS_INDEX_NEW) {
istatus = 'A'; icount++;
}
if (status_flags & GIT_STATUS_INDEX_MODIFIED) {
istatus = 'M'; icount++;
}
if (status_flags & GIT_STATUS_INDEX_DELETED) {
istatus = 'D'; icount++;
}
if (status_flags & GIT_STATUS_INDEX_RENAMED) {
istatus = 'R'; icount++;
}
if (status_flags & GIT_STATUS_INDEX_TYPECHANGE) {
istatus = 'T'; icount++;
}
if (status_flags & GIT_STATUS_WT_NEW) {
wstatus = 'A'; wcount++;
}
if (status_flags & GIT_STATUS_WT_MODIFIED) {
wstatus = 'M'; wcount++;
}
if (status_flags & GIT_STATUS_WT_DELETED) {
wstatus = 'D'; wcount++;
}
if (status_flags & GIT_STATUS_WT_TYPECHANGE) {
wstatus = 'T'; wcount++;
}
if (status_flags & GIT_STATUS_IGNORED) {
wstatus = 'I'; wcount++;
}
if (status_flags & GIT_STATUS_WT_UNREADABLE) {
wstatus = 'X'; wcount++;
}
fprintf(stderr, "%c%c %s (%d/%d%s)\n",
istatus, wstatus, path, icount, wcount,
(icount > 1 || wcount > 1) ? " INVALID COMBO" : "");
if (payload)
*((int *)payload) += 1;
return 0;
}
+49
View File
@@ -0,0 +1,49 @@
#ifndef INCLUDE_cl_status_helpers_h__
#define INCLUDE_cl_status_helpers_h__
typedef struct {
int wrong_status_flags_count;
int wrong_sorted_path;
int entry_count;
const unsigned int* expected_statuses;
const char** expected_paths;
int expected_entry_count;
const char *file;
int line;
bool debug;
} status_entry_counts;
#define status_counts_init(counts, paths, statuses) do { \
memset(&(counts), 0, sizeof(counts)); \
(counts).expected_statuses = (statuses); \
(counts).expected_paths = (paths); \
(counts).file = __FILE__; \
(counts).line = __LINE__; \
} while (0)
/* cb_status__normal takes payload of "status_entry_counts *" */
extern int cb_status__normal(
const char *path, unsigned int status_flags, void *payload);
/* cb_status__count takes payload of "int *" */
extern int cb_status__count(const char *p, unsigned int s, void *payload);
typedef struct {
int count;
unsigned int status;
bool debug;
} status_entry_single;
/* cb_status__single takes payload of "status_entry_single *" */
extern int cb_status__single(const char *p, unsigned int s, void *payload);
/* cb_status__print takes optional payload of "int *" */
extern int cb_status__print(const char *p, unsigned int s, void *payload);
#endif
+563
View File
@@ -0,0 +1,563 @@
#include "clar_libgit2.h"
#include "futils.h"
#include "status_helpers.h"
#include "../submodule/submodule_helpers.h"
static git_repository *g_repo = NULL;
void test_status_submodules__initialize(void)
{
}
void test_status_submodules__cleanup(void)
{
}
void test_status_submodules__api(void)
{
git_submodule *sm;
g_repo = setup_fixture_submodules();
cl_assert(git_submodule_lookup(NULL, g_repo, "nonexistent") == GIT_ENOTFOUND);
cl_assert(git_submodule_lookup(NULL, g_repo, "modified") == GIT_ENOTFOUND);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_assert(sm != NULL);
cl_assert_equal_s("testrepo", git_submodule_name(sm));
cl_assert_equal_s("testrepo", git_submodule_path(sm));
git_submodule_free(sm);
}
void test_status_submodules__0(void)
{
int counts = 0;
g_repo = setup_fixture_submodules();
cl_assert(git_path_isdir("submodules/.git"));
cl_assert(git_path_isdir("submodules/testrepo/.git"));
cl_assert(git_path_isfile("submodules/.gitmodules"));
cl_git_pass(
git_status_foreach(g_repo, cb_status__count, &counts)
);
cl_assert_equal_i(6, counts);
}
static const char *expected_files[] = {
".gitmodules",
"added",
"deleted",
"ignored",
"modified",
"untracked"
};
static unsigned int expected_status[] = {
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_IGNORED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW
};
static int cb_status__match(const char *p, unsigned int s, void *payload)
{
status_entry_counts *counts = payload;
int idx = counts->entry_count++;
clar__assert_equal(
counts->file, counts->line,
"Status path mismatch", 1,
"%s", counts->expected_paths[idx], p);
clar__assert_equal(
counts->file, counts->line,
"Status code mismatch", 1,
"%o", counts->expected_statuses[idx], s);
return 0;
}
void test_status_submodules__1(void)
{
status_entry_counts counts;
g_repo = setup_fixture_submodules();
cl_assert(git_path_isdir("submodules/.git"));
cl_assert(git_path_isdir("submodules/testrepo/.git"));
cl_assert(git_path_isfile("submodules/.gitmodules"));
status_counts_init(counts, expected_files, expected_status);
cl_git_pass( git_status_foreach(g_repo, cb_status__match, &counts) );
cl_assert_equal_i(6, counts.entry_count);
}
void test_status_submodules__single_file(void)
{
unsigned int status = 0;
g_repo = setup_fixture_submodules();
cl_git_pass( git_status_file(&status, g_repo, "testrepo") );
cl_assert(!status);
}
void test_status_submodules__moved_head(void)
{
git_submodule *sm;
git_repository *smrepo;
git_oid oid;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
static const char *expected_files_with_sub[] = {
".gitmodules",
"added",
"deleted",
"ignored",
"modified",
"testrepo",
"untracked"
};
static unsigned int expected_status_with_sub[] = {
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_IGNORED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW
};
g_repo = setup_fixture_submodules();
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_open(&smrepo, sm));
git_submodule_free(sm);
/* move submodule HEAD to c47800c7266a2be04c571c04d5a6614691ea99bd */
cl_git_pass(
git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
cl_git_pass(git_repository_set_head_detached(smrepo, &oid));
/* first do a normal status, which should now include the submodule */
opts.flags = GIT_STATUS_OPT_DEFAULTS;
status_counts_init(
counts, expected_files_with_sub, expected_status_with_sub);
cl_git_pass(
git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(7, counts.entry_count);
/* try again with EXCLUDE_SUBMODULES which should skip it */
opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
status_counts_init(counts, expected_files, expected_status);
cl_git_pass(
git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(6, counts.entry_count);
git_repository_free(smrepo);
}
void test_status_submodules__dirty_workdir_only(void)
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
static const char *expected_files_with_sub[] = {
".gitmodules",
"added",
"deleted",
"ignored",
"modified",
"testrepo",
"untracked"
};
static unsigned int expected_status_with_sub[] = {
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_IGNORED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW
};
g_repo = setup_fixture_submodules();
cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
/* first do a normal status, which should now include the submodule */
opts.flags = GIT_STATUS_OPT_DEFAULTS;
status_counts_init(
counts, expected_files_with_sub, expected_status_with_sub);
cl_git_pass(
git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(7, counts.entry_count);
/* try again with EXCLUDE_SUBMODULES which should skip it */
opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
status_counts_init(counts, expected_files, expected_status);
cl_git_pass(
git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(6, counts.entry_count);
}
void test_status_submodules__uninitialized(void)
{
git_repository *cloned_repo;
git_status_list *statuslist;
g_repo = cl_git_sandbox_init("submod2");
cl_git_pass(git_clone(&cloned_repo, "submod2", "submod2-clone", NULL));
cl_git_pass(git_status_list_new(&statuslist, cloned_repo, NULL));
cl_assert_equal_i(0, git_status_list_entrycount(statuslist));
git_status_list_free(statuslist);
git_repository_free(cloned_repo);
cl_git_sandbox_cleanup();
}
void test_status_submodules__contained_untracked_repo(void)
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
git_repository *contained;
static const char *expected_files_not_ignored[] = {
".gitmodules",
"added",
"deleted",
"modified",
"untracked"
};
static unsigned int expected_status_not_ignored[] = {
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
};
static const char *expected_files_with_untracked[] = {
".gitmodules",
"added",
"deleted",
"dir/file.md",
"modified",
"untracked"
};
static const char *expected_files_with_untracked_dir[] = {
".gitmodules",
"added",
"deleted",
"dir/",
"modified",
"untracked"
};
static unsigned int expected_status_with_untracked[] = {
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_WT_NEW,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW
};
g_repo = setup_fixture_submodules();
/* skip empty directory */
cl_must_pass(p_mkdir("submodules/dir", 0777));
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
status_counts_init(
counts, expected_files_not_ignored, expected_status_not_ignored);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(5, counts.entry_count);
/* still skipping because empty == ignored */
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
status_counts_init(
counts, expected_files_not_ignored, expected_status_not_ignored);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(5, counts.entry_count);
/* find non-ignored contents of directory */
cl_git_mkfile("submodules/dir/file.md", "hello");
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
status_counts_init(
counts, expected_files_with_untracked, expected_status_with_untracked);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(6, counts.entry_count);
/* but skip if all content is ignored */
cl_git_append2file("submodules/.git/info/exclude", "\n*.md\n\n");
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
status_counts_init(
counts, expected_files_not_ignored, expected_status_not_ignored);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(5, counts.entry_count);
/* same is true if it contains a git link */
cl_git_mkfile("submodules/dir/.git", "gitlink: ../.git");
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
status_counts_init(
counts, expected_files_not_ignored, expected_status_not_ignored);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(5, counts.entry_count);
/* but if it contains tracked files, it should just show up as a
* directory and exclude the files in it
*/
cl_git_mkfile("submodules/dir/another_file", "hello");
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
status_counts_init(
counts, expected_files_with_untracked_dir,
expected_status_with_untracked);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(6, counts.entry_count);
/* that applies to a git repo with a .git directory too */
cl_must_pass(p_unlink("submodules/dir/.git"));
cl_git_pass(git_repository_init(&contained, "submodules/dir", false));
git_repository_free(contained);
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
status_counts_init(
counts, expected_files_with_untracked_dir,
expected_status_with_untracked);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(6, counts.entry_count);
/* same result even if we don't recurse into subdirectories */
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
status_counts_init(
counts, expected_files_with_untracked_dir,
expected_status_with_untracked);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(6, counts.entry_count);
/* and if we remove the untracked file, it goes back to ignored */
cl_must_pass(p_unlink("submodules/dir/another_file"));
status_counts_init(
counts, expected_files_not_ignored, expected_status_not_ignored);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(5, counts.entry_count);
}
void test_status_submodules__broken_stuff_that_git_allows(void)
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
git_repository *contained;
static const char *expected_files_with_broken[] = {
".gitmodules",
"added",
"broken/tracked",
"deleted",
"ignored",
"modified",
"untracked"
};
static unsigned int expected_status_with_broken[] = {
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_INDEX_NEW,
GIT_STATUS_INDEX_DELETED,
GIT_STATUS_IGNORED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
};
g_repo = setup_fixture_submodules();
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
GIT_STATUS_OPT_INCLUDE_IGNORED;
/* make a directory and stick a tracked item into the index */
{
git_index *idx;
cl_must_pass(p_mkdir("submodules/broken", 0777));
cl_git_mkfile("submodules/broken/tracked", "tracked content");
cl_git_pass(git_repository_index(&idx, g_repo));
cl_git_pass(git_index_add_bypath(idx, "broken/tracked"));
cl_git_pass(git_index_write(idx));
git_index_free(idx);
}
status_counts_init(
counts, expected_files_with_broken, expected_status_with_broken);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(7, counts.entry_count);
/* directory with tracked items that looks a little bit like a repo */
cl_must_pass(p_mkdir("submodules/broken/.git", 0777));
cl_must_pass(p_mkdir("submodules/broken/.git/info", 0777));
cl_git_mkfile("submodules/broken/.git/info/exclude", "# bogus");
status_counts_init(
counts, expected_files_with_broken, expected_status_with_broken);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(7, counts.entry_count);
/* directory with tracked items that is a repo */
cl_git_pass(git_futils_rmdir_r(
"submodules/broken/.git", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_repository_init(&contained, "submodules/broken", false));
git_repository_free(contained);
status_counts_init(
counts, expected_files_with_broken, expected_status_with_broken);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(7, counts.entry_count);
/* directory with tracked items that claims to be a submodule but is not */
cl_git_pass(git_futils_rmdir_r(
"submodules/broken/.git", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_append2file("submodules/.gitmodules",
"\n[submodule \"broken\"]\n"
"\tpath = broken\n"
"\turl = https://github.com/not/used\n\n");
status_counts_init(
counts, expected_files_with_broken, expected_status_with_broken);
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, cb_status__match, &counts));
cl_assert_equal_i(7, counts.entry_count);
}
void test_status_submodules__entry_but_dir_tracked(void)
{
git_repository *repo;
git_status_list *status;
git_diff *diff;
git_index *index;
git_tree *tree;
cl_git_pass(git_repository_init(&repo, "mixed-submodule", 0));
cl_git_mkfile("mixed-submodule/.gitmodules", "[submodule \"sub\"]\n path = sub\n url = ../foo\n");
cl_git_pass(p_mkdir("mixed-submodule/sub", 0777));
cl_git_mkfile("mixed-submodule/sub/file", "");
/* Create the commit with sub/file as a file, and an entry for sub in the modules list */
{
git_oid tree_id, commit_id;
git_signature *sig;
git_reference *ref;
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_add_bypath(index, ".gitmodules"));
cl_git_pass(git_index_add_bypath(index, "sub/file"));
cl_git_pass(git_index_write(index));
cl_git_pass(git_index_write_tree(&tree_id, index));
cl_git_pass(git_signature_now(&sig, "Sloppy Submoduler", "sloppy@example.com"));
cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
cl_git_pass(git_commit_create(&commit_id, repo, NULL, sig, sig, NULL, "message", tree, 0, NULL));
cl_git_pass(git_reference_create(&ref, repo, "refs/heads/master", &commit_id, 1, "commit: foo"));
git_reference_free(ref);
git_signature_free(sig);
}
cl_git_pass(git_diff_tree_to_index(&diff, repo, tree, index, NULL));
cl_assert_equal_i(0, git_diff_num_deltas(diff));
git_diff_free(diff);
cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, NULL));
cl_assert_equal_i(0, git_diff_num_deltas(diff));
git_diff_free(diff);
cl_git_pass(git_status_list_new(&status, repo, NULL));
cl_assert_equal_i(0, git_status_list_entrycount(status));
git_status_list_free(status);
git_index_free(index);
git_tree_free(tree);
git_repository_free(repo);
}
void test_status_submodules__mixed_case(void)
{
git_status_list *status;
git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
const git_status_entry *s;
size_t i;
status_opts.flags =
GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_INCLUDE_IGNORED |
GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS |
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR |
GIT_STATUS_OPT_RENAMES_FROM_REWRITES |
GIT_STATUS_OPT_INCLUDE_UNREADABLE |
GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED;
g_repo = setup_fixture_submod3();
cl_git_pass(git_status_list_new(&status, g_repo, &status_opts));
for (i = 0; i < git_status_list_entrycount(status); i++) {
s = git_status_byindex(status, i);
if (s->head_to_index &&
strcmp(s->head_to_index->old_file.path, ".gitmodules") == 0)
continue;
cl_assert_equal_i(0, s->status);
}
git_status_list_free(status);
}
File diff suppressed because it is too large Load Diff
+338
View File
@@ -0,0 +1,338 @@
#include "clar_libgit2.h"
#include "git2/sys/repository.h"
#include "futils.h"
#include "ignore.h"
#include "status_helpers.h"
#include "posix.h"
#include "util.h"
#include "path.h"
static void cleanup_new_repo(void *path)
{
cl_fixture_cleanup((char *)path);
}
void test_status_worktree_init__cannot_retrieve_the_status_of_a_bare_repository(void)
{
git_repository *repo;
unsigned int status = 0;
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
cl_assert_equal_i(GIT_EBAREREPO, git_status_file(&status, repo, "dummy"));
git_repository_free(repo);
}
void test_status_worktree_init__first_commit_in_progress(void)
{
git_repository *repo;
git_index *index;
status_entry_single result;
cl_set_cleanup(&cleanup_new_repo, "getting_started");
cl_git_pass(git_repository_init(&repo, "getting_started", 0));
cl_git_mkfile("getting_started/testfile.txt", "content\n");
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(1, result.count);
cl_assert(result.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_add_bypath(index, "testfile.txt"));
cl_git_pass(git_index_write(index));
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(1, result.count);
cl_assert(result.status == GIT_STATUS_INDEX_NEW);
git_index_free(index);
git_repository_free(repo);
}
void test_status_worktree_init__status_file_without_index_or_workdir(void)
{
git_repository *repo;
unsigned int status = 0;
git_index *index;
cl_git_pass(p_mkdir("wd", 0777));
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
cl_git_pass(git_repository_set_workdir(repo, "wd", false));
cl_git_pass(git_index_open(&index, "empty-index"));
cl_assert_equal_i(0, (int)git_index_entrycount(index));
git_repository_set_index(repo, index);
cl_git_pass(git_status_file(&status, repo, "branch_file.txt"));
cl_assert_equal_i(GIT_STATUS_INDEX_DELETED, status);
git_repository_free(repo);
git_index_free(index);
cl_git_pass(p_rmdir("wd"));
}
static void fill_index_wth_head_entries(git_repository *repo, git_index *index)
{
git_oid oid;
git_commit *commit;
git_tree *tree;
cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
cl_git_pass(git_commit_tree(&tree, commit));
cl_git_pass(git_index_read_tree(index, tree));
cl_git_pass(git_index_write(index));
git_tree_free(tree);
git_commit_free(commit);
}
void test_status_worktree_init__status_file_with_clean_index_and_empty_workdir(void)
{
git_repository *repo;
unsigned int status = 0;
git_index *index;
cl_git_pass(p_mkdir("wd", 0777));
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
cl_git_pass(git_repository_set_workdir(repo, "wd", false));
cl_git_pass(git_index_open(&index, "my-index"));
fill_index_wth_head_entries(repo, index);
git_repository_set_index(repo, index);
cl_git_pass(git_status_file(&status, repo, "branch_file.txt"));
cl_assert_equal_i(GIT_STATUS_WT_DELETED, status);
git_repository_free(repo);
git_index_free(index);
cl_git_pass(p_rmdir("wd"));
cl_git_pass(p_unlink("my-index"));
}
void test_status_worktree_init__bracket_in_filename(void)
{
git_repository *repo;
git_index *index;
status_entry_single result;
unsigned int status_flags;
#define FILE_WITH_BRACKET "LICENSE[1].md"
#define FILE_WITHOUT_BRACKET "LICENSE1.md"
cl_set_cleanup(&cleanup_new_repo, "with_bracket");
cl_git_pass(git_repository_init(&repo, "with_bracket", 0));
cl_git_mkfile("with_bracket/" FILE_WITH_BRACKET, "I have a bracket in my name\n");
/* file is new to working directory */
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(1, result.count);
cl_assert(result.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
cl_assert(status_flags == GIT_STATUS_WT_NEW);
/* ignore the file */
cl_git_rewritefile("with_bracket/.gitignore", "*.md\n.gitignore\n");
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(2, result.count);
cl_assert(result.status == GIT_STATUS_IGNORED);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
cl_assert(status_flags == GIT_STATUS_IGNORED);
/* don't ignore the file */
cl_git_rewritefile("with_bracket/.gitignore", ".gitignore\n");
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(2, result.count);
cl_assert(result.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
cl_assert(status_flags == GIT_STATUS_WT_NEW);
/* add the file to the index */
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_add_bypath(index, FILE_WITH_BRACKET));
cl_git_pass(git_index_write(index));
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(2, result.count);
cl_assert(result.status == GIT_STATUS_INDEX_NEW);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
/* Create file without bracket */
cl_git_mkfile("with_bracket/" FILE_WITHOUT_BRACKET, "I have no bracket in my name!\n");
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITHOUT_BRACKET));
cl_assert(status_flags == GIT_STATUS_WT_NEW);
cl_git_fail_with(git_status_file(&status_flags, repo, "LICENSE\\[1\\].md"), GIT_ENOTFOUND);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
git_index_free(index);
git_repository_free(repo);
}
void test_status_worktree_init__space_in_filename(void)
{
git_repository *repo;
git_index *index;
status_entry_single result;
unsigned int status_flags;
#define FILE_WITH_SPACE "LICENSE - copy.md"
cl_set_cleanup(&cleanup_new_repo, "with_space");
cl_git_pass(git_repository_init(&repo, "with_space", 0));
cl_git_mkfile("with_space/" FILE_WITH_SPACE, "I have a space in my name\n");
/* file is new to working directory */
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(1, result.count);
cl_assert(result.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
cl_assert(status_flags == GIT_STATUS_WT_NEW);
/* ignore the file */
cl_git_rewritefile("with_space/.gitignore", "*.md\n.gitignore\n");
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(2, result.count);
cl_assert(result.status == GIT_STATUS_IGNORED);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
cl_assert(status_flags == GIT_STATUS_IGNORED);
/* don't ignore the file */
cl_git_rewritefile("with_space/.gitignore", ".gitignore\n");
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(2, result.count);
cl_assert(result.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
cl_assert(status_flags == GIT_STATUS_WT_NEW);
/* add the file to the index */
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_add_bypath(index, FILE_WITH_SPACE));
cl_git_pass(git_index_write(index));
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(2, result.count);
cl_assert(result.status == GIT_STATUS_INDEX_NEW);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
git_index_free(index);
git_repository_free(repo);
}
static int cb_status__expected_path(const char *p, unsigned int s, void *payload)
{
const char *expected_path = (const char *)payload;
GIT_UNUSED(s);
if (payload == NULL)
cl_fail("Unexpected path");
cl_assert_equal_s(expected_path, p);
return 0;
}
void test_status_worktree_init__disable_pathspec_match(void)
{
git_repository *repo;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
char *file_with_bracket = "LICENSE[1].md",
*imaginary_file_with_bracket = "LICENSE[1-2].md";
cl_set_cleanup(&cleanup_new_repo, "pathspec");
cl_git_pass(git_repository_init(&repo, "pathspec", 0));
cl_git_mkfile("pathspec/LICENSE[1].md", "screaming bracket\n");
cl_git_mkfile("pathspec/LICENSE1.md", "no bracket\n");
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
opts.pathspec.count = 1;
opts.pathspec.strings = &file_with_bracket;
cl_git_pass(
git_status_foreach_ext(repo, &opts, cb_status__expected_path,
file_with_bracket)
);
/* Test passing a pathspec matching files in the workdir. */
/* Must not match because pathspecs are disabled. */
opts.pathspec.strings = &imaginary_file_with_bracket;
cl_git_pass(
git_status_foreach_ext(repo, &opts, cb_status__expected_path, NULL)
);
git_repository_free(repo);
}
void test_status_worktree_init__new_staged_file_must_handle_crlf(void)
{
git_repository *repo;
git_index *index;
unsigned int status;
cl_set_cleanup(&cleanup_new_repo, "getting_started");
cl_git_pass(git_repository_init(&repo, "getting_started", 0));
/* Ensure that repo has core.autocrlf=true */
cl_repo_set_bool(repo, "core.autocrlf", true);
cl_git_mkfile("getting_started/testfile.txt", "content\r\n"); /* Content with CRLF */
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_add_bypath(index, "testfile.txt"));
cl_git_pass(git_index_write(index));
cl_git_pass(git_status_file(&status, repo, "testfile.txt"));
cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
git_index_free(index);
git_repository_free(repo);
}