Commit 14997dc5 by Russell Belfer

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.
parent 5173ea92
...@@ -299,10 +299,12 @@ GIT_EXTERN(int) git_repository_init_ext( ...@@ -299,10 +299,12 @@ GIT_EXTERN(int) git_repository_init_ext(
* properties to compensate for the current filesystem of the repo. * properties to compensate for the current filesystem of the repo.
* *
* @param repo A repository object * @param repo A repository object
* @param recurse_submodules Should submodules be reset recursively
* @returrn 0 on success, < 0 on error * @returrn 0 on success, < 0 on error
*/ */
GIT_EXTERN(int) git_repository_reset_filesystem( 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. * Retrieve and resolve the reference pointed at by HEAD.
......
...@@ -783,7 +783,8 @@ static int checkout_update_index( ...@@ -783,7 +783,8 @@ static int checkout_update_index(
memset(&entry, 0, sizeof(entry)); memset(&entry, 0, sizeof(entry));
entry.path = (char *)file->path; /* cast to prevent warning */ 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); git_oid_cpy(&entry.oid, &file->oid);
return git_index_add(data->index, &entry); return git_index_add(data->index, &entry);
......
...@@ -116,4 +116,3 @@ const git_error *giterr_last(void) ...@@ -116,4 +116,3 @@ const git_error *giterr_last(void)
{ {
return GIT_GLOBAL->last_error; return GIT_GLOBAL->last_error;
} }
...@@ -78,11 +78,8 @@ int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, con ...@@ -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 git_futils_open_ro(const char *path)
{ {
int fd = p_open(path, O_RDONLY); int fd = p_open(path, O_RDONLY);
if (fd < 0) { if (fd < 0)
if (errno == ENOENT || errno == ENOTDIR) return git_path_set_error(errno, path, "open");
fd = GIT_ENOTFOUND;
giterr_set(GITERR_OS, "Failed to open '%s'", path);
}
return fd; return fd;
} }
...@@ -138,7 +135,6 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) ...@@ -138,7 +135,6 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
int git_futils_readbuffer_updated( int git_futils_readbuffer_updated(
git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated) git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
{ {
int error = 0;
git_file fd; git_file fd;
struct stat st; struct stat st;
bool changed = false; bool changed = false;
...@@ -148,13 +144,8 @@ int git_futils_readbuffer_updated( ...@@ -148,13 +144,8 @@ int git_futils_readbuffer_updated(
if (updated != NULL) if (updated != NULL)
*updated = 0; *updated = 0;
if (p_stat(path, &st) < 0) { if (p_stat(path, &st) < 0)
error = errno; return git_path_set_error(errno, path, "stat");
giterr_set(GITERR_OS, "Failed to stat '%s'", path);
if (error == ENOENT || error == ENOTDIR)
return GIT_ENOTFOUND;
return -1;
}
if (S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) { if (S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path); 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) ...@@ -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) static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
{ {
struct stat st;
futils__rmdir_data *data = opaque; futils__rmdir_data *data = opaque;
int error = data->error;
struct stat st;
if (data->depth > FUTILS_MAX_DEPTH) { if (data->depth > FUTILS_MAX_DEPTH)
data->error = error = futils__error_cannot_rmdir(
futils__error_cannot_rmdir(path->ptr, "directory nesting too deep"); 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) if (errno == ENOENT)
data->error = 0; error = 0;
else if (errno == ENOTDIR) { else if (errno == ENOTDIR) {
/* asked to remove a/b/c/d/e and a/b is a normal file */ /* asked to remove a/b/c/d/e and a/b is a normal file */
if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0) 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 else
futils__error_cannot_rmdir( futils__error_cannot_rmdir(
path->ptr, "parent is not directory"); path->ptr, "parent is not directory");
} }
else 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)) { else if (S_ISDIR(st.st_mode)) {
data->depth++; data->depth++;
{ error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data);
int error = if (error < 0)
git_path_direach(path, 0, futils__rmdir_recurs_foreach, data); return (error == GIT_EUSER) ? data->error : error;
if (error < 0)
return (error == GIT_EUSER) ? data->error : error;
}
data->depth--; data->depth--;
if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0) if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0)
return data->error; return data->error;
data->error = p_rmdir(path->ptr); if ((error = p_rmdir(path->ptr)) < 0) {
if (data->error < 0) {
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 && if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
(errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY)) (errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
data->error = 0; error = 0;
else 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) { else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
data->error = p_unlink(path->ptr); if (p_unlink(path->ptr) < 0)
error = git_path_set_error(errno, path->ptr, "remove");
if (data->error < 0)
futils__error_cannot_rmdir(path->ptr, "cannot be removed");
} }
else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0) 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) 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) ...@@ -523,7 +508,7 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
giterr_clear(); giterr_clear();
error = GIT_ITEROVER; error = GIT_ITEROVER;
} else { } 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) ...@@ -818,11 +803,8 @@ int git_futils_cp(const char *from, const char *to, mode_t filemode)
return ifd; return ifd;
if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) { 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); p_close(ifd);
return ofd; return git_path_set_error(errno, to, "open for writing");
} }
return cp_by_fd(ifd, ofd, true); return cp_by_fd(ifd, ofd, true);
...@@ -905,15 +887,14 @@ static int _cp_r_callback(void *ref, git_buf *from) ...@@ -905,15 +887,14 @@ static int _cp_r_callback(void *ref, git_buf *from)
goto exit; goto exit;
} }
if (p_lstat(info->to.ptr, &to_st) < 0) { if (!(error = git_path_lstat(info->to.ptr, &to_st)))
if (errno != ENOENT && errno != ENOTDIR) {
giterr_set(GITERR_OS,
"Could not access %s while copying files", info->to.ptr);
error = -1;
goto exit;
}
} else
exists = true; exists = true;
else if (error != GIT_ENOTFOUND)
goto exit;
else {
giterr_clear();
error = 0;
}
if ((error = git_path_lstat(from->ptr, &from_st)) < 0) if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
goto exit; goto exit;
......
...@@ -579,7 +579,8 @@ const git_index_entry *git_index_get_bypath( ...@@ -579,7 +579,8 @@ const git_index_entry *git_index_get_bypath(
return git_index_get_byindex(index, pos); 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->ctime.seconds = (git_time_t)st->st_ctime;
entry->mtime.seconds = (git_time_t)st->st_mtime; 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) ...@@ -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->ctime.nanoseconds = st->st_ctimensec; */
entry->dev = st->st_rdev; entry->dev = st->st_rdev;
entry->ino = st->st_ino; 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->uid = st->st_uid;
entry->gid = st->st_gid; entry->gid = st->st_gid;
entry->file_size = st->st_size; entry->file_size = st->st_size;
...@@ -631,7 +633,7 @@ static int index_entry_init( ...@@ -631,7 +633,7 @@ static int index_entry_init(
entry = git__calloc(1, sizeof(git_index_entry)); entry = git__calloc(1, sizeof(git_index_entry));
GITERR_CHECK_ALLOC(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->oid = oid;
entry->path = git__strdup(rel_path); entry->path = git__strdup(rel_path);
......
...@@ -48,7 +48,7 @@ struct git_index_conflict_iterator { ...@@ -48,7 +48,7 @@ struct git_index_conflict_iterator {
}; };
extern void git_index_entry__init_from_stat( 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); extern size_t git_index__prefix_position(git_index *index, const char *path);
......
...@@ -1175,7 +1175,7 @@ static int fs_iterator__update_entry(fs_iterator *fi) ...@@ -1175,7 +1175,7 @@ static int fs_iterator__update_entry(fs_iterator *fi)
return GIT_ITEROVER; return GIT_ITEROVER;
fi->entry.path = ps->path; 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 */ /* need different mode here to keep directories during iteration */
fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode); fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
......
...@@ -538,16 +538,35 @@ bool git_path_is_empty_dir(const char *path) ...@@ -538,16 +538,35 @@ bool git_path_is_empty_dir(const char *path)
#endif #endif
int git_path_lstat(const char *path, struct stat *st) int git_path_set_error(int errno_value, const char *path, const char *action)
{ {
int err = 0; switch (errno_value) {
case ENOENT:
if (p_lstat(path, st) < 0) { case ENOTDIR:
err = (errno == ENOENT) ? GIT_ENOTFOUND : -1; giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action);
giterr_set(GITERR_OS, "Failed to stat file '%s'", path); 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)
{
if (p_lstat(path, st) == 0)
return 0;
return err; return git_path_set_error(errno, path, "stat");
} }
static bool _check_dir_contents( static bool _check_dir_contents(
......
...@@ -358,6 +358,10 @@ extern int git_path_dirload_with_stat( ...@@ -358,6 +358,10 @@ extern int git_path_dirload_with_stat(
const char *end_stat, const char *end_stat,
git_vector *contents); 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 */ /* check if non-ascii characters are present in filename */
extern bool git_path_has_non_ascii(const char *path, size_t pathlen); extern bool git_path_has_non_ascii(const char *path, size_t pathlen);
......
...@@ -961,80 +961,121 @@ static int create_empty_file(const char *path, mode_t mode) ...@@ -961,80 +961,121 @@ static int create_empty_file(const char *path, mode_t mode)
return 0; return 0;
} }
static int repo_init_config( static int repo_local_config(
git_config *parent, git_config **out,
const char *repo_dir, git_buf *config_dir,
const char *work_dir, git_repository *repo,
uint32_t flags, const char *repo_dir)
uint32_t mode)
{ {
int error = 0; int error = 0;
git_buf buf = GIT_BUF_INIT; git_config *parent;
const char *cfg_path = NULL; const char *cfg_path;
git_config *config = NULL;
bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\ if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
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; return -1;
cfg_path = git_buf_cstr(&buf); cfg_path = git_buf_cstr(config_dir);
/* make LOCAL config if missing */
if (!git_path_isfile(cfg_path) && if (!git_path_isfile(cfg_path) &&
(error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0) (error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
goto cleanup; return error;
if (!parent) /* if no repo, just open that file directly */
error = git_config_open_ondisk(&config, cfg_path); if (!repo)
else if ((error = git_config_open_level( return git_config_open_ondisk(out, cfg_path);
&config, parent, GIT_CONFIG_LEVEL_LOCAL)) < 0)
{ /* 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(); giterr_clear();
if (!(error = git_config_add_file_ondisk( if (!(error = git_config_add_file_ondisk(
parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false))) parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
error = git_config_open_level( error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
&config, parent, GIT_CONFIG_LEVEL_LOCAL);
} }
if (error < 0)
goto cleanup;
if ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 && git_config_free(parent);
(error = check_repositoryformatversion(config)) < 0)
goto cleanup; 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();
SET_REPO_CONFIG( if (update_ignorecase) {
bool, "core.bare", is_bare); if (is_filesystem_case_insensitive(repo_dir)) {
SET_REPO_CONFIG( if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0)
int32, "core.repositoryformatversion", GIT_REPO_VERSION); return error;
SET_REPO_CONFIG( } else if (git_config_delete_entry(cfg, "core.ignorecase") < 0)
bool, "core.filemode", is_chmod_supported(cfg_path)); giterr_clear();
}
#ifdef GIT_USE_ICONV #ifdef GIT_USE_ICONV
SET_REPO_CONFIG( if ((error = git_config_set_bool(
bool, "core.precomposeunicode", cfg, "core.precomposeunicode",
does_fs_decompose_unicode_paths(is_bare ? repo_dir : work_dir)); does_fs_decompose_unicode_paths(work_dir))) < 0)
return error;
#endif #endif
if (!are_symlinks_supported(is_bare ? repo_dir : work_dir)) return 0;
SET_REPO_CONFIG(bool, "core.symlinks", false); }
/* core git does not do this on a reinit, but it is a property of static int repo_init_config(
* the filesystem, so I think we should... const char *repo_dir,
*/ const char *work_dir,
if (!(flags & GIT_REPOSITORY_INIT__IS_REINIT) && uint32_t flags,
is_filesystem_case_insensitive(repo_dir)) uint32_t mode)
SET_REPO_CONFIG(bool, "core.ignorecase", true); {
int error = 0;
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);
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)
SET_REPO_CONFIG(bool, "core.bare", is_bare);
SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
if ((error = repo_init_fs_configs(
config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
goto cleanup;
if (!is_bare) { if (!is_bare) {
SET_REPO_CONFIG(bool, "core.logallrefupdates", true); 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); SET_REPO_CONFIG(string, "core.worktree", work_dir);
} else if (is_reinit) {
else if ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
if (git_config_delete_entry(config, "core.worktree") < 0) if (git_config_delete_entry(config, "core.worktree") < 0)
giterr_clear(); giterr_clear();
} }
...@@ -1050,38 +1091,44 @@ static int repo_init_config( ...@@ -1050,38 +1091,44 @@ static int repo_init_config(
} }
cleanup: cleanup:
git_buf_free(&buf); git_buf_free(&cfg_path);
git_config_free(config); git_config_free(config);
return error; return error;
} }
int git_repository_reset_filesystem(git_repository *repo) static int repo_reset_submodule_fs(git_submodule *sm, const char *n, void *p)
{ {
int error = 0; git_repository *smrepo = NULL;
uint32_t flags = 0; GIT_UNUSED(n); GIT_UNUSED(p);
const char *repo_dir, *work_dir;
git_config *cfg;
assert(repo); if (git_submodule_open(&smrepo, sm) < 0 ||
git_repository_reset_filesystem(smrepo, true) < 0)
giterr_clear();
git_repository_free(smrepo);
repo_dir = git_repository_path(repo); return 0;
work_dir = git_repository_workdir(repo); }
if (git_repository_is_bare(repo)) int git_repository_reset_filesystem(git_repository *repo, int recurse)
flags |= GIT_REPOSITORY_INIT_BARE; {
else if (!git__prefixcmp(repo_dir, work_dir) && int error = 0;
!strcmp(repo_dir + strlen(work_dir), DOT_GIT "/")) git_buf path = GIT_BUF_INIT;
flags |= GIT_REPOSITORY_INIT__NATURAL_WD; git_config *config = NULL;
const char *repo_dir = git_repository_path(repo);
if ((error = git_repository_config(&cfg, repo)) < 0) if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
return error; error = repo_init_fs_configs(
config, path.ptr, repo_dir, git_repository_workdir(repo), true);
error = repo_init_config(cfg, repo_dir, work_dir, flags, 0); git_config_free(config);
git_buf_free(&path);
git_config_free(cfg);
git_repository__cvar_cache_clear(repo); git_repository__cvar_cache_clear(repo);
if (!repo->is_bare && recurse)
(void)git_submodule_foreach(repo, repo_reset_submodule_fs, NULL);
return error; return error;
} }
...@@ -1473,7 +1520,7 @@ int git_repository_init_ext( ...@@ -1473,7 +1520,7 @@ int git_repository_init_ext(
opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT; opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
error = repo_init_config( 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 */ /* TODO: reinitialize the templates */
} }
...@@ -1481,7 +1528,7 @@ int git_repository_init_ext( ...@@ -1481,7 +1528,7 @@ int git_repository_init_ext(
if (!(error = repo_init_structure( if (!(error = repo_init_structure(
repo_path.ptr, wd_path.ptr, opts)) && repo_path.ptr, wd_path.ptr, opts)) &&
!(error = repo_init_config( !(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( error = repo_init_create_head(
repo_path.ptr, opts->initial_head); repo_path.ptr, opts->initial_head);
} }
......
...@@ -372,7 +372,8 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index) ...@@ -372,7 +372,8 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
memset(&entry, 0, sizeof(entry)); memset(&entry, 0, sizeof(entry));
entry.path = sm->path; 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 */ /* calling git_submodule_open will have set sm->wd_oid if possible */
if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) { if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
......
...@@ -125,8 +125,8 @@ static int do_lstat( ...@@ -125,8 +125,8 @@ static int do_lstat(
errno = ENOENT; errno = ENOENT;
/* We need POSIX behavior, then ENOTDIR must set when any of the folders in the /* To match POSIX behavior, set ENOTDIR when any of the folders in the
* file path is a regular file,otherwise ENOENT must be set. * file path is a regular file, otherwise set ENOENT.
*/ */
if (posix_enotdir) { if (posix_enotdir) {
/* scan up path until we find an existing item */ /* scan up path until we find an existing item */
......
...@@ -191,7 +191,7 @@ git_repository *cl_git_sandbox_init(const char *sandbox) ...@@ -191,7 +191,7 @@ git_repository *cl_git_sandbox_init(const char *sandbox)
cl_git_pass(git_repository_open(&_cl_repo, sandbox)); cl_git_pass(git_repository_open(&_cl_repo, sandbox));
/* Adjust configs after copying to new filesystem */ /* 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; return _cl_repo;
} }
......
...@@ -240,7 +240,7 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) ...@@ -240,7 +240,7 @@ void test_diff_patch__hunks_have_correct_line_numbers(void)
git_repository_set_config(g_repo, cfg); git_repository_set_config(g_repo, cfg);
git_config_free(cfg); git_config_free(cfg);
git_repository_reset_filesystem(g_repo); git_repository_reset_filesystem(g_repo, false);
cl_git_pass( cl_git_pass(
git_futils_readbuffer(&old_content, "renames/songof7cities.txt")); git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
...@@ -524,7 +524,7 @@ void test_diff_patch__line_counts_with_eofnl(void) ...@@ -524,7 +524,7 @@ void test_diff_patch__line_counts_with_eofnl(void)
git_repository_set_config(g_repo, cfg); git_repository_set_config(g_repo, cfg);
git_config_free(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")); cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));
......
...@@ -761,16 +761,7 @@ void test_diff_workdir__submodules(void) ...@@ -761,16 +761,7 @@ void test_diff_workdir__submodules(void)
git_diff_list *diff = NULL; git_diff_list *diff = NULL;
diff_expects exp; diff_expects exp;
g_repo = cl_git_sandbox_init("submod2"); g_repo = setup_fixture_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");
a = resolve_commit_oid_to_tree(g_repo, a_commit); a = resolve_commit_oid_to_tree(g_repo, a_commit);
......
...@@ -111,8 +111,9 @@ static void check_stat_data(git_index *index, const char *path, bool match) ...@@ -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(st.st_gid == entry->gid);
cl_assert_equal_i_fmt( cl_assert_equal_i_fmt(
GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o"); GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o");
cl_assert_equal_b( if (cl_is_chmod_supported())
GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode)); cl_assert_equal_b(
GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode));
} else { } else {
/* most things will still match */ /* most things will still match */
cl_assert(st.st_size != entry->file_size); cl_assert(st.st_size != entry->file_size);
......
...@@ -74,7 +74,6 @@ static void add_and_check_mode_( ...@@ -74,7 +74,6 @@ static void add_and_check_mode_(
void test_index_filemodes__untrusted(void) void test_index_filemodes__untrusted(void)
{ {
git_index *index; git_index *index;
bool can_filemode = cl_is_chmod_supported();
cl_repo_set_bool(g_repo, "core.filemode", false); cl_repo_set_bool(g_repo, "core.filemode", false);
...@@ -97,20 +96,15 @@ void test_index_filemodes__untrusted(void) ...@@ -97,20 +96,15 @@ void test_index_filemodes__untrusted(void)
replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755); replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE); add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
/* these tests of newly added files won't give predictable results on /* 5 - add new 0644 -> expect 0644 */
* filesystems without actual filemode support, so skip them. cl_git_write2file("filemodes/new_off", "blah", 0,
*/ O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (can_filemode) { add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
/* 5 - add new 0644 -> expect 0644 */
cl_git_write2file("filemodes/new_off", "blah", 0, /* 6 - add new 0755 -> expect 0644 if core.filemode == false */
O_WRONLY | O_CREAT | O_TRUNC, 0644); cl_git_write2file("filemodes/new_on", "blah", 0,
add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB); O_WRONLY | O_CREAT | O_TRUNC, 0755);
add_and_check_mode(index, "new_on", 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);
}
git_index_free(index); git_index_free(index);
} }
......
...@@ -22,23 +22,38 @@ void test_refs_unicode__create_and_lookup(void) ...@@ -22,23 +22,38 @@ void test_refs_unicode__create_and_lookup(void)
git_reference *ref0, *ref1, *ref2; git_reference *ref0, *ref1, *ref2;
git_repository *repo2; 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"; const char *master = "refs/heads/master";
/* Create the reference */ /* Create the reference */
cl_git_pass(git_reference_lookup(&ref0, repo, master)); cl_git_pass(git_reference_lookup(&ref0, repo, master));
cl_git_pass(git_reference_create(&ref1, repo, REFNAME, git_reference_target(ref0), 0)); cl_git_pass(git_reference_create(&ref1, repo, REFNAME, git_reference_target(ref0), 0));
cl_assert_equal_s(REFNAME, git_reference_name(ref1)); cl_assert_equal_s(REFNAME, git_reference_name(ref1));
git_reference_free(ref0);
/* Lookup the reference in a different instance of the repository */ /* Lookup the reference in a different instance of the repository */
cl_git_pass(git_repository_open(&repo2, "testrepo.git")); cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME)); cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
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);
#if GIT_USE_ICONV
/* Lookup reference by decomposed unicode name */
cl_assert(git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)) == 0); 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)); cl_assert_equal_s(REFNAME, git_reference_name(ref2));
git_reference_free(ref2);
#endif
/* Cleanup */
git_reference_free(ref0);
git_reference_free(ref1); git_reference_free(ref1);
git_reference_free(ref2);
git_repository_free(repo2); git_repository_free(repo2);
} }
...@@ -7,20 +7,7 @@ static git_repository *g_repo = NULL; ...@@ -7,20 +7,7 @@ static git_repository *g_repo = NULL;
void test_submodule_lookup__initialize(void) void test_submodule_lookup__initialize(void)
{ {
g_repo = cl_git_sandbox_init("submod2"); g_repo = setup_fixture_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");
} }
void test_submodule_lookup__simple_lookup(void) void test_submodule_lookup__simple_lookup(void)
......
...@@ -11,20 +11,7 @@ static git_repository *g_repo = NULL; ...@@ -11,20 +11,7 @@ static git_repository *g_repo = NULL;
void test_submodule_modify__initialize(void) void test_submodule_modify__initialize(void)
{ {
g_repo = cl_git_sandbox_init("submod2"); g_repo = setup_fixture_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");
} }
void test_submodule_modify__add(void) void test_submodule_modify__add(void)
......
...@@ -102,6 +102,8 @@ git_repository *setup_fixture_submodules(void) ...@@ -102,6 +102,8 @@ git_repository *setup_fixture_submodules(void)
cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git"); cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
cl_git_pass(git_repository_reset_filesystem(repo, 1));
return repo; return repo;
} }
...@@ -118,5 +120,7 @@ git_repository *setup_fixture_submod2(void) ...@@ -118,5 +120,7 @@ git_repository *setup_fixture_submod2(void)
cl_set_cleanup(cleanup_fixture_submodules, "submod2_target"); cl_set_cleanup(cleanup_fixture_submodules, "submod2_target");
cl_git_pass(git_repository_reset_filesystem(repo, 1));
return repo; return repo;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment