More filemode cleanups for FAT on MacOS

This cleans up some additional issues.  The main change is that
on a filesystem that doesn't support mode bits, libgit2 will now
create new blobs with GIT_FILEMODE_BLOB always instead of being
at the mercy to the filesystem driver to report executable or not.
This means that if "core.filemode" lies and claims that filemode
is not supported, then we will ignore the executable bit from the
filesystem.  Previously we would have allowed it.

This adds an option to the new git_repository_reset_filesystem to
recurse through submodules if desired.  There may be other types
of APIs that would like a "recurse submodules" option, but this
one is particularly useful.

This also has a number of cleanups, etc., for related things
including trying to give better error messages when problems come
up from the filesystem.  For example, the FAT filesystem driver on
MacOS appears to return errno EINVAL if you attempt to write a
filename with invalid UTF-8 in it.  We try to capture that with a
better error message now.
This commit is contained in:
Russell Belfer 2013-10-08 12:45:43 -07:00
parent 5173ea921d
commit 14997dc5f6
21 changed files with 244 additions and 209 deletions

View File

@ -299,10 +299,12 @@ GIT_EXTERN(int) git_repository_init_ext(
* properties to compensate for the current filesystem of the repo.
*
* @param repo A repository object
* @param recurse_submodules Should submodules be reset recursively
* @returrn 0 on success, < 0 on error
*/
GIT_EXTERN(int) git_repository_reset_filesystem(
git_repository *repo);
git_repository *repo,
int recurse_submodules);
/**
* Retrieve and resolve the reference pointed at by HEAD.

View File

@ -783,7 +783,8 @@ static int checkout_update_index(
memset(&entry, 0, sizeof(entry));
entry.path = (char *)file->path; /* cast to prevent warning */
git_index_entry__init_from_stat(&entry, st);
git_index_entry__init_from_stat(
&entry, st, !(git_index_caps(data->index) & GIT_INDEXCAP_NO_FILEMODE));
git_oid_cpy(&entry.oid, &file->oid);
return git_index_add(data->index, &entry);

View File

@ -116,4 +116,3 @@ const git_error *giterr_last(void)
{
return GIT_GLOBAL->last_error;
}

View File

@ -78,11 +78,8 @@ int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, con
int git_futils_open_ro(const char *path)
{
int fd = p_open(path, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT || errno == ENOTDIR)
fd = GIT_ENOTFOUND;
giterr_set(GITERR_OS, "Failed to open '%s'", path);
}
if (fd < 0)
return git_path_set_error(errno, path, "open");
return fd;
}
@ -138,7 +135,6 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
int git_futils_readbuffer_updated(
git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
{
int error = 0;
git_file fd;
struct stat st;
bool changed = false;
@ -148,13 +144,8 @@ int git_futils_readbuffer_updated(
if (updated != NULL)
*updated = 0;
if (p_stat(path, &st) < 0) {
error = errno;
giterr_set(GITERR_OS, "Failed to stat '%s'", path);
if (error == ENOENT || error == ENOTDIR)
return GIT_ENOTFOUND;
return -1;
}
if (p_stat(path, &st) < 0)
return git_path_set_error(errno, path, "stat");
if (S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
@ -441,66 +432,60 @@ static int futils__rm_first_parent(git_buf *path, const char *ceiling)
static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
{
struct stat st;
futils__rmdir_data *data = opaque;
int error = data->error;
struct stat st;
if (data->depth > FUTILS_MAX_DEPTH) {
data->error =
futils__error_cannot_rmdir(path->ptr, "directory nesting too deep");
}
if (data->depth > FUTILS_MAX_DEPTH)
error = futils__error_cannot_rmdir(
path->ptr, "directory nesting too deep");
else if ((data->error = p_lstat_posixly(path->ptr, &st)) < 0) {
else if ((error = p_lstat_posixly(path->ptr, &st)) < 0) {
if (errno == ENOENT)
data->error = 0;
error = 0;
else if (errno == ENOTDIR) {
/* asked to remove a/b/c/d/e and a/b is a normal file */
if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0)
data->error = futils__rm_first_parent(path, data->base);
error = futils__rm_first_parent(path, data->base);
else
futils__error_cannot_rmdir(
path->ptr, "parent is not directory");
}
else
futils__error_cannot_rmdir(path->ptr, "cannot access");
error = git_path_set_error(errno, path->ptr, "rmdir");
}
else if (S_ISDIR(st.st_mode)) {
data->depth++;
{
int error =
git_path_direach(path, 0, futils__rmdir_recurs_foreach, data);
if (error < 0)
return (error == GIT_EUSER) ? data->error : error;
}
error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data);
if (error < 0)
return (error == GIT_EUSER) ? data->error : error;
data->depth--;
if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0)
return data->error;
data->error = p_rmdir(path->ptr);
if (data->error < 0) {
if ((error = p_rmdir(path->ptr)) < 0) {
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
(errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
data->error = 0;
error = 0;
else
futils__error_cannot_rmdir(path->ptr, NULL);
error = git_path_set_error(errno, path->ptr, "rmdir");
}
}
else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
data->error = p_unlink(path->ptr);
if (data->error < 0)
futils__error_cannot_rmdir(path->ptr, "cannot be removed");
if (p_unlink(path->ptr) < 0)
error = git_path_set_error(errno, path->ptr, "remove");
}
else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0)
data->error = futils__error_cannot_rmdir(path->ptr, "still present");
error = futils__error_cannot_rmdir(path->ptr, "still present");
return data->error;
data->error = error;
return error;
}
static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
@ -523,7 +508,7 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
giterr_clear();
error = GIT_ITEROVER;
} else {
futils__error_cannot_rmdir(git_buf_cstr(path), NULL);
error = git_path_set_error(errno, git_buf_cstr(path), "rmdir");
}
}
@ -818,11 +803,8 @@ int git_futils_cp(const char *from, const char *to, mode_t filemode)
return ifd;
if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
if (errno == ENOENT || errno == ENOTDIR)
ofd = GIT_ENOTFOUND;
giterr_set(GITERR_OS, "Failed to open '%s' for writing", to);
p_close(ifd);
return ofd;
return git_path_set_error(errno, to, "open for writing");
}
return cp_by_fd(ifd, ofd, true);
@ -905,15 +887,14 @@ static int _cp_r_callback(void *ref, git_buf *from)
goto exit;
}
if (p_lstat(info->to.ptr, &to_st) < 0) {
if (errno != ENOENT && errno != ENOTDIR) {
giterr_set(GITERR_OS,
"Could not access %s while copying files", info->to.ptr);
error = -1;
goto exit;
}
} else
if (!(error = git_path_lstat(info->to.ptr, &to_st)))
exists = true;
else if (error != GIT_ENOTFOUND)
goto exit;
else {
giterr_clear();
error = 0;
}
if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
goto exit;

View File

@ -579,7 +579,8 @@ const git_index_entry *git_index_get_bypath(
return git_index_get_byindex(index, pos);
}
void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
void git_index_entry__init_from_stat(
git_index_entry *entry, struct stat *st, bool trust_mode)
{
entry->ctime.seconds = (git_time_t)st->st_ctime;
entry->mtime.seconds = (git_time_t)st->st_mtime;
@ -587,7 +588,8 @@ void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
/* entry->ctime.nanoseconds = st->st_ctimensec; */
entry->dev = st->st_rdev;
entry->ino = st->st_ino;
entry->mode = index_create_mode(st->st_mode);
entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ?
index_create_mode(0666) : index_create_mode(st->st_mode);
entry->uid = st->st_uid;
entry->gid = st->st_gid;
entry->file_size = st->st_size;
@ -631,7 +633,7 @@ static int index_entry_init(
entry = git__calloc(1, sizeof(git_index_entry));
GITERR_CHECK_ALLOC(entry);
git_index_entry__init_from_stat(entry, &st);
git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
entry->oid = oid;
entry->path = git__strdup(rel_path);

View File

@ -48,7 +48,7 @@ struct git_index_conflict_iterator {
};
extern void git_index_entry__init_from_stat(
git_index_entry *entry, struct stat *st);
git_index_entry *entry, struct stat *st, bool trust_mode);
extern size_t git_index__prefix_position(git_index *index, const char *path);

View File

@ -1175,7 +1175,7 @@ static int fs_iterator__update_entry(fs_iterator *fi)
return GIT_ITEROVER;
fi->entry.path = ps->path;
git_index_entry__init_from_stat(&fi->entry, &ps->st);
git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
/* need different mode here to keep directories during iteration */
fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);

View File

@ -538,16 +538,35 @@ bool git_path_is_empty_dir(const char *path)
#endif
int git_path_set_error(int errno_value, const char *path, const char *action)
{
switch (errno_value) {
case ENOENT:
case ENOTDIR:
giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action);
return GIT_ENOTFOUND;
case EINVAL:
case ENAMETOOLONG:
giterr_set(GITERR_OS, "Invalid path for filesystem '%s'", path);
return GIT_EINVALIDSPEC;
case EEXIST:
giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path);
return GIT_EEXISTS;
default:
giterr_set(GITERR_OS, "Could not %s '%s'", action, path);
return -1;
}
}
int git_path_lstat(const char *path, struct stat *st)
{
int err = 0;
if (p_lstat(path, st) == 0)
return 0;
if (p_lstat(path, st) < 0) {
err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
}
return err;
return git_path_set_error(errno, path, "stat");
}
static bool _check_dir_contents(

View File

@ -358,6 +358,10 @@ extern int git_path_dirload_with_stat(
const char *end_stat,
git_vector *contents);
/* translate errno to libgit2 error code and set error message */
extern int git_path_set_error(
int errno_value, const char *path, const char *action);
/* check if non-ascii characters are present in filename */
extern bool git_path_has_non_ascii(const char *path, size_t pathlen);

View File

@ -961,80 +961,121 @@ static int create_empty_file(const char *path, mode_t mode)
return 0;
}
static int repo_local_config(
git_config **out,
git_buf *config_dir,
git_repository *repo,
const char *repo_dir)
{
int error = 0;
git_config *parent;
const char *cfg_path;
if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
return -1;
cfg_path = git_buf_cstr(config_dir);
/* make LOCAL config if missing */
if (!git_path_isfile(cfg_path) &&
(error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
return error;
/* if no repo, just open that file directly */
if (!repo)
return git_config_open_ondisk(out, cfg_path);
/* otherwise, open parent config and get that level */
if ((error = git_repository_config__weakptr(&parent, repo)) < 0)
return error;
if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) {
giterr_clear();
if (!(error = git_config_add_file_ondisk(
parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
}
git_config_free(parent);
return error;
}
static int repo_init_fs_configs(
git_config *cfg,
const char *cfg_path,
const char *repo_dir,
const char *work_dir,
bool update_ignorecase)
{
int error = 0;
if (!work_dir)
work_dir = repo_dir;
if ((error = git_config_set_bool(
cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
return error;
if (!are_symlinks_supported(work_dir)) {
if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
return error;
} else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
giterr_clear();
if (update_ignorecase) {
if (is_filesystem_case_insensitive(repo_dir)) {
if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0)
return error;
} else if (git_config_delete_entry(cfg, "core.ignorecase") < 0)
giterr_clear();
}
#ifdef GIT_USE_ICONV
if ((error = git_config_set_bool(
cfg, "core.precomposeunicode",
does_fs_decompose_unicode_paths(work_dir))) < 0)
return error;
#endif
return 0;
}
static int repo_init_config(
git_config *parent,
const char *repo_dir,
const char *work_dir,
uint32_t flags,
uint32_t mode)
{
int error = 0;
git_buf buf = GIT_BUF_INIT;
const char *cfg_path = NULL;
git_buf cfg_path = GIT_BUF_INIT;
git_config *config = NULL;
bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
goto cleanup;
if (is_reinit && (error = check_repositoryformatversion(config)) < 0)
goto cleanup;
#define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \
if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
goto cleanup; } while (0)
if (git_buf_joinpath(&buf, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
return -1;
cfg_path = git_buf_cstr(&buf);
SET_REPO_CONFIG(bool, "core.bare", is_bare);
SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
if (!git_path_isfile(cfg_path) &&
(error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
if ((error = repo_init_fs_configs(
config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
goto cleanup;
if (!parent)
error = git_config_open_ondisk(&config, cfg_path);
else if ((error = git_config_open_level(
&config, parent, GIT_CONFIG_LEVEL_LOCAL)) < 0)
{
giterr_clear();
if (!(error = git_config_add_file_ondisk(
parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
error = git_config_open_level(
&config, parent, GIT_CONFIG_LEVEL_LOCAL);
}
if (error < 0)
goto cleanup;
if ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
(error = check_repositoryformatversion(config)) < 0)
goto cleanup;
SET_REPO_CONFIG(
bool, "core.bare", is_bare);
SET_REPO_CONFIG(
int32, "core.repositoryformatversion", GIT_REPO_VERSION);
SET_REPO_CONFIG(
bool, "core.filemode", is_chmod_supported(cfg_path));
#ifdef GIT_USE_ICONV
SET_REPO_CONFIG(
bool, "core.precomposeunicode",
does_fs_decompose_unicode_paths(is_bare ? repo_dir : work_dir));
#endif
if (!are_symlinks_supported(is_bare ? repo_dir : work_dir))
SET_REPO_CONFIG(bool, "core.symlinks", false);
/* core git does not do this on a reinit, but it is a property of
* the filesystem, so I think we should...
*/
if (!(flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
is_filesystem_case_insensitive(repo_dir))
SET_REPO_CONFIG(bool, "core.ignorecase", true);
if (!is_bare) {
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD))
SET_REPO_CONFIG(string, "core.worktree", work_dir);
}
else if ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
else if (is_reinit) {
if (git_config_delete_entry(config, "core.worktree") < 0)
giterr_clear();
}
@ -1050,38 +1091,44 @@ static int repo_init_config(
}
cleanup:
git_buf_free(&buf);
git_buf_free(&cfg_path);
git_config_free(config);
return error;
}
int git_repository_reset_filesystem(git_repository *repo)
static int repo_reset_submodule_fs(git_submodule *sm, const char *n, void *p)
{
git_repository *smrepo = NULL;
GIT_UNUSED(n); GIT_UNUSED(p);
if (git_submodule_open(&smrepo, sm) < 0 ||
git_repository_reset_filesystem(smrepo, true) < 0)
giterr_clear();
git_repository_free(smrepo);
return 0;
}
int git_repository_reset_filesystem(git_repository *repo, int recurse)
{
int error = 0;
uint32_t flags = 0;
const char *repo_dir, *work_dir;
git_config *cfg;
git_buf path = GIT_BUF_INIT;
git_config *config = NULL;
const char *repo_dir = git_repository_path(repo);
assert(repo);
if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
error = repo_init_fs_configs(
config, path.ptr, repo_dir, git_repository_workdir(repo), true);
repo_dir = git_repository_path(repo);
work_dir = git_repository_workdir(repo);
git_config_free(config);
git_buf_free(&path);
if (git_repository_is_bare(repo))
flags |= GIT_REPOSITORY_INIT_BARE;
else if (!git__prefixcmp(repo_dir, work_dir) &&
!strcmp(repo_dir + strlen(work_dir), DOT_GIT "/"))
flags |= GIT_REPOSITORY_INIT__NATURAL_WD;
if ((error = git_repository_config(&cfg, repo)) < 0)
return error;
error = repo_init_config(cfg, repo_dir, work_dir, flags, 0);
git_config_free(cfg);
git_repository__cvar_cache_clear(repo);
if (!repo->is_bare && recurse)
(void)git_submodule_foreach(repo, repo_reset_submodule_fs, NULL);
return error;
}
@ -1473,7 +1520,7 @@ int git_repository_init_ext(
opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
error = repo_init_config(
NULL, repo_path.ptr, wd_path.ptr, opts->flags, opts->mode);
repo_path.ptr, wd_path.ptr, opts->flags, opts->mode);
/* TODO: reinitialize the templates */
}
@ -1481,7 +1528,7 @@ int git_repository_init_ext(
if (!(error = repo_init_structure(
repo_path.ptr, wd_path.ptr, opts)) &&
!(error = repo_init_config(
NULL, repo_path.ptr, wd_path.ptr, opts->flags, opts->mode)))
repo_path.ptr, wd_path.ptr, opts->flags, opts->mode)))
error = repo_init_create_head(
repo_path.ptr, opts->initial_head);
}

View File

@ -372,7 +372,8 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
memset(&entry, 0, sizeof(entry));
entry.path = sm->path;
git_index_entry__init_from_stat(&entry, &st);
git_index_entry__init_from_stat(
&entry, &st, !(git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE));
/* calling git_submodule_open will have set sm->wd_oid if possible */
if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {

View File

@ -125,8 +125,8 @@ static int do_lstat(
errno = ENOENT;
/* We need POSIX behavior, then ENOTDIR must set when any of the folders in the
* file path is a regular file,otherwise ENOENT must be set.
/* To match POSIX behavior, set ENOTDIR when any of the folders in the
* file path is a regular file, otherwise set ENOENT.
*/
if (posix_enotdir) {
/* scan up path until we find an existing item */

View File

@ -191,7 +191,7 @@ git_repository *cl_git_sandbox_init(const char *sandbox)
cl_git_pass(git_repository_open(&_cl_repo, sandbox));
/* Adjust configs after copying to new filesystem */
cl_git_pass(git_repository_reset_filesystem(_cl_repo));
cl_git_pass(git_repository_reset_filesystem(_cl_repo, 0));
return _cl_repo;
}

View File

@ -240,7 +240,7 @@ void test_diff_patch__hunks_have_correct_line_numbers(void)
git_repository_set_config(g_repo, cfg);
git_config_free(cfg);
git_repository_reset_filesystem(g_repo);
git_repository_reset_filesystem(g_repo, false);
cl_git_pass(
git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
@ -524,7 +524,7 @@ void test_diff_patch__line_counts_with_eofnl(void)
git_repository_set_config(g_repo, cfg);
git_config_free(cfg);
git_repository_reset_filesystem(g_repo);
git_repository_reset_filesystem(g_repo, false);
cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));

View File

@ -761,16 +761,7 @@ void test_diff_workdir__submodules(void)
git_diff_list *diff = NULL;
diff_expects exp;
g_repo = cl_git_sandbox_init("submod2");
cl_fixture_sandbox("submod2_target");
p_rename("submod2_target/.gitted", "submod2_target/.git");
rewrite_gitmodules(git_repository_workdir(g_repo));
p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
p_rename("submod2/not/.gitted", "submod2/not/.git");
cl_fixture_cleanup("submod2_target");
g_repo = setup_fixture_submod2();
a = resolve_commit_oid_to_tree(g_repo, a_commit);

View File

@ -111,8 +111,9 @@ static void check_stat_data(git_index *index, const char *path, bool match)
cl_assert(st.st_gid == entry->gid);
cl_assert_equal_i_fmt(
GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o");
cl_assert_equal_b(
GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode));
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);

View File

@ -74,7 +74,6 @@ static void add_and_check_mode_(
void test_index_filemodes__untrusted(void)
{
git_index *index;
bool can_filemode = cl_is_chmod_supported();
cl_repo_set_bool(g_repo, "core.filemode", false);
@ -97,20 +96,15 @@ void test_index_filemodes__untrusted(void)
replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
/* these tests of newly added files won't give predictable results on
* filesystems without actual filemode support, so skip them.
*/
if (can_filemode) {
/* 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);
/* 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 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);
}
/* 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);
}

View File

@ -22,23 +22,38 @@ void test_refs_unicode__create_and_lookup(void)
git_reference *ref0, *ref1, *ref2;
git_repository *repo2;
const char *REFNAME = "refs/heads/" "\305" "ngstr" "\366" "m";
const char *REFNAME = "refs/heads/" "\303\205" "ngstr" "\303\266" "m";
const char *REFNAME_DECOMPOSED =
"refs/heads/" "A" "\314\212" "ngstro" "\314\210" "m";
const char *master = "refs/heads/master";
/* Create the reference */
cl_git_pass(git_reference_lookup(&ref0, repo, master));
cl_git_pass(git_reference_create(&ref1, repo, REFNAME, git_reference_target(ref0), 0));
cl_assert_equal_s(REFNAME, git_reference_name(ref1));
git_reference_free(ref0);
/* Lookup the reference in a different instance of the repository */
cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
cl_assert(git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)) == 0);
cl_assert_equal_i(
0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)));
cl_assert_equal_s(REFNAME, git_reference_name(ref2));
git_reference_free(ref0);
git_reference_free(ref1);
git_reference_free(ref2);
#if GIT_USE_ICONV
/* Lookup reference by decomposed unicode name */
cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME_DECOMPOSED));
cl_assert_equal_i(
0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)));
cl_assert_equal_s(REFNAME, git_reference_name(ref2));
git_reference_free(ref2);
#endif
/* Cleanup */
git_reference_free(ref1);
git_repository_free(repo2);
}

View File

@ -7,20 +7,7 @@ static git_repository *g_repo = NULL;
void test_submodule_lookup__initialize(void)
{
g_repo = cl_git_sandbox_init("submod2");
cl_fixture_sandbox("submod2_target");
p_rename("submod2_target/.gitted", "submod2_target/.git");
/* must create submod2_target before rewrite so prettify will work */
rewrite_gitmodules(git_repository_workdir(g_repo));
p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
}
void test_submodule_lookup__cleanup(void)
{
cl_git_sandbox_cleanup();
cl_fixture_cleanup("submod2_target");
g_repo = setup_fixture_submod2();
}
void test_submodule_lookup__simple_lookup(void)

View File

@ -11,20 +11,7 @@ static git_repository *g_repo = NULL;
void test_submodule_modify__initialize(void)
{
g_repo = cl_git_sandbox_init("submod2");
cl_fixture_sandbox("submod2_target");
p_rename("submod2_target/.gitted", "submod2_target/.git");
/* must create submod2_target before rewrite so prettify will work */
rewrite_gitmodules(git_repository_workdir(g_repo));
p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
}
void test_submodule_modify__cleanup(void)
{
cl_git_sandbox_cleanup();
cl_fixture_cleanup("submod2_target");
g_repo = setup_fixture_submod2();
}
void test_submodule_modify__add(void)

View File

@ -102,6 +102,8 @@ git_repository *setup_fixture_submodules(void)
cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
cl_git_pass(git_repository_reset_filesystem(repo, 1));
return repo;
}
@ -118,5 +120,7 @@ git_repository *setup_fixture_submod2(void)
cl_set_cleanup(cleanup_fixture_submodules, "submod2_target");
cl_git_pass(git_repository_reset_filesystem(repo, 1));
return repo;
}