Commit 95c148b2 by Vicent Martí

Merge pull request #1886 from libgit2/precompose-utf8

Add support for core.precomposeunicode on Mac
parents 062c95c2 867f7c9b
......@@ -68,5 +68,6 @@ Sven Strickroth
Tim Branyen
Tim Clem
Tim Harder
Torsten Bögershausen
Trent Mick
Vicent Marti
......@@ -27,9 +27,14 @@ OPTION( BUILD_EXAMPLES "Build library usage example apps" OFF )
OPTION( TAGS "Generate tags" OFF )
OPTION( PROFILE "Generate profiling information" OFF )
OPTION( ENABLE_TRACE "Enables tracing support" OFF )
OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
OPTION( ANDROID "Build for android NDK" OFF )
OPTION( ANDROID "Build for android NDK" OFF )
OPTION( USE_ICONV "Link with and use iconv library" OFF )
IF(APPLE)
SET( USE_ICONV ON )
ENDIF()
IF(MSVC)
# This option is only available when building with MSVC. By default, libgit2
......@@ -58,7 +63,13 @@ FUNCTION(TARGET_OS_LIBRARIES target)
TARGET_LINK_LIBRARIES(${target} socket nsl)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
TARGET_LINK_LIBRARIES(${target} rt)
ENDIF ()
ENDIF()
IF(USE_ICONV)
TARGET_LINK_LIBRARIES(${target} iconv)
ADD_DEFINITIONS(-DGIT_USE_ICONV)
ENDIF()
IF(THREADSAFE)
TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
ENDIF()
......
......@@ -68,3 +68,4 @@ ok Sebastian Schuberth <sschuberth@gmail.com>
ok Shawn O. Pearce <spearce@spearce.org>
ok Steffen Prohaska <prohaska@zib.de>
ok Sven Verdoolaege <skimo@kotnet.org>
ok Torsten Bögershausen <tboegi@web.de>
......@@ -453,7 +453,7 @@ GIT_EXTERN(int) git_reference_is_remote(git_reference *ref);
GIT_EXTERN(int) git_reference_is_tag(git_reference *ref);
typedef enum {
GIT_REF_FORMAT_NORMAL = 0,
GIT_REF_FORMAT_NORMAL = 0u,
/**
* Control whether one-level refnames are accepted
......@@ -461,7 +461,7 @@ typedef enum {
* components). Those are expected to be written only using
* uppercase letters and underscore (FETCH_HEAD, ...)
*/
GIT_REF_FORMAT_ALLOW_ONELEVEL = (1 << 0),
GIT_REF_FORMAT_ALLOW_ONELEVEL = (1u << 0),
/**
* Interpret the provided name as a reference pattern for a
......@@ -470,14 +470,14 @@ typedef enum {
* in place of a one full pathname component
* (e.g., foo/<star>/bar but not foo/bar<star>).
*/
GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1),
GIT_REF_FORMAT_REFSPEC_PATTERN = (1u << 1),
/**
* Interpret the name as part of a refspec in shorthand form
* so the `ONELEVEL` naming rules aren't enforced and 'master'
* becomes a valid name.
*/
GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1 << 2),
GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1u << 2),
} git_reference_normalize_t;
/**
......
......@@ -27,7 +27,6 @@ GIT_BEGIN_DECL
*/
GIT_EXTERN(int) git_repository_new(git_repository **out);
/**
* Reset all the internal state in a repository.
*
......@@ -42,6 +41,25 @@ GIT_EXTERN(int) git_repository_new(git_repository **out);
GIT_EXTERN(void) git_repository__cleanup(git_repository *repo);
/**
* Update the filesystem config settings for an open repository
*
* When a repository is initialized, config values are set based on the
* properties of the filesystem that the repository is on, such as
* "core.ignorecase", "core.filemode", "core.symlinks", etc. If the
* repository is moved to a new filesystem, these properties may no
* longer be correct and API calls may not behave as expected. This
* call reruns the phase of repository initialization that sets those
* properties to compensate for the current filesystem of the repo.
*
* @param repo A repository object
* @param recurse_submodules Should submodules be updated recursively
* @returrn 0 on success, < 0 on error
*/
GIT_EXTERN(int) git_repository_reinit_filesystem(
git_repository *repo,
int recurse_submodules);
/**
* Set the configuration file for this repository
*
* This configuration file will be used for all configuration
......
......@@ -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);
......
......@@ -391,11 +391,11 @@ int git_clone(
const char *local_path,
const git_clone_options *_options)
{
int retcode = GIT_ERROR;
int error = 0;
git_repository *repo = NULL;
git_remote *origin;
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
int remove_directory_on_failure = 0;
uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
assert(out && url && local_path);
......@@ -408,33 +408,29 @@ int git_clone(
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
giterr_set(GITERR_INVALID,
"'%s' exists and is not an empty directory", local_path);
return GIT_ERROR;
return GIT_EEXISTS;
}
/* Only remove the directory on failure if we create it */
remove_directory_on_failure = !git_path_exists(local_path);
/* Only remove the root directory on failure if we create it */
if (git_path_exists(local_path))
rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
if ((retcode = git_repository_init(&repo, local_path, options.bare)) < 0)
return retcode;
if ((error = git_repository_init(&repo, local_path, options.bare)) < 0)
return error;
if ((retcode = create_and_configure_origin(&origin, repo, url, &options)) < 0)
goto cleanup;
if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
error = git_clone_into(
repo, origin, &options.checkout_opts, options.checkout_branch);
retcode = git_clone_into(repo, origin, &options.checkout_opts, options.checkout_branch);
git_remote_free(origin);
git_remote_free(origin);
}
if (retcode < 0)
goto cleanup;
if (error < 0) {
git_repository_free(repo);
repo = NULL;
(void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
}
*out = repo;
return 0;
cleanup:
git_repository_free(repo);
if (remove_directory_on_failure)
git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
else
git_futils_cleanupdir_r(local_path);
return retcode;
return error;
}
......@@ -47,7 +47,7 @@ extern int git_config_rename_section(
* @param out the new backend
* @param path where the config file is located
*/
extern int git_config_file__ondisk(struct git_config_backend **out, const char *path);
extern int git_config_file__ondisk(git_config_backend **out, const char *path);
extern int git_config__normalize_name(const char *in, char **out);
......
......@@ -67,6 +67,7 @@ static struct map_data _cvar_maps[] = {
{"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT },
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
{"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
};
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
......
......@@ -116,4 +116,3 @@ const git_error *giterr_last(void)
{
return GIT_GLOBAL->last_error;
}
......@@ -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);
......@@ -398,8 +389,11 @@ typedef struct {
size_t baselen;
uint32_t flags;
int error;
int depth;
} futils__rmdir_data;
#define FUTILS_MAX_DEPTH 100
static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
{
if (filemsg)
......@@ -438,51 +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)
error = futils__error_cannot_rmdir(
path->ptr, "directory nesting too deep");
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)) {
int error = git_path_direach(path, futils__rmdir_recurs_foreach, data);
data->depth++;
error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data);
if (error < 0)
return (error == GIT_EUSER) ? data->error : error;
data->error = p_rmdir(path->ptr);
data->depth--;
if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0)
return data->error;
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)
......@@ -505,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");
}
}
......@@ -517,7 +520,7 @@ int git_futils_rmdir_r(
{
int error;
git_buf fullpath = GIT_BUF_INIT;
futils__rmdir_data data;
futils__rmdir_data data = { 0 };
/* build path and find "root" where we should start calling mkdir */
if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
......@@ -526,7 +529,6 @@ int git_futils_rmdir_r(
data.base = base ? base : "";
data.baselen = base ? strlen(base) : 0;
data.flags = flags;
data.error = 0;
error = futils__rmdir_recurs_foreach(&data, &fullpath);
......@@ -544,41 +546,6 @@ int git_futils_rmdir_r(
return error;
}
int git_futils_cleanupdir_r(const char *path)
{
int error;
git_buf fullpath = GIT_BUF_INIT;
futils__rmdir_data data;
if ((error = git_buf_put(&fullpath, path, strlen(path))) < 0)
goto clean_up;
data.base = "";
data.baselen = 0;
data.flags = GIT_RMDIR_REMOVE_FILES;
data.error = 0;
if (!git_path_exists(path)) {
giterr_set(GITERR_OS, "Path does not exist: %s" , path);
error = GIT_ERROR;
goto clean_up;
}
if (!git_path_isdir(path)) {
giterr_set(GITERR_OS, "Path is not a directory: %s" , path);
error = GIT_ERROR;
goto clean_up;
}
error = git_path_direach(&fullpath, futils__rmdir_recurs_foreach, &data);
if (error == GIT_EUSER)
error = data.error;
clean_up:
git_buf_free(&fullpath);
return error;
}
static int git_futils_guess_system_dirs(git_buf *out)
{
......@@ -836,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);
......@@ -923,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;
......@@ -948,9 +911,12 @@ static int _cp_r_callback(void *ref, git_buf *from)
error = _cp_r_mkdir(info, from);
/* recurse onto target directory */
if (!error && (!exists || S_ISDIR(to_st.st_mode)) &&
((error = git_path_direach(from, _cp_r_callback, info)) == GIT_EUSER))
error = info->error;
if (!error && (!exists || S_ISDIR(to_st.st_mode))) {
error = git_path_direach(from, 0, _cp_r_callback, info);
if (error == GIT_EUSER)
error = info->error;
}
if (oldmode != 0)
info->dirmode = oldmode;
......
......@@ -115,12 +115,7 @@ extern int git_futils_mkpath2file(const char *path, const mode_t mode);
* * GIT_RMDIR_EMPTY_PARENTS - remove containing directories up to base
* if removing this item leaves them empty
* * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
*
* The old values translate into the new as follows:
*
* * GIT_DIRREMOVAL_EMPTY_HIERARCHY == GIT_RMDIR_EMPTY_HIERARCHY
* * GIT_DIRREMOVAL_FILES_AND_DIRS ~= GIT_RMDIR_REMOVE_FILES
* * GIT_DIRREMOVAL_ONLY_EMPTY_DIRS == GIT_RMDIR_SKIP_NONEMPTY
* * GIT_RMDIR_SKIP_ROOT - don't remove root directory itself
*/
typedef enum {
GIT_RMDIR_EMPTY_HIERARCHY = 0,
......@@ -128,6 +123,7 @@ typedef enum {
GIT_RMDIR_SKIP_NONEMPTY = (1 << 1),
GIT_RMDIR_EMPTY_PARENTS = (1 << 2),
GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
GIT_RMDIR_SKIP_ROOT = (1 << 4),
} git_futils_rmdir_flags;
/**
......@@ -141,14 +137,6 @@ typedef enum {
extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
/**
* Remove all files and directories beneath the specified path.
*
* @param path Path to the top level directory to process.
* @return 0 on success; -1 on error.
*/
extern int git_futils_cleanupdir_r(const char *path);
/**
* Create and open a temporary file with a `_git2_` suffix.
* Writes the filename into path_out.
* @return On success, an open file descriptor, else an error code < 0.
......
......@@ -580,7 +580,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;
......@@ -588,7 +589,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;
......@@ -632,7 +634,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);
......
......@@ -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);
......
......@@ -50,7 +50,7 @@ struct git_indexer_stream {
/* Fields for calculating the packfile trailer (hash of everything before it) */
char inbuf[GIT_OID_RAWSZ];
int inbuf_len;
size_t inbuf_len;
git_hash_ctx trailer;
};
......@@ -378,13 +378,13 @@ static int do_progress_callback(git_indexer_stream *idx, git_transfer_progress *
/* Hash everything but the last 20B of input */
static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t size)
{
int to_expell, to_keep;
size_t to_expell, to_keep;
if (size == 0)
return;
/* Easy case, dump the buffer and the data minus the last 20 bytes */
if (size >= 20) {
if (size >= GIT_OID_RAWSZ) {
git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);
......@@ -402,8 +402,8 @@ static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t
}
/* We need to partially drain the buffer and then append */
to_expell = abs(size - (GIT_OID_RAWSZ - idx->inbuf_len));
to_keep = abs(idx->inbuf_len - to_expell);
to_keep = GIT_OID_RAWSZ - size;
to_expell = idx->inbuf_len - to_keep;
git_hash_update(&idx->trailer, idx->inbuf, to_expell);
......
......@@ -893,6 +893,7 @@ struct fs_iterator {
git_index_entry entry;
git_buf path;
size_t root_len;
uint32_t dirload_flags;
int depth;
int (*enter_dir_cb)(fs_iterator *self);
......@@ -986,7 +987,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
GITERR_CHECK_ALLOC(ff);
error = git_path_dirload_with_stat(
fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
fi->path.ptr, fi->root_len, fi->dirload_flags,
fi->base.start, fi->base.end, &ff->entries);
if (error < 0) {
......@@ -1174,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);
......@@ -1207,6 +1208,11 @@ static int fs_iterator__initialize(
}
fi->root_len = fi->path.size;
fi->dirload_flags =
(iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
(iterator__flag(fi, PRECOMPOSE_UNICODE) ?
GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
if ((error = fs_iterator__expand_dir(fi)) < 0) {
if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
giterr_clear();
......@@ -1329,7 +1335,7 @@ int git_iterator_for_workdir_ext(
const char *start,
const char *end)
{
int error;
int error, precompose = 0;
workdir_iterator *wi;
if (!repo_workdir) {
......@@ -1356,6 +1362,12 @@ int git_iterator_for_workdir_ext(
return error;
}
/* try to look up precompose and set flag if appropriate */
if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
giterr_clear();
else if (precompose)
wi->fi.base.flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
return fs_iterator__initialize(out, &wi->fi, repo_workdir);
}
......
......@@ -24,13 +24,15 @@ typedef enum {
typedef enum {
/** ignore case for entry sort order */
GIT_ITERATOR_IGNORE_CASE = (1 << 0),
GIT_ITERATOR_IGNORE_CASE = (1u << 0),
/** force case sensitivity for entry sort order */
GIT_ITERATOR_DONT_IGNORE_CASE = (1 << 1),
GIT_ITERATOR_DONT_IGNORE_CASE = (1u << 1),
/** return tree items in addition to blob items */
GIT_ITERATOR_INCLUDE_TREES = (1 << 2),
GIT_ITERATOR_INCLUDE_TREES = (1u << 2),
/** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */
GIT_ITERATOR_DONT_AUTOEXPAND = (1 << 3),
GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3),
/** convert precomposed unicode to decomposed unicode */
GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4),
} git_iterator_flag_t;
typedef struct {
......
......@@ -544,7 +544,7 @@ static int locate_object_short_oid(
/* Explore directory to find a unique object matching short_oid */
error = git_path_direach(
object_location, fn_locate_object_short_oid, &state);
object_location, 0, fn_locate_object_short_oid, &state);
if (error && error != GIT_EUSER)
return error;
......@@ -745,7 +745,7 @@ static int foreach_cb(void *_state, git_buf *path)
{
struct foreach_state *state = (struct foreach_state *) _state;
return git_path_direach(path, foreach_object_dir_cb, state);
return git_path_direach(path, 0, foreach_object_dir_cb, state);
}
static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
......@@ -768,7 +768,7 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb
state.data = data;
state.dir_len = git_buf_len(&buf);
error = git_path_direach(&buf, foreach_cb, &state);
error = git_path_direach(&buf, 0, foreach_cb, &state);
git_buf_free(&buf);
......
......@@ -331,7 +331,7 @@ static int pack_backend__refresh(git_odb_backend *_backend)
git_buf_sets(&path, backend->pack_folder);
/* reload all packs */
error = git_path_direach(&path, packfile_load__cb, (void *)backend);
error = git_path_direach(&path, 0, packfile_load__cb, backend);
git_buf_free(&path);
......
......@@ -483,23 +483,23 @@ bool git_path_isfile(const char *path)
bool git_path_is_empty_dir(const char *path)
{
git_buf pathbuf = GIT_BUF_INIT;
HANDLE hFind = INVALID_HANDLE_VALUE;
git_win32_path wbuf;
int wbufsz;
WIN32_FIND_DATAW ffd;
bool retval = true;
if (!git_path_isdir(path)) return false;
if (!git_path_isdir(path))
return false;
git_buf_printf(&pathbuf, "%s\\*", path);
git_win32_path_from_c(wbuf, git_buf_cstr(&pathbuf));
wbufsz = git_win32_path_from_c(wbuf, path);
if (!wbufsz || wbufsz + 2 > GIT_WIN_PATH_UTF16)
return false;
memcpy(&wbuf[wbufsz - 1], L"\\*", 3 * sizeof(wchar_t));
hFind = FindFirstFileW(wbuf, &ffd);
if (INVALID_HANDLE_VALUE == hFind) {
giterr_set(GITERR_OS, "Couldn't open '%s'", path);
git_buf_free(&pathbuf);
if (INVALID_HANDLE_VALUE == hFind)
return false;
}
do {
if (!git_path_is_dot_or_dotdotW(ffd.cFileName)) {
......@@ -509,50 +509,64 @@ bool git_path_is_empty_dir(const char *path)
} while (FindNextFileW(hFind, &ffd) != 0);
FindClose(hFind);
git_buf_free(&pathbuf);
return retval;
}
#else
bool git_path_is_empty_dir(const char *path)
static int path_found_entry(void *payload, git_buf *path)
{
DIR *dir = NULL;
struct dirent *e;
bool retval = true;
GIT_UNUSED(payload);
return !git_path_is_dot_or_dotdot(path->ptr);
}
if (!git_path_isdir(path)) return false;
bool git_path_is_empty_dir(const char *path)
{
int error;
git_buf dir = GIT_BUF_INIT;
dir = opendir(path);
if (!dir) {
giterr_set(GITERR_OS, "Couldn't open '%s'", path);
if (!git_path_isdir(path))
return false;
}
while ((e = readdir(dir)) != NULL) {
if (!git_path_is_dot_or_dotdot(e->d_name)) {
giterr_set(GITERR_INVALID,
"'%s' exists and is not an empty directory", path);
retval = false;
break;
}
}
closedir(dir);
if (!(error = git_buf_sets(&dir, path)))
error = git_path_direach(&dir, 0, path_found_entry, NULL);
return retval;
git_buf_free(&dir);
return !error;
}
#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;
if (p_lstat(path, st) < 0) {
err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
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)
{
if (p_lstat(path, st) == 0)
return 0;
return err;
return git_path_set_error(errno, path, "stat");
}
static bool _check_dir_contents(
......@@ -724,14 +738,103 @@ int git_path_cmp(
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
}
bool git_path_has_non_ascii(const char *path, size_t pathlen)
{
const uint8_t *scan = (const uint8_t *)path, *end;
for (end = scan + pathlen; scan < end; ++scan)
if (*scan & 0x80)
return true;
return false;
}
#ifdef GIT_USE_ICONV
int git_path_iconv_init_precompose(git_path_iconv_t *ic)
{
git_buf_init(&ic->buf, 0);
ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING);
return 0;
}
void git_path_iconv_clear(git_path_iconv_t *ic)
{
if (ic) {
if (ic->map != (iconv_t)-1)
iconv_close(ic->map);
git_buf_free(&ic->buf);
}
}
int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen)
{
char *nfd = *in, *nfc;
size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, rv;
int retry = 1;
if (!ic || ic->map == (iconv_t)-1 ||
!git_path_has_non_ascii(*in, *inlen))
return 0;
while (1) {
if (git_buf_grow(&ic->buf, wantlen) < 0)
return -1;
nfc = ic->buf.ptr + ic->buf.size;
nfclen = ic->buf.asize - ic->buf.size;
rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen);
ic->buf.size = (nfc - ic->buf.ptr);
if (rv != (size_t)-1)
break;
if (errno != E2BIG)
goto fail;
/* make space for 2x the remaining data to be converted
* (with per retry overhead to avoid infinite loops)
*/
wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4);
if (retry++ > 4)
goto fail;
}
ic->buf.ptr[ic->buf.size] = '\0';
*in = ic->buf.ptr;
*inlen = ic->buf.size;
return 0;
fail:
giterr_set(GITERR_OS, "Unable to convert unicode path data");
return -1;
}
#endif
#if defined(__sun) || defined(__GNU__)
typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1];
#else
typedef struct dirent path_dirent_data;
#endif
int git_path_direach(
git_buf *path,
uint32_t flags,
int (*fn)(void *, git_buf *),
void *arg)
{
int error = 0;
ssize_t wd_len;
DIR *dir;
struct dirent *de, *de_buf;
path_dirent_data de_data;
struct dirent *de, *de_buf = (struct dirent *)&de_data;
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
if (git_path_to_dir(path) < 0)
return -1;
......@@ -743,99 +846,98 @@ int git_path_direach(
return -1;
}
#if defined(__sun) || defined(__GNU__)
de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
#else
de_buf = git__malloc(sizeof(struct dirent));
#endif
if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
(void)git_path_iconv_init_precompose(&ic);
while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
int result;
char *de_path = de->d_name;
size_t de_len = strlen(de_path);
if (git_path_is_dot_or_dotdot(de->d_name))
if (git_path_is_dot_or_dotdot(de_path))
continue;
if (git_buf_puts(path, de->d_name) < 0) {
closedir(dir);
git__free(de_buf);
return -1;
}
if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0 ||
(error = git_buf_put(path, de_path, de_len)) < 0)
break;
result = fn(arg, path);
error = fn(arg, path);
git_buf_truncate(path, wd_len); /* restore path */
if (result) {
closedir(dir);
git__free(de_buf);
return GIT_EUSER;
if (error) {
error = GIT_EUSER;
break;
}
}
closedir(dir);
git__free(de_buf);
return 0;
git_path_iconv_clear(&ic);
return error;
}
int git_path_dirload(
const char *path,
size_t prefix_len,
size_t alloc_extra,
unsigned int flags,
git_vector *contents)
{
int error, need_slash;
DIR *dir;
struct dirent *de, *de_buf;
size_t path_len;
path_dirent_data de_data;
struct dirent *de, *de_buf = (struct dirent *)&de_data;
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
assert(path && contents);
assert(path != NULL && contents != NULL);
path_len = strlen(path);
assert(path_len > 0 && path_len >= prefix_len);
if (!path_len || path_len < prefix_len) {
giterr_set(GITERR_INVALID, "Invalid directory path '%s'", path);
return -1;
}
if ((dir = opendir(path)) == NULL) {
giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
return -1;
}
#if defined(__sun) || defined(__GNU__)
de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
#else
de_buf = git__malloc(sizeof(struct dirent));
#endif
if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
(void)git_path_iconv_init_precompose(&ic);
path += prefix_len;
path_len -= prefix_len;
need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
char *entry_path;
size_t entry_len;
char *entry_path, *de_path = de->d_name;
size_t alloc_size, de_len = strlen(de_path);
if (git_path_is_dot_or_dotdot(de->d_name))
if (git_path_is_dot_or_dotdot(de_path))
continue;
entry_len = strlen(de->d_name);
if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0)
break;
entry_path = git__malloc(
path_len + need_slash + entry_len + 1 + alloc_extra);
GITERR_CHECK_ALLOC(entry_path);
alloc_size = path_len + need_slash + de_len + 1 + alloc_extra;
if ((entry_path = git__calloc(alloc_size, 1)) == NULL) {
error = -1;
break;
}
if (path_len)
memcpy(entry_path, path, path_len);
if (need_slash)
entry_path[path_len] = '/';
memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);
entry_path[path_len + need_slash + entry_len] = '\0';
memcpy(&entry_path[path_len + need_slash], de_path, de_len);
if (git_vector_insert(contents, entry_path) < 0) {
closedir(dir);
git__free(de_buf);
return -1;
}
if ((error = git_vector_insert(contents, entry_path)) < 0)
break;
}
closedir(dir);
git__free(de_buf);
git_path_iconv_clear(&ic);
if (error != 0)
giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);
......@@ -858,7 +960,7 @@ int git_path_with_stat_cmp_icase(const void *a, const void *b)
int git_path_dirload_with_stat(
const char *path,
size_t prefix_len,
bool ignore_case,
unsigned int flags,
const char *start_stat,
const char *end_stat,
git_vector *contents)
......@@ -875,13 +977,14 @@ int git_path_dirload_with_stat(
return -1;
error = git_path_dirload(
path, prefix_len, sizeof(git_path_with_stat) + 1, contents);
path, prefix_len, sizeof(git_path_with_stat) + 1, flags, contents);
if (error < 0) {
git_buf_free(&full);
return error;
}
strncomp = ignore_case ? git__strncasecmp : git__strncmp;
strncomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ?
git__strncasecmp : git__strncmp;
/* stat struct at start of git_path_with_stat, so shift path text */
git_vector_foreach(contents, i, ps) {
......
......@@ -242,21 +242,28 @@ extern int git_path_resolve_relative(git_buf *path, size_t ceiling);
*/
extern int git_path_apply_relative(git_buf *target, const char *relpath);
enum {
GIT_PATH_DIR_IGNORE_CASE = (1u << 0),
GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1),
};
/**
* Walk each directory entry, except '.' and '..', calling fn(state).
*
* @param pathbuf buffer the function reads the initial directory
* @param pathbuf Buffer the function reads the initial directory
* path from, and updates with each successive entry's name.
* @param fn function to invoke with each entry. The first arg is
* the input state and the second arg is pathbuf. The function
* may modify the pathbuf, but only by appending new text.
* @param state to pass to fn as the first arg.
* @param flags Combination of GIT_PATH_DIR flags.
* @param callback Callback for each entry. Passed the `payload` and each
* successive path inside the directory as a full path. This may
* safely append text to the pathbuf if needed.
* @param payload Passed to callback as first argument.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/
extern int git_path_direach(
git_buf *pathbuf,
int (*fn)(void *, git_buf *),
void *state);
uint32_t flags,
int (*callback)(void *payload, git_buf *path),
void *payload);
/**
* Sort function to order two paths
......@@ -276,19 +283,19 @@ extern int git_path_cmp(
* @param pathbuf Buffer the function reads the directory from and
* and updates with each successive name.
* @param ceiling Prefix of path at which to stop walking up. If NULL,
* this will walk all the way up to the root. If not a prefix of
* pathbuf, the callback will be invoked a single time on the
* original input path.
* @param fn Function to invoke on each path. The first arg is the
* input satte and the second arg is the pathbuf. The function
* should not modify the pathbuf.
* this will walk all the way up to the root. If not a prefix of
* pathbuf, the callback will be invoked a single time on the
* original input path.
* @param callback Function to invoke on each path. Passed the `payload`
* and the buffer containing the current path. The path should not
* be modified in any way.
* @param state Passed to fn as the first ath.
*/
extern int git_path_walk_up(
git_buf *pathbuf,
const char *ceiling,
int (*fn)(void *state, git_buf *),
void *state);
int (*callback)(void *payload, git_buf *path),
void *payload);
/**
* Load all directory entries (except '.' and '..') into a vector.
......@@ -304,12 +311,14 @@ extern int git_path_walk_up(
* prefix_len 3, the entries will look like "b/e1", "b/e2", etc.
* @param alloc_extra Extra bytes to add to each string allocation in
* case you want to append anything funny.
* @param flags Combination of GIT_PATH_DIR flags.
* @param contents Vector to fill with directory entry names.
*/
extern int git_path_dirload(
const char *path,
size_t prefix_len,
size_t alloc_extra,
uint32_t flags,
git_vector *contents);
......@@ -336,7 +345,7 @@ extern int git_path_with_stat_cmp_icase(const void *a, const void *b);
*
* @param path The directory to read from
* @param prefix_len The trailing part of path to prefix to entry paths
* @param ignore_case How to sort and compare paths with start/end limits
* @param flags GIT_PATH_DIR flags from above
* @param start_stat As optimization, only stat values after this prefix
* @param end_stat As optimization, only stat values before this prefix
* @param contents Vector to fill with git_path_with_stat structures
......@@ -344,9 +353,60 @@ extern int git_path_with_stat_cmp_icase(const void *a, const void *b);
extern int git_path_dirload_with_stat(
const char *path,
size_t prefix_len,
bool ignore_case,
uint32_t flags,
const char *start_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);
#define GIT_PATH_REPO_ENCODING "UTF-8"
#ifdef __APPLE__
#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC"
#else
#define GIT_PATH_NATIVE_ENCODING "UTF-8"
#endif
#ifdef GIT_USE_ICONV
#include <iconv.h>
typedef struct {
iconv_t map;
git_buf buf;
} git_path_iconv_t;
#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_BUF_INIT }
/* Init iconv data for converting decomposed UTF-8 to precomposed */
extern int git_path_iconv_init_precompose(git_path_iconv_t *ic);
/* Clear allocated iconv data */
extern void git_path_iconv_clear(git_path_iconv_t *ic);
/*
* Rewrite `in` buffer using iconv map if necessary, replacing `in`
* pointer internal iconv buffer if rewrite happened. The `in` pointer
* will be left unchanged if no rewrite was needed.
*/
extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen);
#else
typedef struct {
int unused;
} git_path_iconv_t;
#define GIT_PATH_ICONV_INIT { 0 }
#define git_path_iconv_init_precompose(X) 0
#define git_path_iconv_clear(X) (void)(X)
#define git_path_iconv(X,Y,Z) 0
#endif /* GIT_USE_ICONV */
#endif
......@@ -56,6 +56,8 @@ typedef struct refdb_fs_backend {
git_sortedcache *refcache;
int peeling_mode;
git_iterator_flag_t iterator_flags;
uint32_t direach_flags;
} refdb_fs_backend;
static int packref_cmp(const void *a_, const void *b_)
......@@ -269,7 +271,8 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
return 0;
if (git_path_isdir(full_path->ptr))
return git_path_direach(full_path, _dirent_loose_load, backend);
return git_path_direach(
full_path, backend->direach_flags, _dirent_loose_load, backend);
file_path = full_path->ptr + strlen(backend->path);
......@@ -295,7 +298,8 @@ static int packed_loadloose(refdb_fs_backend *backend)
* This will overwrite any old packed entries with their
* updated loose versions
*/
error = git_path_direach(&refs_path, _dirent_loose_load, backend);
error = git_path_direach(
&refs_path, backend->direach_flags, _dirent_loose_load, backend);
git_buf_free(&refs_path);
......@@ -468,7 +472,7 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 ||
(error = git_iterator_for_filesystem(
&fsit, git_buf_cstr(&path), 0, NULL, NULL)) < 0) {
&fsit, path.ptr, backend->iterator_flags, NULL, NULL)) < 0) {
git_buf_free(&path);
return error;
}
......@@ -1071,6 +1075,7 @@ int git_refdb_backend_fs(
git_refdb_backend **backend_out,
git_repository *repository)
{
int t = 0;
git_buf path = GIT_BUF_INIT;
refdb_fs_backend *backend;
......@@ -1092,6 +1097,15 @@ int git_refdb_backend_fs(
git_buf_free(&path);
if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_IGNORECASE) && t) {
backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
backend->direach_flags |= GIT_PATH_DIR_IGNORE_CASE;
}
if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_PRECOMPOSE) && t) {
backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
}
backend->parent.exists = &refdb_fs_backend__exists;
backend->parent.lookup = &refdb_fs_backend__lookup;
backend->parent.iterator = &refdb_fs_backend__iterator;
......
......@@ -138,6 +138,22 @@ int git_reference_name_to_id(
return 0;
}
static int reference_normalize_for_repo(
char *out,
size_t out_size,
git_repository *repo,
const char *name)
{
int precompose;
unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;
if (!git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) &&
precompose)
flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
return git_reference_normalize_name(out, out_size, name, flags);
}
int git_reference_lookup_resolved(
git_reference **ref_out,
git_repository *repo,
......@@ -159,13 +175,13 @@ int git_reference_lookup_resolved(
else if (max_nesting < 0)
max_nesting = DEFAULT_NESTING_LEVEL;
strncpy(scan_name, name, GIT_REFNAME_MAX);
scan_type = GIT_REF_SYMBOLIC;
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return -1;
if ((error = reference_normalize_for_repo(
scan_name, sizeof(scan_name), repo, name)) < 0)
return error;
if ((error = git_reference__normalize_name_lax(scan_name, GIT_REFNAME_MAX, name)) < 0)
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return error;
for (nesting = max_nesting;
......@@ -173,7 +189,7 @@ int git_reference_lookup_resolved(
nesting--)
{
if (nesting != max_nesting) {
strncpy(scan_name, ref->target.symbolic, GIT_REFNAME_MAX);
strncpy(scan_name, ref->target.symbolic, sizeof(scan_name));
git_reference_free(ref);
}
......@@ -711,17 +727,18 @@ static bool is_all_caps_and_underscore(const char *name, size_t len)
return true;
}
/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
int git_reference__normalize_name(
git_buf *buf,
const char *name,
unsigned int flags)
{
// Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
char *current;
int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
unsigned int process_flags;
bool normalize = (buf != NULL);
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
assert(name);
process_flags = flags;
......@@ -733,6 +750,13 @@ int git_reference__normalize_name(
if (normalize)
git_buf_clear(buf);
if ((flags & GIT_REF_FORMAT__PRECOMPOSE_UNICODE) != 0) {
size_t namelen = strlen(current);
if ((error = git_path_iconv_init_precompose(&ic)) < 0 ||
(error = git_path_iconv(&ic, &current, &namelen)) < 0)
goto cleanup;
}
while (true) {
segment_len = ensure_segment_validity(current);
if (segment_len < 0) {
......@@ -809,6 +833,8 @@ cleanup:
if (error && normalize)
git_buf_free(buf);
git_path_iconv_clear(&ic);
return error;
}
......@@ -983,9 +1009,9 @@ static int peel_error(int error, git_reference *ref, const char* msg)
}
int git_reference_peel(
git_object **peeled,
git_reference *ref,
git_otype target_type)
git_object **peeled,
git_reference *ref,
git_otype target_type)
{
git_reference *resolved = NULL;
git_object *target = NULL;
......@@ -1027,24 +1053,19 @@ cleanup:
return error;
}
int git_reference__is_valid_name(
const char *refname,
unsigned int flags)
int git_reference__is_valid_name(const char *refname, unsigned int flags)
{
int error;
error = git_reference__normalize_name(NULL, refname, flags) == 0;
giterr_clear();
if (git_reference__normalize_name(NULL, refname, flags) < 0) {
giterr_clear();
return false;
}
return error;
return true;
}
int git_reference_is_valid_name(
const char *refname)
int git_reference_is_valid_name(const char *refname)
{
return git_reference__is_valid_name(
refname,
GIT_REF_FORMAT_ALLOW_ONELEVEL);
return git_reference__is_valid_name(refname, GIT_REF_FORMAT_ALLOW_ONELEVEL);
}
const char *git_reference_shorthand(git_reference *ref)
......
......@@ -46,6 +46,8 @@
#define GIT_STASH_FILE "stash"
#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
#define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16)
#define GIT_REFNAME_MAX 1024
struct git_reference {
......
......@@ -37,6 +37,7 @@ typedef enum {
GIT_CVAR_IGNORESTAT, /* core.ignorestat */
GIT_CVAR_TRUSTCTIME, /* core.trustctime */
GIT_CVAR_ABBREV, /* core.abbrev */
GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
GIT_CVAR_CACHE_MAX
} git_cvar_cached;
......@@ -86,6 +87,8 @@ typedef enum {
GIT_TRUSTCTIME_DEFAULT = GIT_CVAR_TRUE,
/* core.abbrev */
GIT_ABBREV_DEFAULT = 7,
/* core.precomposeunicode */
GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
} git_cvar_value;
......
......@@ -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) {
......
......@@ -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 */
......
......@@ -224,13 +224,15 @@ void test_checkout_index__options_disable_filters(void)
void test_checkout_index__options_dir_modes(void)
{
#ifndef GIT_WIN32
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
struct stat st;
git_oid oid;
git_commit *commit;
mode_t um;
if (!cl_is_chmod_supported())
return;
cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
......@@ -252,15 +254,16 @@ void test_checkout_index__options_dir_modes(void)
cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE, "%07o");
git_commit_free(commit);
#endif
}
void test_checkout_index__options_override_file_modes(void)
{
#ifndef GIT_WIN32
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
struct stat st;
if (!cl_is_chmod_supported())
return;
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
opts.file_mode = 0700;
......@@ -268,7 +271,6 @@ void test_checkout_index__options_override_file_modes(void)
cl_git_pass(p_stat("./testrepo/new.txt", &st));
cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
#endif
}
void test_checkout_index__options_open_flags(void)
......
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "git2/sys/repository.h"
void cl_git_report_failure(
int error, const char *file, int line, const char *fncall)
......@@ -190,6 +191,9 @@ git_repository *cl_git_sandbox_init(const char *sandbox)
/* Now open the sandbox repository and make it available for tests */
cl_git_pass(git_repository_open(&_cl_repo, sandbox));
/* Adjust configs after copying to new filesystem */
cl_git_pass(git_repository_reinit_filesystem(_cl_repo, 0));
return _cl_repo;
}
......@@ -301,7 +305,7 @@ static int remove_placeholders_recurs(void *_data, git_buf *path)
size_t pathlen;
if (git_path_isdir(path->ptr) == true)
return git_path_direach(path, remove_placeholders_recurs, data);
return git_path_direach(path, 0, remove_placeholders_recurs, data);
pathlen = path->size;
......
......@@ -56,41 +56,18 @@ void test_clone_nonetwork__bad_url(void)
cl_assert(!git_path_exists("./foo"));
}
static int dont_call_me(void *state, git_buf *path)
{
GIT_UNUSED(state);
GIT_UNUSED(path);
return GIT_ERROR;
}
void test_clone_nonetwork__do_not_clean_existing_directory(void)
{
git_buf path_buf = GIT_BUF_INIT;
git_buf_put(&path_buf, "./foo", 5);
/* Clone should not remove the directory if it already exists, but
* Should clean up entries it creates. */
p_mkdir("./foo", GIT_DIR_MODE);
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
cl_assert(git_path_exists("./foo"));
/* Make sure the directory is empty. */
cl_git_pass(git_path_direach(&path_buf,
dont_call_me,
NULL));
cl_assert(git_path_is_empty_dir("./foo"));
/* Try again with a bare repository. */
g_options.bare = true;
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
cl_assert(git_path_exists("./foo"));
/* Make sure the directory is empty. */
cl_git_pass(git_path_direach(&path_buf,
dont_call_me,
NULL));
git_buf_free(&path_buf);
cl_assert(git_path_is_empty_dir("./foo"));
}
void test_clone_nonetwork__local(void)
......
......@@ -88,14 +88,6 @@ static int one_entry(void *state, git_buf *path)
return GIT_ERROR;
}
static int dont_call_me(void *state, git_buf *path)
{
GIT_UNUSED(state);
GIT_UNUSED(path);
return GIT_ERROR;
}
static name_data dot_names[] = {
{ 0, "./a" },
......@@ -115,9 +107,7 @@ void test_core_dirent__dont_traverse_dot(void)
cl_set_cleanup(&dirent_cleanup__cb, &dot);
setup(&dot);
cl_git_pass(git_path_direach(&dot.path,
one_entry,
&dot));
cl_git_pass(git_path_direach(&dot.path, 0, one_entry, &dot));
check_counts(&dot);
}
......@@ -141,9 +131,7 @@ void test_core_dirent__traverse_subfolder(void)
cl_set_cleanup(&dirent_cleanup__cb, &sub);
setup(&sub);
cl_git_pass(git_path_direach(&sub.path,
one_entry,
&sub));
cl_git_pass(git_path_direach(&sub.path, 0, one_entry, &sub));
check_counts(&sub);
}
......@@ -161,9 +149,7 @@ void test_core_dirent__traverse_slash_terminated_folder(void)
cl_set_cleanup(&dirent_cleanup__cb, &sub_slash);
setup(&sub_slash);
cl_git_pass(git_path_direach(&sub_slash.path,
one_entry,
&sub_slash));
cl_git_pass(git_path_direach(&sub_slash.path, 0, one_entry, &sub_slash));
check_counts(&sub_slash);
}
......@@ -184,16 +170,12 @@ void test_core_dirent__dont_traverse_empty_folders(void)
cl_set_cleanup(&dirent_cleanup__cb, &empty);
setup(&empty);
cl_git_pass(git_path_direach(&empty.path,
one_entry,
&empty));
cl_git_pass(git_path_direach(&empty.path, 0, one_entry, &empty));
check_counts(&empty);
/* make sure callback not called */
cl_git_pass(git_path_direach(&empty.path,
dont_call_me,
&empty));
cl_assert(git_path_is_empty_dir(empty.path.ptr));
}
static name_data odd_names[] = {
......@@ -216,9 +198,7 @@ void test_core_dirent__traverse_weird_filenames(void)
cl_set_cleanup(&dirent_cleanup__cb, &odd);
setup(&odd);
cl_git_pass(git_path_direach(&odd.path,
one_entry,
&odd));
cl_git_pass(git_path_direach(&odd.path, 0, one_entry, &odd));
check_counts(&odd);
}
......@@ -231,5 +211,26 @@ void test_core_dirent__length_limits(void)
big_filename[FILENAME_MAX] = 0;
cl_must_fail(p_creat(big_filename, 0666));
git__free(big_filename);
}
void test_core_dirent__empty_dir(void)
{
cl_must_pass(p_mkdir("empty_dir", 0777));
cl_assert(git_path_is_empty_dir("empty_dir"));
cl_git_mkfile("empty_dir/content", "whatever\n");
cl_assert(!git_path_is_empty_dir("empty_dir"));
cl_assert(!git_path_is_empty_dir("empty_dir/content"));
cl_must_pass(p_unlink("empty_dir/content"));
cl_must_pass(p_mkdir("empty_dir/content", 0777));
cl_assert(!git_path_is_empty_dir("empty_dir"));
cl_assert(git_path_is_empty_dir("empty_dir/content"));
cl_must_pass(p_rmdir("empty_dir/content"));
cl_must_pass(p_rmdir("empty_dir"));
}
#include "clar_libgit2.h"
#include "path.h"
static git_path_iconv_t ic;
static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
void test_core_iconv__initialize(void)
{
cl_git_pass(git_path_iconv_init_precompose(&ic));
}
void test_core_iconv__cleanup(void)
{
git_path_iconv_clear(&ic);
}
void test_core_iconv__unchanged(void)
{
char *data = "Ascii data", *original = data;
size_t datalen = strlen(data);
cl_git_pass(git_path_iconv(&ic, &data, &datalen));
GIT_UNUSED(datalen);
/* There are no high bits set, so this should leave data untouched */
cl_assert(data == original);
}
void test_core_iconv__decomposed_to_precomposed(void)
{
char *data = nfd;
size_t datalen = strlen(nfd);
cl_git_pass(git_path_iconv(&ic, &data, &datalen));
GIT_UNUSED(datalen);
/* The decomposed nfd string should be transformed to the nfc form
* (on platforms where iconv is enabled, of course).
*/
#ifdef GIT_USE_ICONV
cl_assert_equal_s(nfc, data);
#else
cl_assert_equal_s(nfd, data);
#endif
}
void test_core_iconv__precomposed_is_unmodified(void)
{
char *data = nfc;
size_t datalen = strlen(nfc);
cl_git_pass(git_path_iconv(&ic, &data, &datalen));
GIT_UNUSED(datalen);
/* data is already in precomposed form, so even though some bytes have
* the high-bit set, the iconv transform should result in no change.
*/
cl_assert_equal_s(nfc, data);
}
......@@ -111,14 +111,20 @@ static void cleanup_chmod_root(void *ref)
git_futils_rmdir_r("r", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
}
static void check_mode(mode_t expected, mode_t actual)
#define check_mode(X,A) check_mode_at_line((X), (A), __FILE__, __LINE__)
static void check_mode_at_line(
mode_t expected, mode_t actual, const char *file, int line)
{
#ifdef GIT_WIN32
/* chmod on Win32 doesn't support exec bit, not group/world bits */
cl_assert_equal_i_fmt((expected & 0600), (actual & 0777), "%07o");
#else
cl_assert_equal_i_fmt(expected, (actual & 0777), "%07o");
#endif
/* FAT filesystems don't support exec bit, nor group/world bits */
if (!cl_is_chmod_supported()) {
expected &= 0600;
actual &= 0600;
}
clar__assert_equal(
file, line, "expected_mode != actual_mode", 1,
"%07o", (int)expected, (int)(actual & 0777));
}
void test_core_mkdir__chmods(void)
......
#include "clar_libgit2.h"
#include <fileops.h>
#include "fileops.h"
static void
check_dirname(const char *A, const char *B)
......
......@@ -21,6 +21,35 @@ git_tree *resolve_commit_oid_to_tree(
return tree;
}
static char diff_pick_suffix(int mode)
{
if (S_ISDIR(mode))
return '/';
else if (GIT_PERMS_IS_EXEC(mode))
return '*';
else
return ' ';
}
static void fprintf_delta(FILE *fp, const git_diff_delta *delta, float progress)
{
char code = git_diff_status_char(delta->status);
char old_suffix = diff_pick_suffix(delta->old_file.mode);
char new_suffix = diff_pick_suffix(delta->new_file.mode);
fprintf(fp, "%c\t%s", code, delta->old_file.path);
if ((delta->old_file.path != delta->new_file.path &&
strcmp(delta->old_file.path, delta->new_file.path) != 0) ||
(delta->old_file.mode != delta->new_file.mode &&
delta->old_file.mode != 0 && delta->new_file.mode != 0))
fprintf(fp, "%c %s%c", old_suffix, delta->new_file.path, new_suffix);
else if (old_suffix != ' ')
fprintf(fp, "%c", old_suffix);
fprintf(fp, "\t[%.2f]\n", progress);
}
int diff_file_cb(
const git_diff_delta *delta,
float progress,
......@@ -29,9 +58,7 @@ int diff_file_cb(
diff_expects *e = payload;
if (e->debug)
fprintf(stderr, "%c %s (%.3f)\n",
git_diff_status_char(delta->status),
delta->old_file.path, progress);
fprintf_delta(stderr, delta, progress);
if (e->names)
cl_assert_equal_s(e->names[e->files], delta->old_file.path);
......@@ -55,8 +82,14 @@ int diff_print_file_cb(
float progress,
void *payload)
{
fprintf(stderr, "%c %s\n",
git_diff_status_char(delta->status), delta->old_file.path);
if (!payload) {
fprintf_delta(stderr, delta, progress);
return 0;
}
if (!((diff_expects *)payload)->debug)
fprintf_delta(stderr, delta, progress);
return diff_file_cb(delta, progress, payload);
}
......
......@@ -27,25 +27,25 @@ void test_diff_diffiter__create(void)
git_diff_list_free(diff);
}
void test_diff_diffiter__iterate_files(void)
void test_diff_diffiter__iterate_files_1(void)
{
git_repository *repo = cl_git_sandbox_init("attr");
git_diff_list *diff;
size_t d, num_d;
int count = 0;
diff_expects exp = { 0 };
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
num_d = git_diff_num_deltas(diff);
cl_assert_equal_i(6, (int)num_d);
for (d = 0; d < num_d; ++d) {
const git_diff_delta *delta;
cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
cl_assert(delta != NULL);
count++;
diff_file_cb(delta, (float)d / (float)num_d, &exp);
}
cl_assert_equal_i(6, count);
cl_assert_equal_sz(6, exp.files);
git_diff_list_free(diff);
}
......
......@@ -147,6 +147,13 @@ void test_diff_drivers__long_lines(void)
cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
cl_git_pass(git_diff_patch_to_str(&actual, patch));
/* if chmod not supported, overwrite mode bits since anything is possible */
if (!cl_is_chmod_supported()) {
size_t actual_len = strlen(actual);
if (actual_len > 72 && memcmp(&actual[66], "100644", 6) != 0)
memcpy(&actual[66], "100644", 6);
}
cl_assert_equal_s(expected, actual);
free(actual);
......
......@@ -238,6 +238,9 @@ void test_diff_patch__hunks_have_correct_line_numbers(void)
cl_git_pass(git_config_new(&cfg));
git_repository_set_config(g_repo, cfg);
git_config_free(cfg);
git_repository_reinit_filesystem(g_repo, false);
cl_git_pass(
git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
......@@ -408,7 +411,6 @@ void test_diff_patch__hunks_have_correct_line_numbers(void)
git_buf_free(&actual);
git_buf_free(&old_content);
git_tree_free(head);
git_config_free(cfg);
}
static void check_single_patch_stats(
......@@ -520,6 +522,9 @@ void test_diff_patch__line_counts_with_eofnl(void)
cl_git_pass(git_config_new(&cfg));
git_repository_set_config(g_repo, cfg);
git_config_free(cfg);
git_repository_reinit_filesystem(g_repo, false);
cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));
......@@ -574,5 +579,4 @@ void test_diff_patch__line_counts_with_eofnl(void)
g_repo, 1, 1, 1, 6, expected_sizes, expected);
git_buf_free(&content);
git_config_free(cfg);
}
......@@ -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);
......
......@@ -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);
......
......@@ -51,24 +51,29 @@ static void replace_file_with_mode(
git_buf_free(&content);
}
static void add_and_check_mode(
git_index *index, const char *filename, unsigned int expect_mode)
#define add_and_check_mode(I,F,X) add_and_check_mode_(I,F,X,__FILE__,__LINE__)
static void add_and_check_mode_(
git_index *index, const char *filename, unsigned int expect_mode,
const char *file, int line)
{
size_t pos;
const git_index_entry *entry;
cl_git_pass(git_index_add_bypath(index, filename));
cl_assert(!git_index_find(&pos, index, filename));
clar__assert(!git_index_find(&pos, index, filename),
file, line, "Cannot find index entry", NULL, 1);
entry = git_index_get_byindex(index, pos);
cl_assert(entry->mode == expect_mode);
clar__assert_equal(file, line, "Expected mode does not match index",
1, "%07o", (unsigned int)entry->mode, (unsigned int)expect_mode);
}
void test_index_filemodes__untrusted(void)
{
git_index *index;
bool can_filemode = cl_is_chmod_supported();
cl_repo_set_bool(g_repo, "core.filemode", false);
......@@ -96,15 +101,10 @@ void test_index_filemodes__untrusted(void)
O_WRONLY | O_CREAT | O_TRUNC, 0644);
add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
/* this test won't give predictable results on a platform
* that doesn't support filemodes correctly, so skip it.
*/
if (can_filemode) {
/* 6 - add 0755 -> expect 0755 */
cl_git_write2file("filemodes/new_on", "blah", 0,
O_WRONLY | O_CREAT | O_TRUNC, 0755);
add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
}
/* 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);
}
......
......@@ -291,7 +291,7 @@ int merge_test_workdir(git_repository *repo, const struct merge_index_entry expe
git_buf wd = GIT_BUF_INIT;
git_buf_puts(&wd, repo->workdir);
git_path_direach(&wd, dircount, &actual_len);
git_path_direach(&wd, 0, dircount, &actual_len);
if (actual_len != expected_len)
return 0;
......
......@@ -356,7 +356,7 @@ static int push_pack_progress_cb(int stage, unsigned int current, unsigned int t
static int push_transfer_progress_cb(unsigned int current, unsigned int total, size_t bytes, void* payload)
{
int *was_called = (int *) payload;
GIT_UNUSED(current); GIT_UNUSED(total); GIT_UNUSED(bytes);
GIT_UNUSED(current); GIT_UNUSED(total); GIT_UNUSED(bytes);
*was_called = 1;
return 0;
}
......
......@@ -4,17 +4,13 @@ static git_repository *repo;
void test_refs_unicode__initialize(void)
{
cl_fixture_sandbox("testrepo.git");
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
repo = cl_git_sandbox_init("testrepo.git");
}
void test_refs_unicode__cleanup(void)
{
git_repository_free(repo);
cl_git_sandbox_cleanup();
repo = NULL;
cl_fixture_cleanup("testrepo.git");
}
void test_refs_unicode__create_and_lookup(void)
......@@ -22,23 +18,39 @@ 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 *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_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_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);
cl_assert(git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)) == 0);
#if GIT_USE_ICONV
/* Lookup reference by decomposed unicode name */
#define REFNAME_DECOMPOSED "refs/heads/" "A" "\314\212" "ngstro" "\314\210" "m"
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(ref0);
git_reference_free(ref1);
git_reference_free(ref2);
git_repository_free(repo2);
}
......@@ -179,41 +179,32 @@ void test_repo_init__additional_templates(void)
git_buf_free(&path);
}
static void assert_config_entry_on_init_bytype(const char *config_key, int expected_value, bool is_bare)
static void assert_config_entry_on_init_bytype(
const char *config_key, int expected_value, bool is_bare)
{
git_config *config;
int current_value;
git_buf repo_path = GIT_BUF_INIT;
int error, current_value;
const char *repo_path = is_bare ?
"config_entry/test.bare.git" : "config_entry/test.non.bare.git";
cl_set_cleanup(&cleanup_repository, "config_entry");
cl_git_pass(git_buf_puts(&repo_path, "config_entry/test."));
if (!is_bare)
cl_git_pass(git_buf_puts(&repo_path, "non."));
cl_git_pass(git_buf_puts(&repo_path, "bare.git"));
cl_git_pass(git_repository_init(&_repo, git_buf_cstr(&repo_path), is_bare));
cl_git_pass(git_repository_init(&_repo, repo_path, is_bare));
git_buf_free(&repo_path);
git_repository_config(&config, _repo);
cl_git_pass(git_repository_config(&config, _repo));
error = git_config_get_bool(&current_value, config, config_key);
git_config_free(config);
if (expected_value >= 0) {
cl_git_pass(git_config_get_bool(&current_value, config, config_key));
cl_assert_equal_i(0, error);
cl_assert_equal_i(expected_value, current_value);
} else {
int error = git_config_get_bool(&current_value, config, config_key);
cl_assert_equal_i(expected_value, error);
}
git_config_free(config);
}
static void assert_config_entry_on_init(const char *config_key, int expected_value)
static void assert_config_entry_on_init(
const char *config_key, int expected_value)
{
assert_config_entry_on_init_bytype(config_key, expected_value, true);
git_repository_free(_repo);
......@@ -223,21 +214,36 @@ static void assert_config_entry_on_init(const char *config_key, int expected_val
void test_repo_init__detect_filemode(void)
{
#ifdef GIT_WIN32
assert_config_entry_on_init("core.filemode", false);
#else
assert_config_entry_on_init("core.filemode", true);
#endif
assert_config_entry_on_init("core.filemode", cl_is_chmod_supported());
}
#define CASE_INSENSITIVE_FILESYSTEM (defined GIT_WIN32 || defined __APPLE__)
void test_repo_init__detect_ignorecase(void)
{
#if CASE_INSENSITIVE_FILESYSTEM
assert_config_entry_on_init("core.ignorecase", true);
struct stat st;
bool found_without_match;
cl_git_write2file("testCAPS", "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
found_without_match = (p_stat("Testcaps", &st) == 0);
cl_must_pass(p_unlink("testCAPS"));
assert_config_entry_on_init(
"core.ignorecase", found_without_match ? true : GIT_ENOTFOUND);
}
void test_repo_init__detect_precompose_unicode_required(void)
{
char *composed = "ḱṷṓn", *decomposed = "ḱṷṓn";
struct stat st;
bool found_with_nfd;
cl_git_write2file(composed, "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
found_with_nfd = (p_stat(decomposed, &st) == 0);
cl_must_pass(p_unlink(composed));
#ifdef GIT_USE_ICONV
assert_config_entry_on_init("core.precomposeunicode", found_with_nfd);
#else
assert_config_entry_on_init("core.ignorecase", GIT_ENOTFOUND);
assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND);
#endif
}
......@@ -270,13 +276,7 @@ void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
void test_repo_init__reinit_overwrites_filemode(void)
{
int expected, current_value;
#ifdef GIT_WIN32
expected = false;
#else
expected = true;
#endif
int expected = cl_is_chmod_supported(), current_value;
/* Init a new repo */
cl_set_cleanup(&cleanup_repository, "overwrite.git");
......@@ -348,7 +348,10 @@ void test_repo_init__extended_1(void)
cl_git_pass(git_path_lstat(git_repository_path(_repo), &st));
cl_assert(S_ISDIR(st.st_mode));
cl_assert((S_ISGID & st.st_mode) == S_ISGID);
if (cl_is_chmod_supported())
cl_assert((S_ISGID & st.st_mode) == S_ISGID);
else
cl_assert((S_ISGID & st.st_mode) == 0);
cl_git_pass(git_reference_lookup(&ref, _repo, "HEAD"));
cl_assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
......
......@@ -69,14 +69,14 @@ static void test_status(
actual = git_status_byindex(status_list, i);
expected = &expected_list[i];
cl_assert_equal_i_fmt(expected->status, actual->status, "%04x");
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 (oldname)
cl_assert(git__strcmp(oldname, expected->oldname) == 0);
else
......@@ -507,14 +507,14 @@ void test_status_renames__both_casechange_two(void)
"untimely.txt", "untimeliest.txt" }
};
struct status_entry expected_case[] = {
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
"ikeepsix.txt", "ikeepsix.txt" },
{ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED,
"sixserving.txt", "SixServing.txt" },
{ 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" }
};
......
......@@ -119,7 +119,7 @@ void test_status_worktree__purged_worktree(void)
/* first purge the contents of the worktree */
cl_git_pass(git_buf_sets(&workdir, git_repository_workdir(repo)));
cl_git_pass(git_path_direach(&workdir, remove_file_cb, NULL));
cl_git_pass(git_path_direach(&workdir, 0, remove_file_cb, NULL));
git_buf_free(&workdir);
/* now get status */
......
......@@ -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)
......
......@@ -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)
......
......@@ -388,7 +388,8 @@ void test_submodule_status__iterator(void)
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
GIT_STATUS_OPT_INCLUDE_IGNORED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, confirm_submodule_status, &exp));
......
......@@ -4,6 +4,7 @@
#include "util.h"
#include "posix.h"
#include "submodule_helpers.h"
#include "git2/sys/repository.h"
/* rewrite gitmodules -> .gitmodules
* rewrite the empty or relative urls inside each module
......@@ -102,6 +103,8 @@ git_repository *setup_fixture_submodules(void)
cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
cl_git_pass(git_repository_reinit_filesystem(repo, 1));
return repo;
}
......@@ -118,5 +121,7 @@ git_repository *setup_fixture_submod2(void)
cl_set_cleanup(cleanup_fixture_submodules, "submod2_target");
cl_git_pass(git_repository_reinit_filesystem(repo, 1));
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