mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-21 22:21:37 +00:00
Merge pull request #1886 from libgit2/precompose-utf8
Add support for core.precomposeunicode on Mac
This commit is contained in:
commit
95c148b2c7
1
AUTHORS
1
AUTHORS
@ -68,5 +68,6 @@ Sven Strickroth
|
|||||||
Tim Branyen
|
Tim Branyen
|
||||||
Tim Clem
|
Tim Clem
|
||||||
Tim Harder
|
Tim Harder
|
||||||
|
Torsten Bögershausen
|
||||||
Trent Mick
|
Trent Mick
|
||||||
Vicent Marti
|
Vicent Marti
|
||||||
|
@ -27,9 +27,14 @@ OPTION( BUILD_EXAMPLES "Build library usage example apps" OFF )
|
|||||||
OPTION( TAGS "Generate tags" OFF )
|
OPTION( TAGS "Generate tags" OFF )
|
||||||
OPTION( PROFILE "Generate profiling information" OFF )
|
OPTION( PROFILE "Generate profiling information" OFF )
|
||||||
OPTION( ENABLE_TRACE "Enables tracing support" 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)
|
IF(MSVC)
|
||||||
# This option is only available when building with MSVC. By default, libgit2
|
# 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)
|
TARGET_LINK_LIBRARIES(${target} socket nsl)
|
||||||
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
TARGET_LINK_LIBRARIES(${target} rt)
|
TARGET_LINK_LIBRARIES(${target} rt)
|
||||||
ENDIF ()
|
ENDIF()
|
||||||
|
|
||||||
|
IF(USE_ICONV)
|
||||||
|
TARGET_LINK_LIBRARIES(${target} iconv)
|
||||||
|
ADD_DEFINITIONS(-DGIT_USE_ICONV)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
IF(THREADSAFE)
|
IF(THREADSAFE)
|
||||||
TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
|
TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
@ -68,3 +68,4 @@ ok Sebastian Schuberth <sschuberth@gmail.com>
|
|||||||
ok Shawn O. Pearce <spearce@spearce.org>
|
ok Shawn O. Pearce <spearce@spearce.org>
|
||||||
ok Steffen Prohaska <prohaska@zib.de>
|
ok Steffen Prohaska <prohaska@zib.de>
|
||||||
ok Sven Verdoolaege <skimo@kotnet.org>
|
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);
|
GIT_EXTERN(int) git_reference_is_tag(git_reference *ref);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GIT_REF_FORMAT_NORMAL = 0,
|
GIT_REF_FORMAT_NORMAL = 0u,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Control whether one-level refnames are accepted
|
* Control whether one-level refnames are accepted
|
||||||
@ -461,7 +461,7 @@ typedef enum {
|
|||||||
* components). Those are expected to be written only using
|
* components). Those are expected to be written only using
|
||||||
* uppercase letters and underscore (FETCH_HEAD, ...)
|
* 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
|
* Interpret the provided name as a reference pattern for a
|
||||||
@ -470,14 +470,14 @@ typedef enum {
|
|||||||
* in place of a one full pathname component
|
* in place of a one full pathname component
|
||||||
* (e.g., foo/<star>/bar but not foo/bar<star>).
|
* (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
|
* Interpret the name as part of a refspec in shorthand form
|
||||||
* so the `ONELEVEL` naming rules aren't enforced and 'master'
|
* so the `ONELEVEL` naming rules aren't enforced and 'master'
|
||||||
* becomes a valid name.
|
* becomes a valid name.
|
||||||
*/
|
*/
|
||||||
GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1 << 2),
|
GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1u << 2),
|
||||||
} git_reference_normalize_t;
|
} git_reference_normalize_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,7 +27,6 @@ GIT_BEGIN_DECL
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_repository_new(git_repository **out);
|
GIT_EXTERN(int) git_repository_new(git_repository **out);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset all the internal state in a repository.
|
* Reset all the internal state in a repository.
|
||||||
*
|
*
|
||||||
@ -41,6 +40,25 @@ GIT_EXTERN(int) git_repository_new(git_repository **out);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(void) git_repository__cleanup(git_repository *repo);
|
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
|
* Set the configuration file for this repository
|
||||||
*
|
*
|
||||||
|
@ -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);
|
||||||
|
42
src/clone.c
42
src/clone.c
@ -391,11 +391,11 @@ int git_clone(
|
|||||||
const char *local_path,
|
const char *local_path,
|
||||||
const git_clone_options *_options)
|
const git_clone_options *_options)
|
||||||
{
|
{
|
||||||
int retcode = GIT_ERROR;
|
int error = 0;
|
||||||
git_repository *repo = NULL;
|
git_repository *repo = NULL;
|
||||||
git_remote *origin;
|
git_remote *origin;
|
||||||
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
|
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);
|
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)) {
|
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
|
||||||
giterr_set(GITERR_INVALID,
|
giterr_set(GITERR_INVALID,
|
||||||
"'%s' exists and is not an empty directory", local_path);
|
"'%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 */
|
/* Only remove the root directory on failure if we create it */
|
||||||
remove_directory_on_failure = !git_path_exists(local_path);
|
if (git_path_exists(local_path))
|
||||||
|
rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
|
||||||
|
|
||||||
if ((retcode = git_repository_init(&repo, local_path, options.bare)) < 0)
|
if ((error = git_repository_init(&repo, local_path, options.bare)) < 0)
|
||||||
return retcode;
|
return error;
|
||||||
|
|
||||||
if ((retcode = create_and_configure_origin(&origin, repo, url, &options)) < 0)
|
if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
|
||||||
goto cleanup;
|
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)
|
if (error < 0) {
|
||||||
goto cleanup;
|
git_repository_free(repo);
|
||||||
|
repo = NULL;
|
||||||
|
(void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
|
||||||
|
}
|
||||||
|
|
||||||
*out = repo;
|
*out = repo;
|
||||||
return 0;
|
return error;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ extern int git_config_rename_section(
|
|||||||
* @param out the new backend
|
* @param out the new backend
|
||||||
* @param path where the config file is located
|
* @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);
|
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.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT },
|
||||||
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
|
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
|
||||||
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_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)
|
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;
|
return GIT_GLOBAL->last_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
130
src/fileops.c
130
src/fileops.c
@ -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)
|
|||||||
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(
|
|||||||
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);
|
||||||
@ -398,8 +389,11 @@ typedef struct {
|
|||||||
size_t baselen;
|
size_t baselen;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
int error;
|
int error;
|
||||||
|
int depth;
|
||||||
} futils__rmdir_data;
|
} futils__rmdir_data;
|
||||||
|
|
||||||
|
#define FUTILS_MAX_DEPTH 100
|
||||||
|
|
||||||
static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
|
static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
|
||||||
{
|
{
|
||||||
if (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)
|
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->error = p_lstat_posixly(path->ptr, &st)) < 0) {
|
if (data->depth > FUTILS_MAX_DEPTH)
|
||||||
|
error = futils__error_cannot_rmdir(
|
||||||
|
path->ptr, "directory nesting too deep");
|
||||||
|
|
||||||
|
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)) {
|
||||||
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)
|
if (error < 0)
|
||||||
return (error == GIT_EUSER) ? data->error : error;
|
return (error == GIT_EUSER) ? data->error : error;
|
||||||
|
|
||||||
data->error = p_rmdir(path->ptr);
|
data->depth--;
|
||||||
|
|
||||||
if (data->error < 0) {
|
if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0)
|
||||||
|
return data->error;
|
||||||
|
|
||||||
|
if ((error = p_rmdir(path->ptr)) < 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)
|
||||||
@ -505,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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,7 +520,7 @@ int git_futils_rmdir_r(
|
|||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
git_buf fullpath = GIT_BUF_INIT;
|
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 */
|
/* build path and find "root" where we should start calling mkdir */
|
||||||
if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
|
if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
|
||||||
@ -526,7 +529,6 @@ int git_futils_rmdir_r(
|
|||||||
data.base = base ? base : "";
|
data.base = base ? base : "";
|
||||||
data.baselen = base ? strlen(base) : 0;
|
data.baselen = base ? strlen(base) : 0;
|
||||||
data.flags = flags;
|
data.flags = flags;
|
||||||
data.error = 0;
|
|
||||||
|
|
||||||
error = futils__rmdir_recurs_foreach(&data, &fullpath);
|
error = futils__rmdir_recurs_foreach(&data, &fullpath);
|
||||||
|
|
||||||
@ -544,41 +546,6 @@ int git_futils_rmdir_r(
|
|||||||
return error;
|
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)
|
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;
|
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);
|
||||||
@ -923,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;
|
||||||
@ -948,9 +911,12 @@ static int _cp_r_callback(void *ref, git_buf *from)
|
|||||||
error = _cp_r_mkdir(info, from);
|
error = _cp_r_mkdir(info, from);
|
||||||
|
|
||||||
/* recurse onto target directory */
|
/* recurse onto target directory */
|
||||||
if (!error && (!exists || S_ISDIR(to_st.st_mode)) &&
|
if (!error && (!exists || S_ISDIR(to_st.st_mode))) {
|
||||||
((error = git_path_direach(from, _cp_r_callback, info)) == GIT_EUSER))
|
error = git_path_direach(from, 0, _cp_r_callback, info);
|
||||||
error = info->error;
|
|
||||||
|
if (error == GIT_EUSER)
|
||||||
|
error = info->error;
|
||||||
|
}
|
||||||
|
|
||||||
if (oldmode != 0)
|
if (oldmode != 0)
|
||||||
info->dirmode = oldmode;
|
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
|
* * GIT_RMDIR_EMPTY_PARENTS - remove containing directories up to base
|
||||||
* if removing this item leaves them empty
|
* if removing this item leaves them empty
|
||||||
* * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
|
* * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
|
||||||
*
|
* * GIT_RMDIR_SKIP_ROOT - don't remove root directory itself
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GIT_RMDIR_EMPTY_HIERARCHY = 0,
|
GIT_RMDIR_EMPTY_HIERARCHY = 0,
|
||||||
@ -128,6 +123,7 @@ typedef enum {
|
|||||||
GIT_RMDIR_SKIP_NONEMPTY = (1 << 1),
|
GIT_RMDIR_SKIP_NONEMPTY = (1 << 1),
|
||||||
GIT_RMDIR_EMPTY_PARENTS = (1 << 2),
|
GIT_RMDIR_EMPTY_PARENTS = (1 << 2),
|
||||||
GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
|
GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
|
||||||
|
GIT_RMDIR_SKIP_ROOT = (1 << 4),
|
||||||
} git_futils_rmdir_flags;
|
} git_futils_rmdir_flags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,14 +136,6 @@ typedef enum {
|
|||||||
*/
|
*/
|
||||||
extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
|
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.
|
* Create and open a temporary file with a `_git2_` suffix.
|
||||||
* Writes the filename into path_out.
|
* Writes the filename into path_out.
|
||||||
|
@ -580,7 +580,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;
|
||||||
@ -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->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;
|
||||||
@ -632,7 +634,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 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ struct git_indexer_stream {
|
|||||||
|
|
||||||
/* Fields for calculating the packfile trailer (hash of everything before it) */
|
/* Fields for calculating the packfile trailer (hash of everything before it) */
|
||||||
char inbuf[GIT_OID_RAWSZ];
|
char inbuf[GIT_OID_RAWSZ];
|
||||||
int inbuf_len;
|
size_t inbuf_len;
|
||||||
git_hash_ctx trailer;
|
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 */
|
/* Hash everything but the last 20B of input */
|
||||||
static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t size)
|
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)
|
if (size == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Easy case, dump the buffer and the data minus the last 20 bytes */
|
/* 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, idx->inbuf, idx->inbuf_len);
|
||||||
git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);
|
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 */
|
/* We need to partially drain the buffer and then append */
|
||||||
to_expell = abs(size - (GIT_OID_RAWSZ - idx->inbuf_len));
|
to_keep = GIT_OID_RAWSZ - size;
|
||||||
to_keep = abs(idx->inbuf_len - to_expell);
|
to_expell = idx->inbuf_len - to_keep;
|
||||||
|
|
||||||
git_hash_update(&idx->trailer, idx->inbuf, to_expell);
|
git_hash_update(&idx->trailer, idx->inbuf, to_expell);
|
||||||
|
|
||||||
|
@ -893,6 +893,7 @@ struct fs_iterator {
|
|||||||
git_index_entry entry;
|
git_index_entry entry;
|
||||||
git_buf path;
|
git_buf path;
|
||||||
size_t root_len;
|
size_t root_len;
|
||||||
|
uint32_t dirload_flags;
|
||||||
int depth;
|
int depth;
|
||||||
|
|
||||||
int (*enter_dir_cb)(fs_iterator *self);
|
int (*enter_dir_cb)(fs_iterator *self);
|
||||||
@ -986,7 +987,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
|
|||||||
GITERR_CHECK_ALLOC(ff);
|
GITERR_CHECK_ALLOC(ff);
|
||||||
|
|
||||||
error = git_path_dirload_with_stat(
|
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);
|
fi->base.start, fi->base.end, &ff->entries);
|
||||||
|
|
||||||
if (error < 0) {
|
if (error < 0) {
|
||||||
@ -1174,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);
|
||||||
@ -1207,6 +1208,11 @@ static int fs_iterator__initialize(
|
|||||||
}
|
}
|
||||||
fi->root_len = fi->path.size;
|
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 = fs_iterator__expand_dir(fi)) < 0) {
|
||||||
if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
|
if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
|
||||||
giterr_clear();
|
giterr_clear();
|
||||||
@ -1329,7 +1335,7 @@ int git_iterator_for_workdir_ext(
|
|||||||
const char *start,
|
const char *start,
|
||||||
const char *end)
|
const char *end)
|
||||||
{
|
{
|
||||||
int error;
|
int error, precompose = 0;
|
||||||
workdir_iterator *wi;
|
workdir_iterator *wi;
|
||||||
|
|
||||||
if (!repo_workdir) {
|
if (!repo_workdir) {
|
||||||
@ -1356,6 +1362,12 @@ int git_iterator_for_workdir_ext(
|
|||||||
return error;
|
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);
|
return fs_iterator__initialize(out, &wi->fi, repo_workdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,13 +24,15 @@ typedef enum {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
/** ignore case for entry sort order */
|
/** 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 */
|
/** 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 */
|
/** 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) */
|
/** 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;
|
} git_iterator_flag_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -544,7 +544,7 @@ static int locate_object_short_oid(
|
|||||||
|
|
||||||
/* Explore directory to find a unique object matching short_oid */
|
/* Explore directory to find a unique object matching short_oid */
|
||||||
error = git_path_direach(
|
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)
|
if (error && error != GIT_EUSER)
|
||||||
return error;
|
return error;
|
||||||
@ -745,7 +745,7 @@ static int foreach_cb(void *_state, git_buf *path)
|
|||||||
{
|
{
|
||||||
struct foreach_state *state = (struct foreach_state *) _state;
|
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)
|
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.data = data;
|
||||||
state.dir_len = git_buf_len(&buf);
|
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);
|
git_buf_free(&buf);
|
||||||
|
|
||||||
|
@ -331,7 +331,7 @@ static int pack_backend__refresh(git_odb_backend *_backend)
|
|||||||
git_buf_sets(&path, backend->pack_folder);
|
git_buf_sets(&path, backend->pack_folder);
|
||||||
|
|
||||||
/* reload all packs */
|
/* 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);
|
git_buf_free(&path);
|
||||||
|
|
||||||
|
265
src/path.c
265
src/path.c
@ -483,23 +483,23 @@ bool git_path_isfile(const char *path)
|
|||||||
|
|
||||||
bool git_path_is_empty_dir(const char *path)
|
bool git_path_is_empty_dir(const char *path)
|
||||||
{
|
{
|
||||||
git_buf pathbuf = GIT_BUF_INIT;
|
|
||||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||||
git_win32_path wbuf;
|
git_win32_path wbuf;
|
||||||
|
int wbufsz;
|
||||||
WIN32_FIND_DATAW ffd;
|
WIN32_FIND_DATAW ffd;
|
||||||
bool retval = true;
|
bool retval = true;
|
||||||
|
|
||||||
if (!git_path_isdir(path)) return false;
|
if (!git_path_isdir(path))
|
||||||
|
return false;
|
||||||
|
|
||||||
git_buf_printf(&pathbuf, "%s\\*", path);
|
wbufsz = git_win32_path_from_c(wbuf, path);
|
||||||
git_win32_path_from_c(wbuf, git_buf_cstr(&pathbuf));
|
if (!wbufsz || wbufsz + 2 > GIT_WIN_PATH_UTF16)
|
||||||
|
return false;
|
||||||
|
memcpy(&wbuf[wbufsz - 1], L"\\*", 3 * sizeof(wchar_t));
|
||||||
|
|
||||||
hFind = FindFirstFileW(wbuf, &ffd);
|
hFind = FindFirstFileW(wbuf, &ffd);
|
||||||
if (INVALID_HANDLE_VALUE == hFind) {
|
if (INVALID_HANDLE_VALUE == hFind)
|
||||||
giterr_set(GITERR_OS, "Couldn't open '%s'", path);
|
|
||||||
git_buf_free(&pathbuf);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (!git_path_is_dot_or_dotdotW(ffd.cFileName)) {
|
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);
|
} while (FindNextFileW(hFind, &ffd) != 0);
|
||||||
|
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
git_buf_free(&pathbuf);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
static int path_found_entry(void *payload, git_buf *path)
|
||||||
|
{
|
||||||
|
GIT_UNUSED(payload);
|
||||||
|
return !git_path_is_dot_or_dotdot(path->ptr);
|
||||||
|
}
|
||||||
|
|
||||||
bool git_path_is_empty_dir(const char *path)
|
bool git_path_is_empty_dir(const char *path)
|
||||||
{
|
{
|
||||||
DIR *dir = NULL;
|
int error;
|
||||||
struct dirent *e;
|
git_buf dir = GIT_BUF_INIT;
|
||||||
bool retval = true;
|
|
||||||
|
|
||||||
if (!git_path_isdir(path)) return false;
|
if (!git_path_isdir(path))
|
||||||
|
|
||||||
dir = opendir(path);
|
|
||||||
if (!dir) {
|
|
||||||
giterr_set(GITERR_OS, "Couldn't open '%s'", path);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
while ((e = readdir(dir)) != NULL) {
|
if (!(error = git_buf_sets(&dir, path)))
|
||||||
if (!git_path_is_dot_or_dotdot(e->d_name)) {
|
error = git_path_direach(&dir, 0, path_found_entry, NULL);
|
||||||
giterr_set(GITERR_INVALID,
|
|
||||||
"'%s' exists and is not an empty directory", path);
|
|
||||||
retval = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
|
|
||||||
return retval;
|
git_buf_free(&dir);
|
||||||
|
|
||||||
|
return !error;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int git_path_set_error(int errno_value, const char *path, const char *action)
|
||||||
|
{
|
||||||
|
switch (errno_value) {
|
||||||
|
case ENOENT:
|
||||||
|
case ENOTDIR:
|
||||||
|
giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action);
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
|
||||||
|
case EINVAL:
|
||||||
|
case ENAMETOOLONG:
|
||||||
|
giterr_set(GITERR_OS, "Invalid path for filesystem '%s'", path);
|
||||||
|
return GIT_EINVALIDSPEC;
|
||||||
|
|
||||||
|
case EEXIST:
|
||||||
|
giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path);
|
||||||
|
return GIT_EEXISTS;
|
||||||
|
|
||||||
|
default:
|
||||||
|
giterr_set(GITERR_OS, "Could not %s '%s'", action, path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int git_path_lstat(const char *path, struct stat *st)
|
int git_path_lstat(const char *path, struct stat *st)
|
||||||
{
|
{
|
||||||
int err = 0;
|
if (p_lstat(path, st) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (p_lstat(path, st) < 0) {
|
return git_path_set_error(errno, path, "stat");
|
||||||
err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
|
|
||||||
giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _check_dir_contents(
|
static bool _check_dir_contents(
|
||||||
@ -724,14 +738,103 @@ int git_path_cmp(
|
|||||||
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
|
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(
|
int git_path_direach(
|
||||||
git_buf *path,
|
git_buf *path,
|
||||||
|
uint32_t flags,
|
||||||
int (*fn)(void *, git_buf *),
|
int (*fn)(void *, git_buf *),
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
|
int error = 0;
|
||||||
ssize_t wd_len;
|
ssize_t wd_len;
|
||||||
DIR *dir;
|
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)
|
if (git_path_to_dir(path) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
@ -743,99 +846,98 @@ int git_path_direach(
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__sun) || defined(__GNU__)
|
if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
|
||||||
de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
|
(void)git_path_iconv_init_precompose(&ic);
|
||||||
#else
|
|
||||||
de_buf = git__malloc(sizeof(struct dirent));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
|
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;
|
continue;
|
||||||
|
|
||||||
if (git_buf_puts(path, de->d_name) < 0) {
|
if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0 ||
|
||||||
closedir(dir);
|
(error = git_buf_put(path, de_path, de_len)) < 0)
|
||||||
git__free(de_buf);
|
break;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = fn(arg, path);
|
error = fn(arg, path);
|
||||||
|
|
||||||
git_buf_truncate(path, wd_len); /* restore path */
|
git_buf_truncate(path, wd_len); /* restore path */
|
||||||
|
|
||||||
if (result) {
|
if (error) {
|
||||||
closedir(dir);
|
error = GIT_EUSER;
|
||||||
git__free(de_buf);
|
break;
|
||||||
return GIT_EUSER;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
git__free(de_buf);
|
git_path_iconv_clear(&ic);
|
||||||
return 0;
|
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_path_dirload(
|
int git_path_dirload(
|
||||||
const char *path,
|
const char *path,
|
||||||
size_t prefix_len,
|
size_t prefix_len,
|
||||||
size_t alloc_extra,
|
size_t alloc_extra,
|
||||||
|
unsigned int flags,
|
||||||
git_vector *contents)
|
git_vector *contents)
|
||||||
{
|
{
|
||||||
int error, need_slash;
|
int error, need_slash;
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
struct dirent *de, *de_buf;
|
|
||||||
size_t path_len;
|
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);
|
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) {
|
if ((dir = opendir(path)) == NULL) {
|
||||||
giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
|
giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__sun) || defined(__GNU__)
|
if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
|
||||||
de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
|
(void)git_path_iconv_init_precompose(&ic);
|
||||||
#else
|
|
||||||
de_buf = git__malloc(sizeof(struct dirent));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
path += prefix_len;
|
path += prefix_len;
|
||||||
path_len -= prefix_len;
|
path_len -= prefix_len;
|
||||||
need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
|
need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
|
||||||
|
|
||||||
while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
|
while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
|
||||||
char *entry_path;
|
char *entry_path, *de_path = de->d_name;
|
||||||
size_t entry_len;
|
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;
|
continue;
|
||||||
|
|
||||||
entry_len = strlen(de->d_name);
|
if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
entry_path = git__malloc(
|
alloc_size = path_len + need_slash + de_len + 1 + alloc_extra;
|
||||||
path_len + need_slash + entry_len + 1 + alloc_extra);
|
if ((entry_path = git__calloc(alloc_size, 1)) == NULL) {
|
||||||
GITERR_CHECK_ALLOC(entry_path);
|
error = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (path_len)
|
if (path_len)
|
||||||
memcpy(entry_path, path, path_len);
|
memcpy(entry_path, path, path_len);
|
||||||
if (need_slash)
|
if (need_slash)
|
||||||
entry_path[path_len] = '/';
|
entry_path[path_len] = '/';
|
||||||
memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);
|
memcpy(&entry_path[path_len + need_slash], de_path, de_len);
|
||||||
entry_path[path_len + need_slash + entry_len] = '\0';
|
|
||||||
|
|
||||||
if (git_vector_insert(contents, entry_path) < 0) {
|
if ((error = git_vector_insert(contents, entry_path)) < 0)
|
||||||
closedir(dir);
|
break;
|
||||||
git__free(de_buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
git__free(de_buf);
|
git_path_iconv_clear(&ic);
|
||||||
|
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);
|
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(
|
int git_path_dirload_with_stat(
|
||||||
const char *path,
|
const char *path,
|
||||||
size_t prefix_len,
|
size_t prefix_len,
|
||||||
bool ignore_case,
|
unsigned int flags,
|
||||||
const char *start_stat,
|
const char *start_stat,
|
||||||
const char *end_stat,
|
const char *end_stat,
|
||||||
git_vector *contents)
|
git_vector *contents)
|
||||||
@ -875,13 +977,14 @@ int git_path_dirload_with_stat(
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
error = git_path_dirload(
|
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) {
|
if (error < 0) {
|
||||||
git_buf_free(&full);
|
git_buf_free(&full);
|
||||||
return error;
|
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 */
|
/* stat struct at start of git_path_with_stat, so shift path text */
|
||||||
git_vector_foreach(contents, i, ps) {
|
git_vector_foreach(contents, i, ps) {
|
||||||
|
94
src/path.h
94
src/path.h
@ -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);
|
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).
|
* 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.
|
* path from, and updates with each successive entry's name.
|
||||||
* @param fn function to invoke with each entry. The first arg is
|
* @param flags Combination of GIT_PATH_DIR flags.
|
||||||
* the input state and the second arg is pathbuf. The function
|
* @param callback Callback for each entry. Passed the `payload` and each
|
||||||
* may modify the pathbuf, but only by appending new text.
|
* successive path inside the directory as a full path. This may
|
||||||
* @param state to pass to fn as the first arg.
|
* 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
|
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
|
||||||
*/
|
*/
|
||||||
extern int git_path_direach(
|
extern int git_path_direach(
|
||||||
git_buf *pathbuf,
|
git_buf *pathbuf,
|
||||||
int (*fn)(void *, git_buf *),
|
uint32_t flags,
|
||||||
void *state);
|
int (*callback)(void *payload, git_buf *path),
|
||||||
|
void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort function to order two paths
|
* 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
|
* @param pathbuf Buffer the function reads the directory from and
|
||||||
* and updates with each successive name.
|
* and updates with each successive name.
|
||||||
* @param ceiling Prefix of path at which to stop walking up. If NULL,
|
* @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
|
* 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
|
* pathbuf, the callback will be invoked a single time on the
|
||||||
* original input path.
|
* original input path.
|
||||||
* @param fn Function to invoke on each path. The first arg is the
|
* @param callback Function to invoke on each path. Passed the `payload`
|
||||||
* input satte and the second arg is the pathbuf. The function
|
* and the buffer containing the current path. The path should not
|
||||||
* should not modify the pathbuf.
|
* be modified in any way.
|
||||||
* @param state Passed to fn as the first ath.
|
* @param state Passed to fn as the first ath.
|
||||||
*/
|
*/
|
||||||
extern int git_path_walk_up(
|
extern int git_path_walk_up(
|
||||||
git_buf *pathbuf,
|
git_buf *pathbuf,
|
||||||
const char *ceiling,
|
const char *ceiling,
|
||||||
int (*fn)(void *state, git_buf *),
|
int (*callback)(void *payload, git_buf *path),
|
||||||
void *state);
|
void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load all directory entries (except '.' and '..') into a vector.
|
* 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.
|
* 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
|
* @param alloc_extra Extra bytes to add to each string allocation in
|
||||||
* case you want to append anything funny.
|
* case you want to append anything funny.
|
||||||
|
* @param flags Combination of GIT_PATH_DIR flags.
|
||||||
* @param contents Vector to fill with directory entry names.
|
* @param contents Vector to fill with directory entry names.
|
||||||
*/
|
*/
|
||||||
extern int git_path_dirload(
|
extern int git_path_dirload(
|
||||||
const char *path,
|
const char *path,
|
||||||
size_t prefix_len,
|
size_t prefix_len,
|
||||||
size_t alloc_extra,
|
size_t alloc_extra,
|
||||||
|
uint32_t flags,
|
||||||
git_vector *contents);
|
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 path The directory to read from
|
||||||
* @param prefix_len The trailing part of path to prefix to entry paths
|
* @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 start_stat As optimization, only stat values after this prefix
|
||||||
* @param end_stat As optimization, only stat values before this prefix
|
* @param end_stat As optimization, only stat values before this prefix
|
||||||
* @param contents Vector to fill with git_path_with_stat structures
|
* @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(
|
extern int git_path_dirload_with_stat(
|
||||||
const char *path,
|
const char *path,
|
||||||
size_t prefix_len,
|
size_t prefix_len,
|
||||||
bool ignore_case,
|
uint32_t flags,
|
||||||
const char *start_stat,
|
const char *start_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 */
|
||||||
|
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
|
#endif
|
||||||
|
@ -56,6 +56,8 @@ typedef struct refdb_fs_backend {
|
|||||||
|
|
||||||
git_sortedcache *refcache;
|
git_sortedcache *refcache;
|
||||||
int peeling_mode;
|
int peeling_mode;
|
||||||
|
git_iterator_flag_t iterator_flags;
|
||||||
|
uint32_t direach_flags;
|
||||||
} refdb_fs_backend;
|
} refdb_fs_backend;
|
||||||
|
|
||||||
static int packref_cmp(const void *a_, const void *b_)
|
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;
|
return 0;
|
||||||
|
|
||||||
if (git_path_isdir(full_path->ptr))
|
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);
|
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
|
* This will overwrite any old packed entries with their
|
||||||
* updated loose versions
|
* 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);
|
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 ||
|
if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 ||
|
||||||
(error = git_iterator_for_filesystem(
|
(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);
|
git_buf_free(&path);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@ -1071,6 +1075,7 @@ int git_refdb_backend_fs(
|
|||||||
git_refdb_backend **backend_out,
|
git_refdb_backend **backend_out,
|
||||||
git_repository *repository)
|
git_repository *repository)
|
||||||
{
|
{
|
||||||
|
int t = 0;
|
||||||
git_buf path = GIT_BUF_INIT;
|
git_buf path = GIT_BUF_INIT;
|
||||||
refdb_fs_backend *backend;
|
refdb_fs_backend *backend;
|
||||||
|
|
||||||
@ -1092,6 +1097,15 @@ int git_refdb_backend_fs(
|
|||||||
|
|
||||||
git_buf_free(&path);
|
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.exists = &refdb_fs_backend__exists;
|
||||||
backend->parent.lookup = &refdb_fs_backend__lookup;
|
backend->parent.lookup = &refdb_fs_backend__lookup;
|
||||||
backend->parent.iterator = &refdb_fs_backend__iterator;
|
backend->parent.iterator = &refdb_fs_backend__iterator;
|
||||||
|
67
src/refs.c
67
src/refs.c
@ -138,6 +138,22 @@ int git_reference_name_to_id(
|
|||||||
return 0;
|
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(
|
int git_reference_lookup_resolved(
|
||||||
git_reference **ref_out,
|
git_reference **ref_out,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
@ -159,13 +175,13 @@ int git_reference_lookup_resolved(
|
|||||||
else if (max_nesting < 0)
|
else if (max_nesting < 0)
|
||||||
max_nesting = DEFAULT_NESTING_LEVEL;
|
max_nesting = DEFAULT_NESTING_LEVEL;
|
||||||
|
|
||||||
strncpy(scan_name, name, GIT_REFNAME_MAX);
|
|
||||||
scan_type = GIT_REF_SYMBOLIC;
|
scan_type = GIT_REF_SYMBOLIC;
|
||||||
|
|
||||||
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
|
if ((error = reference_normalize_for_repo(
|
||||||
return -1;
|
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;
|
return error;
|
||||||
|
|
||||||
for (nesting = max_nesting;
|
for (nesting = max_nesting;
|
||||||
@ -173,7 +189,7 @@ int git_reference_lookup_resolved(
|
|||||||
nesting--)
|
nesting--)
|
||||||
{
|
{
|
||||||
if (nesting != max_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);
|
git_reference_free(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,17 +727,18 @@ static bool is_all_caps_and_underscore(const char *name, size_t len)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
|
||||||
int git_reference__normalize_name(
|
int git_reference__normalize_name(
|
||||||
git_buf *buf,
|
git_buf *buf,
|
||||||
const char *name,
|
const char *name,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
// Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
|
|
||||||
|
|
||||||
char *current;
|
char *current;
|
||||||
int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
|
int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
|
||||||
unsigned int process_flags;
|
unsigned int process_flags;
|
||||||
bool normalize = (buf != NULL);
|
bool normalize = (buf != NULL);
|
||||||
|
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
|
||||||
|
|
||||||
assert(name);
|
assert(name);
|
||||||
|
|
||||||
process_flags = flags;
|
process_flags = flags;
|
||||||
@ -733,6 +750,13 @@ int git_reference__normalize_name(
|
|||||||
if (normalize)
|
if (normalize)
|
||||||
git_buf_clear(buf);
|
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, ¤t, &namelen)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
segment_len = ensure_segment_validity(current);
|
segment_len = ensure_segment_validity(current);
|
||||||
if (segment_len < 0) {
|
if (segment_len < 0) {
|
||||||
@ -809,6 +833,8 @@ cleanup:
|
|||||||
if (error && normalize)
|
if (error && normalize)
|
||||||
git_buf_free(buf);
|
git_buf_free(buf);
|
||||||
|
|
||||||
|
git_path_iconv_clear(&ic);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -983,9 +1009,9 @@ static int peel_error(int error, git_reference *ref, const char* msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int git_reference_peel(
|
int git_reference_peel(
|
||||||
git_object **peeled,
|
git_object **peeled,
|
||||||
git_reference *ref,
|
git_reference *ref,
|
||||||
git_otype target_type)
|
git_otype target_type)
|
||||||
{
|
{
|
||||||
git_reference *resolved = NULL;
|
git_reference *resolved = NULL;
|
||||||
git_object *target = NULL;
|
git_object *target = NULL;
|
||||||
@ -1027,24 +1053,19 @@ cleanup:
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_reference__is_valid_name(
|
int git_reference__is_valid_name(const char *refname, unsigned int flags)
|
||||||
const char *refname,
|
|
||||||
unsigned int flags)
|
|
||||||
{
|
{
|
||||||
int error;
|
if (git_reference__normalize_name(NULL, refname, flags) < 0) {
|
||||||
|
giterr_clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
error = git_reference__normalize_name(NULL, refname, flags) == 0;
|
return true;
|
||||||
giterr_clear();
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_reference_is_valid_name(
|
int git_reference_is_valid_name(const char *refname)
|
||||||
const char *refname)
|
|
||||||
{
|
{
|
||||||
return git_reference__is_valid_name(
|
return git_reference__is_valid_name(refname, GIT_REF_FORMAT_ALLOW_ONELEVEL);
|
||||||
refname,
|
|
||||||
GIT_REF_FORMAT_ALLOW_ONELEVEL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *git_reference_shorthand(git_reference *ref)
|
const char *git_reference_shorthand(git_reference *ref)
|
||||||
|
@ -46,6 +46,8 @@
|
|||||||
#define GIT_STASH_FILE "stash"
|
#define GIT_STASH_FILE "stash"
|
||||||
#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
|
#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
|
||||||
|
|
||||||
|
#define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16)
|
||||||
|
|
||||||
#define GIT_REFNAME_MAX 1024
|
#define GIT_REFNAME_MAX 1024
|
||||||
|
|
||||||
struct git_reference {
|
struct git_reference {
|
||||||
|
272
src/repository.c
272
src/repository.c
@ -843,10 +843,6 @@ fail:
|
|||||||
static bool is_chmod_supported(const char *file_path)
|
static bool is_chmod_supported(const char *file_path)
|
||||||
{
|
{
|
||||||
struct stat st1, st2;
|
struct stat st1, st2;
|
||||||
static int _is_supported = -1;
|
|
||||||
|
|
||||||
if (_is_supported > -1)
|
|
||||||
return _is_supported;
|
|
||||||
|
|
||||||
if (p_stat(file_path, &st1) < 0)
|
if (p_stat(file_path, &st1) < 0)
|
||||||
return false;
|
return false;
|
||||||
@ -857,27 +853,19 @@ static bool is_chmod_supported(const char *file_path)
|
|||||||
if (p_stat(file_path, &st2) < 0)
|
if (p_stat(file_path, &st2) < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_is_supported = (st1.st_mode != st2.st_mode);
|
return (st1.st_mode != st2.st_mode);
|
||||||
|
|
||||||
return _is_supported;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_filesystem_case_insensitive(const char *gitdir_path)
|
static bool is_filesystem_case_insensitive(const char *gitdir_path)
|
||||||
{
|
{
|
||||||
git_buf path = GIT_BUF_INIT;
|
git_buf path = GIT_BUF_INIT;
|
||||||
static int _is_insensitive = -1;
|
int is_insensitive = -1;
|
||||||
|
|
||||||
if (_is_insensitive > -1)
|
if (!git_buf_joinpath(&path, gitdir_path, "CoNfIg"))
|
||||||
return _is_insensitive;
|
is_insensitive = git_path_exists(git_buf_cstr(&path));
|
||||||
|
|
||||||
if (git_buf_joinpath(&path, gitdir_path, "CoNfIg") < 0)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
_is_insensitive = git_path_exists(git_buf_cstr(&path));
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
git_buf_free(&path);
|
git_buf_free(&path);
|
||||||
return _is_insensitive;
|
return is_insensitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool are_symlinks_supported(const char *wd_path)
|
static bool are_symlinks_supported(const char *wd_path)
|
||||||
@ -885,26 +873,77 @@ static bool are_symlinks_supported(const char *wd_path)
|
|||||||
git_buf path = GIT_BUF_INIT;
|
git_buf path = GIT_BUF_INIT;
|
||||||
int fd;
|
int fd;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
static int _symlinks_supported = -1;
|
int symlinks_supported = -1;
|
||||||
|
|
||||||
if (_symlinks_supported > -1)
|
|
||||||
return _symlinks_supported;
|
|
||||||
|
|
||||||
if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
|
if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
|
||||||
p_close(fd) < 0 ||
|
p_close(fd) < 0 ||
|
||||||
p_unlink(path.ptr) < 0 ||
|
p_unlink(path.ptr) < 0 ||
|
||||||
p_symlink("testing", path.ptr) < 0 ||
|
p_symlink("testing", path.ptr) < 0 ||
|
||||||
p_lstat(path.ptr, &st) < 0)
|
p_lstat(path.ptr, &st) < 0)
|
||||||
_symlinks_supported = false;
|
symlinks_supported = false;
|
||||||
else
|
else
|
||||||
_symlinks_supported = (S_ISLNK(st.st_mode) != 0);
|
symlinks_supported = (S_ISLNK(st.st_mode) != 0);
|
||||||
|
|
||||||
(void)p_unlink(path.ptr);
|
(void)p_unlink(path.ptr);
|
||||||
git_buf_free(&path);
|
git_buf_free(&path);
|
||||||
|
|
||||||
return _symlinks_supported;
|
return symlinks_supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef GIT_USE_ICONV
|
||||||
|
|
||||||
|
static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
|
||||||
|
static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
|
||||||
|
|
||||||
|
/* Check if the platform is decomposing unicode data for us. We will
|
||||||
|
* emulate core Git and prefer to use precomposed unicode data internally
|
||||||
|
* on these platforms, composing the decomposed unicode on the fly.
|
||||||
|
*
|
||||||
|
* This mainly happens on the Mac where HDFS stores filenames as
|
||||||
|
* decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
|
||||||
|
* return decomposed unicode from readdir() even when the actual
|
||||||
|
* filesystem is storing precomposed unicode.
|
||||||
|
*/
|
||||||
|
static bool does_fs_decompose_unicode_paths(const char *wd_path)
|
||||||
|
{
|
||||||
|
git_buf path = GIT_BUF_INIT;
|
||||||
|
int fd;
|
||||||
|
bool found_decomposed = false;
|
||||||
|
char tmp[6];
|
||||||
|
|
||||||
|
/* Create a file using a precomposed path and then try to find it
|
||||||
|
* using the decomposed name. If the lookup fails, then we will mark
|
||||||
|
* that we should precompose unicode for this repository.
|
||||||
|
*/
|
||||||
|
if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 ||
|
||||||
|
(fd = p_mkstemp(path.ptr)) < 0)
|
||||||
|
goto done;
|
||||||
|
p_close(fd);
|
||||||
|
|
||||||
|
/* record trailing digits generated by mkstemp */
|
||||||
|
memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp));
|
||||||
|
|
||||||
|
/* try to look up as NFD path */
|
||||||
|
if (git_buf_joinpath(&path, wd_path, nfd_file) < 0)
|
||||||
|
goto done;
|
||||||
|
memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
|
||||||
|
|
||||||
|
found_decomposed = git_path_exists(path.ptr);
|
||||||
|
|
||||||
|
/* remove temporary file (using original precomposed path) */
|
||||||
|
if (git_buf_joinpath(&path, wd_path, nfc_file) < 0)
|
||||||
|
goto done;
|
||||||
|
memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
|
||||||
|
|
||||||
|
(void)p_unlink(path.ptr);
|
||||||
|
|
||||||
|
done:
|
||||||
|
git_buf_free(&path);
|
||||||
|
return found_decomposed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static int create_empty_file(const char *path, mode_t mode)
|
static int create_empty_file(const char *path, mode_t mode)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
@ -922,71 +961,131 @@ static int create_empty_file(const char *path, mode_t mode)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int repo_local_config(
|
||||||
|
git_config **out,
|
||||||
|
git_buf *config_dir,
|
||||||
|
git_repository *repo,
|
||||||
|
const char *repo_dir)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
git_config *parent;
|
||||||
|
const char *cfg_path;
|
||||||
|
|
||||||
|
if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
|
||||||
|
return -1;
|
||||||
|
cfg_path = git_buf_cstr(config_dir);
|
||||||
|
|
||||||
|
/* make LOCAL config if missing */
|
||||||
|
if (!git_path_isfile(cfg_path) &&
|
||||||
|
(error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
/* if no repo, just open that file directly */
|
||||||
|
if (!repo)
|
||||||
|
return git_config_open_ondisk(out, cfg_path);
|
||||||
|
|
||||||
|
/* otherwise, open parent config and get that level */
|
||||||
|
if ((error = git_repository_config__weakptr(&parent, repo)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) {
|
||||||
|
giterr_clear();
|
||||||
|
|
||||||
|
if (!(error = git_config_add_file_ondisk(
|
||||||
|
parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
|
||||||
|
error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
git_config_free(parent);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int repo_init_fs_configs(
|
||||||
|
git_config *cfg,
|
||||||
|
const char *cfg_path,
|
||||||
|
const char *repo_dir,
|
||||||
|
const char *work_dir,
|
||||||
|
bool update_ignorecase)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
if (!work_dir)
|
||||||
|
work_dir = repo_dir;
|
||||||
|
|
||||||
|
if ((error = git_config_set_bool(
|
||||||
|
cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (!are_symlinks_supported(work_dir)) {
|
||||||
|
if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
|
||||||
|
return error;
|
||||||
|
} else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
|
||||||
|
giterr_clear();
|
||||||
|
|
||||||
|
if (update_ignorecase) {
|
||||||
|
if (is_filesystem_case_insensitive(repo_dir)) {
|
||||||
|
if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0)
|
||||||
|
return error;
|
||||||
|
} else if (git_config_delete_entry(cfg, "core.ignorecase") < 0)
|
||||||
|
giterr_clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef GIT_USE_ICONV
|
||||||
|
if ((error = git_config_set_bool(
|
||||||
|
cfg, "core.precomposeunicode",
|
||||||
|
does_fs_decompose_unicode_paths(work_dir))) < 0)
|
||||||
|
return error;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int repo_init_config(
|
static int repo_init_config(
|
||||||
const char *repo_dir,
|
const char *repo_dir,
|
||||||
const char *work_dir,
|
const char *work_dir,
|
||||||
git_repository_init_options *opts)
|
uint32_t flags,
|
||||||
|
uint32_t mode)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
git_buf cfg_path = GIT_BUF_INIT;
|
git_buf cfg_path = GIT_BUF_INIT;
|
||||||
git_config *config = NULL;
|
git_config *config = NULL;
|
||||||
|
bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
|
||||||
|
bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
|
||||||
|
|
||||||
#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
|
if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (is_reinit && (error = check_repositoryformatversion(config)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
#define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \
|
||||||
if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
|
if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
|
||||||
goto cleanup; } while (0)
|
goto cleanup; } while (0)
|
||||||
|
|
||||||
if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
|
SET_REPO_CONFIG(bool, "core.bare", is_bare);
|
||||||
return -1;
|
SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
|
||||||
|
|
||||||
if (!git_path_isfile(git_buf_cstr(&cfg_path)) &&
|
if ((error = repo_init_fs_configs(
|
||||||
create_empty_file(git_buf_cstr(&cfg_path), GIT_CONFIG_FILE_MODE) < 0) {
|
config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
|
||||||
git_buf_free(&cfg_path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
|
|
||||||
git_buf_free(&cfg_path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
|
|
||||||
(error = check_repositoryformatversion(config)) < 0)
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
SET_REPO_CONFIG(
|
if (!is_bare) {
|
||||||
bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
|
|
||||||
SET_REPO_CONFIG(
|
|
||||||
int32, "core.repositoryformatversion", GIT_REPO_VERSION);
|
|
||||||
SET_REPO_CONFIG(
|
|
||||||
bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
|
|
||||||
|
|
||||||
if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
|
|
||||||
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
|
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
|
||||||
|
|
||||||
if (!are_symlinks_supported(work_dir))
|
if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD))
|
||||||
SET_REPO_CONFIG(bool, "core.symlinks", false);
|
|
||||||
|
|
||||||
if (!(opts->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 ((opts->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();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (!are_symlinks_supported(repo_dir))
|
|
||||||
SET_REPO_CONFIG(bool, "core.symlinks", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
|
if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
|
||||||
is_filesystem_case_insensitive(repo_dir))
|
|
||||||
SET_REPO_CONFIG(bool, "core.ignorecase", true);
|
|
||||||
|
|
||||||
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
|
|
||||||
SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
|
SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
|
||||||
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
|
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
|
||||||
}
|
}
|
||||||
else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
|
else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
|
||||||
SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
|
SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
|
||||||
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
|
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
|
||||||
}
|
}
|
||||||
@ -998,6 +1097,41 @@ cleanup:
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int repo_reinit_submodule_fs(git_submodule *sm, const char *n, void *p)
|
||||||
|
{
|
||||||
|
git_repository *smrepo = NULL;
|
||||||
|
GIT_UNUSED(n); GIT_UNUSED(p);
|
||||||
|
|
||||||
|
if (git_submodule_open(&smrepo, sm) < 0 ||
|
||||||
|
git_repository_reinit_filesystem(smrepo, true) < 0)
|
||||||
|
giterr_clear();
|
||||||
|
git_repository_free(smrepo);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_repository_reinit_filesystem(git_repository *repo, int recurse)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
git_buf path = GIT_BUF_INIT;
|
||||||
|
git_config *config = NULL;
|
||||||
|
const char *repo_dir = git_repository_path(repo);
|
||||||
|
|
||||||
|
if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
|
||||||
|
error = repo_init_fs_configs(
|
||||||
|
config, path.ptr, repo_dir, git_repository_workdir(repo), true);
|
||||||
|
|
||||||
|
git_config_free(config);
|
||||||
|
git_buf_free(&path);
|
||||||
|
|
||||||
|
git_repository__cvar_cache_clear(repo);
|
||||||
|
|
||||||
|
if (!repo->is_bare && recurse)
|
||||||
|
(void)git_submodule_foreach(repo, repo_reinit_submodule_fs, NULL);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
static int repo_write_template(
|
static int repo_write_template(
|
||||||
const char *git_dir,
|
const char *git_dir,
|
||||||
bool allow_overwrite,
|
bool allow_overwrite,
|
||||||
@ -1386,22 +1520,22 @@ 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(
|
||||||
git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
|
repo_path.ptr, wd_path.ptr, opts->flags, opts->mode);
|
||||||
|
|
||||||
/* TODO: reinitialize the templates */
|
/* TODO: reinitialize the templates */
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!(error = repo_init_structure(
|
if (!(error = repo_init_structure(
|
||||||
git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
|
repo_path.ptr, wd_path.ptr, opts)) &&
|
||||||
!(error = repo_init_config(
|
!(error = repo_init_config(
|
||||||
git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
|
repo_path.ptr, wd_path.ptr, opts->flags, opts->mode)))
|
||||||
error = repo_init_create_head(
|
error = repo_init_create_head(
|
||||||
git_buf_cstr(&repo_path), opts->initial_head);
|
repo_path.ptr, opts->initial_head);
|
||||||
}
|
}
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
error = git_repository_open(out, git_buf_cstr(&repo_path));
|
error = git_repository_open(out, repo_path.ptr);
|
||||||
|
|
||||||
if (!error && opts->origin_url)
|
if (!error && opts->origin_url)
|
||||||
error = repo_init_create_origin(*out, opts->origin_url);
|
error = repo_init_create_origin(*out, opts->origin_url);
|
||||||
|
@ -37,6 +37,7 @@ typedef enum {
|
|||||||
GIT_CVAR_IGNORESTAT, /* core.ignorestat */
|
GIT_CVAR_IGNORESTAT, /* core.ignorestat */
|
||||||
GIT_CVAR_TRUSTCTIME, /* core.trustctime */
|
GIT_CVAR_TRUSTCTIME, /* core.trustctime */
|
||||||
GIT_CVAR_ABBREV, /* core.abbrev */
|
GIT_CVAR_ABBREV, /* core.abbrev */
|
||||||
|
GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
|
||||||
GIT_CVAR_CACHE_MAX
|
GIT_CVAR_CACHE_MAX
|
||||||
} git_cvar_cached;
|
} git_cvar_cached;
|
||||||
|
|
||||||
@ -86,6 +87,8 @@ typedef enum {
|
|||||||
GIT_TRUSTCTIME_DEFAULT = GIT_CVAR_TRUE,
|
GIT_TRUSTCTIME_DEFAULT = GIT_CVAR_TRUE,
|
||||||
/* core.abbrev */
|
/* core.abbrev */
|
||||||
GIT_ABBREV_DEFAULT = 7,
|
GIT_ABBREV_DEFAULT = 7,
|
||||||
|
/* core.precomposeunicode */
|
||||||
|
GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
|
||||||
|
|
||||||
} git_cvar_value;
|
} git_cvar_value;
|
||||||
|
|
||||||
|
@ -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(
|
|||||||
|
|
||||||
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 */
|
||||||
|
@ -224,13 +224,15 @@ void test_checkout_index__options_disable_filters(void)
|
|||||||
|
|
||||||
void test_checkout_index__options_dir_modes(void)
|
void test_checkout_index__options_dir_modes(void)
|
||||||
{
|
{
|
||||||
#ifndef GIT_WIN32
|
|
||||||
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
|
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
git_oid oid;
|
git_oid oid;
|
||||||
git_commit *commit;
|
git_commit *commit;
|
||||||
mode_t um;
|
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_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
|
||||||
cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
|
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");
|
cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE, "%07o");
|
||||||
|
|
||||||
git_commit_free(commit);
|
git_commit_free(commit);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_checkout_index__options_override_file_modes(void)
|
void test_checkout_index__options_override_file_modes(void)
|
||||||
{
|
{
|
||||||
#ifndef GIT_WIN32
|
|
||||||
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
|
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
|
if (!cl_is_chmod_supported())
|
||||||
|
return;
|
||||||
|
|
||||||
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
|
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
|
||||||
opts.file_mode = 0700;
|
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_git_pass(p_stat("./testrepo/new.txt", &st));
|
||||||
cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
|
cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_checkout_index__options_open_flags(void)
|
void test_checkout_index__options_open_flags(void)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "clar_libgit2.h"
|
#include "clar_libgit2.h"
|
||||||
#include "posix.h"
|
#include "posix.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
|
#include "git2/sys/repository.h"
|
||||||
|
|
||||||
void cl_git_report_failure(
|
void cl_git_report_failure(
|
||||||
int error, const char *file, int line, const char *fncall)
|
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 */
|
/* Now open the sandbox repository and make it available for tests */
|
||||||
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 */
|
||||||
|
cl_git_pass(git_repository_reinit_filesystem(_cl_repo, 0));
|
||||||
|
|
||||||
return _cl_repo;
|
return _cl_repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +305,7 @@ static int remove_placeholders_recurs(void *_data, git_buf *path)
|
|||||||
size_t pathlen;
|
size_t pathlen;
|
||||||
|
|
||||||
if (git_path_isdir(path->ptr) == true)
|
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;
|
pathlen = path->size;
|
||||||
|
|
||||||
|
@ -56,41 +56,18 @@ void test_clone_nonetwork__bad_url(void)
|
|||||||
cl_assert(!git_path_exists("./foo"));
|
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)
|
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
|
/* Clone should not remove the directory if it already exists, but
|
||||||
* Should clean up entries it creates. */
|
* Should clean up entries it creates. */
|
||||||
p_mkdir("./foo", GIT_DIR_MODE);
|
p_mkdir("./foo", GIT_DIR_MODE);
|
||||||
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
|
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
|
||||||
cl_assert(git_path_exists("./foo"));
|
cl_assert(git_path_is_empty_dir("./foo"));
|
||||||
|
|
||||||
/* Make sure the directory is empty. */
|
|
||||||
cl_git_pass(git_path_direach(&path_buf,
|
|
||||||
dont_call_me,
|
|
||||||
NULL));
|
|
||||||
|
|
||||||
/* Try again with a bare repository. */
|
/* Try again with a bare repository. */
|
||||||
g_options.bare = true;
|
g_options.bare = true;
|
||||||
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
|
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
|
||||||
cl_assert(git_path_exists("./foo"));
|
cl_assert(git_path_is_empty_dir("./foo"));
|
||||||
|
|
||||||
/* Make sure the directory is empty. */
|
|
||||||
cl_git_pass(git_path_direach(&path_buf,
|
|
||||||
dont_call_me,
|
|
||||||
NULL));
|
|
||||||
|
|
||||||
git_buf_free(&path_buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_clone_nonetwork__local(void)
|
void test_clone_nonetwork__local(void)
|
||||||
|
@ -88,14 +88,6 @@ static int one_entry(void *state, git_buf *path)
|
|||||||
return GIT_ERROR;
|
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[] = {
|
static name_data dot_names[] = {
|
||||||
{ 0, "./a" },
|
{ 0, "./a" },
|
||||||
@ -115,9 +107,7 @@ void test_core_dirent__dont_traverse_dot(void)
|
|||||||
cl_set_cleanup(&dirent_cleanup__cb, &dot);
|
cl_set_cleanup(&dirent_cleanup__cb, &dot);
|
||||||
setup(&dot);
|
setup(&dot);
|
||||||
|
|
||||||
cl_git_pass(git_path_direach(&dot.path,
|
cl_git_pass(git_path_direach(&dot.path, 0, one_entry, &dot));
|
||||||
one_entry,
|
|
||||||
&dot));
|
|
||||||
|
|
||||||
check_counts(&dot);
|
check_counts(&dot);
|
||||||
}
|
}
|
||||||
@ -141,9 +131,7 @@ void test_core_dirent__traverse_subfolder(void)
|
|||||||
cl_set_cleanup(&dirent_cleanup__cb, &sub);
|
cl_set_cleanup(&dirent_cleanup__cb, &sub);
|
||||||
setup(&sub);
|
setup(&sub);
|
||||||
|
|
||||||
cl_git_pass(git_path_direach(&sub.path,
|
cl_git_pass(git_path_direach(&sub.path, 0, one_entry, &sub));
|
||||||
one_entry,
|
|
||||||
&sub));
|
|
||||||
|
|
||||||
check_counts(&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);
|
cl_set_cleanup(&dirent_cleanup__cb, &sub_slash);
|
||||||
setup(&sub_slash);
|
setup(&sub_slash);
|
||||||
|
|
||||||
cl_git_pass(git_path_direach(&sub_slash.path,
|
cl_git_pass(git_path_direach(&sub_slash.path, 0, one_entry, &sub_slash));
|
||||||
one_entry,
|
|
||||||
&sub_slash));
|
|
||||||
|
|
||||||
check_counts(&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);
|
cl_set_cleanup(&dirent_cleanup__cb, &empty);
|
||||||
setup(&empty);
|
setup(&empty);
|
||||||
|
|
||||||
cl_git_pass(git_path_direach(&empty.path,
|
cl_git_pass(git_path_direach(&empty.path, 0, one_entry, &empty));
|
||||||
one_entry,
|
|
||||||
&empty));
|
|
||||||
|
|
||||||
check_counts(&empty);
|
check_counts(&empty);
|
||||||
|
|
||||||
/* make sure callback not called */
|
/* make sure callback not called */
|
||||||
cl_git_pass(git_path_direach(&empty.path,
|
cl_assert(git_path_is_empty_dir(empty.path.ptr));
|
||||||
dont_call_me,
|
|
||||||
&empty));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static name_data odd_names[] = {
|
static name_data odd_names[] = {
|
||||||
@ -216,9 +198,7 @@ void test_core_dirent__traverse_weird_filenames(void)
|
|||||||
cl_set_cleanup(&dirent_cleanup__cb, &odd);
|
cl_set_cleanup(&dirent_cleanup__cb, &odd);
|
||||||
setup(&odd);
|
setup(&odd);
|
||||||
|
|
||||||
cl_git_pass(git_path_direach(&odd.path,
|
cl_git_pass(git_path_direach(&odd.path, 0, one_entry, &odd));
|
||||||
one_entry,
|
|
||||||
&odd));
|
|
||||||
|
|
||||||
check_counts(&odd);
|
check_counts(&odd);
|
||||||
}
|
}
|
||||||
@ -231,5 +211,26 @@ void test_core_dirent__length_limits(void)
|
|||||||
big_filename[FILENAME_MAX] = 0;
|
big_filename[FILENAME_MAX] = 0;
|
||||||
|
|
||||||
cl_must_fail(p_creat(big_filename, 0666));
|
cl_must_fail(p_creat(big_filename, 0666));
|
||||||
|
|
||||||
git__free(big_filename);
|
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"));
|
||||||
|
}
|
||||||
|
60
tests-clar/core/iconv.c
Normal file
60
tests-clar/core/iconv.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#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);
|
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
|
/* FAT filesystems don't support exec bit, nor group/world bits */
|
||||||
/* chmod on Win32 doesn't support exec bit, not group/world bits */
|
if (!cl_is_chmod_supported()) {
|
||||||
cl_assert_equal_i_fmt((expected & 0600), (actual & 0777), "%07o");
|
expected &= 0600;
|
||||||
#else
|
actual &= 0600;
|
||||||
cl_assert_equal_i_fmt(expected, (actual & 0777), "%07o");
|
}
|
||||||
#endif
|
|
||||||
|
clar__assert_equal(
|
||||||
|
file, line, "expected_mode != actual_mode", 1,
|
||||||
|
"%07o", (int)expected, (int)(actual & 0777));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_core_mkdir__chmods(void)
|
void test_core_mkdir__chmods(void)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "clar_libgit2.h"
|
#include "clar_libgit2.h"
|
||||||
#include <fileops.h>
|
#include "fileops.h"
|
||||||
|
|
||||||
static void
|
static void
|
||||||
check_dirname(const char *A, const char *B)
|
check_dirname(const char *A, const char *B)
|
||||||
|
@ -21,6 +21,35 @@ git_tree *resolve_commit_oid_to_tree(
|
|||||||
return 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(
|
int diff_file_cb(
|
||||||
const git_diff_delta *delta,
|
const git_diff_delta *delta,
|
||||||
float progress,
|
float progress,
|
||||||
@ -29,9 +58,7 @@ int diff_file_cb(
|
|||||||
diff_expects *e = payload;
|
diff_expects *e = payload;
|
||||||
|
|
||||||
if (e->debug)
|
if (e->debug)
|
||||||
fprintf(stderr, "%c %s (%.3f)\n",
|
fprintf_delta(stderr, delta, progress);
|
||||||
git_diff_status_char(delta->status),
|
|
||||||
delta->old_file.path, progress);
|
|
||||||
|
|
||||||
if (e->names)
|
if (e->names)
|
||||||
cl_assert_equal_s(e->names[e->files], delta->old_file.path);
|
cl_assert_equal_s(e->names[e->files], delta->old_file.path);
|
||||||
@ -55,8 +82,14 @@ int diff_print_file_cb(
|
|||||||
float progress,
|
float progress,
|
||||||
void *payload)
|
void *payload)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%c %s\n",
|
if (!payload) {
|
||||||
git_diff_status_char(delta->status), delta->old_file.path);
|
fprintf_delta(stderr, delta, progress);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!((diff_expects *)payload)->debug)
|
||||||
|
fprintf_delta(stderr, delta, progress);
|
||||||
|
|
||||||
return diff_file_cb(delta, progress, payload);
|
return diff_file_cb(delta, progress, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,25 +27,25 @@ void test_diff_diffiter__create(void)
|
|||||||
git_diff_list_free(diff);
|
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_repository *repo = cl_git_sandbox_init("attr");
|
||||||
git_diff_list *diff;
|
git_diff_list *diff;
|
||||||
size_t d, num_d;
|
size_t d, num_d;
|
||||||
int count = 0;
|
diff_expects exp = { 0 };
|
||||||
|
|
||||||
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
|
cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
|
||||||
|
|
||||||
num_d = git_diff_num_deltas(diff);
|
num_d = git_diff_num_deltas(diff);
|
||||||
cl_assert_equal_i(6, (int)num_d);
|
|
||||||
|
|
||||||
for (d = 0; d < num_d; ++d) {
|
for (d = 0; d < num_d; ++d) {
|
||||||
const git_diff_delta *delta;
|
const git_diff_delta *delta;
|
||||||
cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
|
cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
|
||||||
cl_assert(delta != NULL);
|
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);
|
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_get_patch(&patch, NULL, diff, 0));
|
||||||
cl_git_pass(git_diff_patch_to_str(&actual, patch));
|
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);
|
cl_assert_equal_s(expected, actual);
|
||||||
|
|
||||||
free(actual);
|
free(actual);
|
||||||
|
@ -238,6 +238,9 @@ void test_diff_patch__hunks_have_correct_line_numbers(void)
|
|||||||
|
|
||||||
cl_git_pass(git_config_new(&cfg));
|
cl_git_pass(git_config_new(&cfg));
|
||||||
git_repository_set_config(g_repo, cfg);
|
git_repository_set_config(g_repo, cfg);
|
||||||
|
git_config_free(cfg);
|
||||||
|
|
||||||
|
git_repository_reinit_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"));
|
||||||
@ -408,7 +411,6 @@ void test_diff_patch__hunks_have_correct_line_numbers(void)
|
|||||||
git_buf_free(&actual);
|
git_buf_free(&actual);
|
||||||
git_buf_free(&old_content);
|
git_buf_free(&old_content);
|
||||||
git_tree_free(head);
|
git_tree_free(head);
|
||||||
git_config_free(cfg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_single_patch_stats(
|
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));
|
cl_git_pass(git_config_new(&cfg));
|
||||||
git_repository_set_config(g_repo, 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"));
|
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);
|
g_repo, 1, 1, 1, 6, expected_sizes, expected);
|
||||||
|
|
||||||
git_buf_free(&content);
|
git_buf_free(&content);
|
||||||
git_config_free(cfg);
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|||||||
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);
|
||||||
|
@ -51,24 +51,29 @@ static void replace_file_with_mode(
|
|||||||
git_buf_free(&content);
|
git_buf_free(&content);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_and_check_mode(
|
#define add_and_check_mode(I,F,X) add_and_check_mode_(I,F,X,__FILE__,__LINE__)
|
||||||
git_index *index, const char *filename, unsigned int expect_mode)
|
|
||||||
|
static void add_and_check_mode_(
|
||||||
|
git_index *index, const char *filename, unsigned int expect_mode,
|
||||||
|
const char *file, int line)
|
||||||
{
|
{
|
||||||
size_t pos;
|
size_t pos;
|
||||||
const git_index_entry *entry;
|
const git_index_entry *entry;
|
||||||
|
|
||||||
cl_git_pass(git_index_add_bypath(index, filename));
|
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);
|
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)
|
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);
|
||||||
|
|
||||||
@ -96,15 +101,10 @@ void test_index_filemodes__untrusted(void)
|
|||||||
O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
|
add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
|
||||||
|
|
||||||
/* this test won't give predictable results on a platform
|
/* 6 - add new 0755 -> expect 0644 if core.filemode == false */
|
||||||
* that doesn't support filemodes correctly, so skip it.
|
cl_git_write2file("filemodes/new_on", "blah", 0,
|
||||||
*/
|
O_WRONLY | O_CREAT | O_TRUNC, 0755);
|
||||||
if (can_filemode) {
|
add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB);
|
||||||
/* 6 - add 0755 -> expect 0755 */
|
|
||||||
cl_git_write2file("filemodes/new_on", "blah", 0,
|
|
||||||
O_WRONLY | O_CREAT | O_TRUNC, 0755);
|
|
||||||
add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
git_index_free(index);
|
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 wd = GIT_BUF_INIT;
|
||||||
|
|
||||||
git_buf_puts(&wd, repo->workdir);
|
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)
|
if (actual_len != expected_len)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -4,17 +4,13 @@ static git_repository *repo;
|
|||||||
|
|
||||||
void test_refs_unicode__initialize(void)
|
void test_refs_unicode__initialize(void)
|
||||||
{
|
{
|
||||||
cl_fixture_sandbox("testrepo.git");
|
repo = cl_git_sandbox_init("testrepo.git");
|
||||||
|
|
||||||
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_refs_unicode__cleanup(void)
|
void test_refs_unicode__cleanup(void)
|
||||||
{
|
{
|
||||||
git_repository_free(repo);
|
cl_git_sandbox_cleanup();
|
||||||
repo = NULL;
|
repo = NULL;
|
||||||
|
|
||||||
cl_fixture_cleanup("testrepo.git");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_refs_unicode__create_and_lookup(void)
|
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_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 *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(
|
||||||
cl_assert(git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)) == 0);
|
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(ref0);
|
|
||||||
git_reference_free(ref1);
|
|
||||||
git_reference_free(ref2);
|
git_reference_free(ref2);
|
||||||
|
|
||||||
|
#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(ref1);
|
||||||
git_repository_free(repo2);
|
git_repository_free(repo2);
|
||||||
}
|
}
|
||||||
|
@ -179,41 +179,32 @@ void test_repo_init__additional_templates(void)
|
|||||||
git_buf_free(&path);
|
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;
|
git_config *config;
|
||||||
int current_value;
|
int error, current_value;
|
||||||
git_buf repo_path = GIT_BUF_INIT;
|
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_set_cleanup(&cleanup_repository, "config_entry");
|
||||||
|
|
||||||
cl_git_pass(git_buf_puts(&repo_path, "config_entry/test."));
|
cl_git_pass(git_repository_init(&_repo, repo_path, is_bare));
|
||||||
|
|
||||||
if (!is_bare)
|
cl_git_pass(git_repository_config(&config, _repo));
|
||||||
cl_git_pass(git_buf_puts(&repo_path, "non."));
|
error = git_config_get_bool(¤t_value, config, config_key);
|
||||||
|
git_config_free(config);
|
||||||
cl_git_pass(git_buf_puts(&repo_path, "bare.git"));
|
|
||||||
|
|
||||||
cl_git_pass(git_repository_init(&_repo, git_buf_cstr(&repo_path), is_bare));
|
|
||||||
|
|
||||||
git_buf_free(&repo_path);
|
|
||||||
|
|
||||||
git_repository_config(&config, _repo);
|
|
||||||
|
|
||||||
if (expected_value >= 0) {
|
if (expected_value >= 0) {
|
||||||
cl_git_pass(git_config_get_bool(¤t_value, config, config_key));
|
cl_assert_equal_i(0, error);
|
||||||
|
|
||||||
cl_assert_equal_i(expected_value, current_value);
|
cl_assert_equal_i(expected_value, current_value);
|
||||||
} else {
|
} else {
|
||||||
int error = git_config_get_bool(¤t_value, config, config_key);
|
|
||||||
|
|
||||||
cl_assert_equal_i(expected_value, error);
|
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);
|
assert_config_entry_on_init_bytype(config_key, expected_value, true);
|
||||||
git_repository_free(_repo);
|
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)
|
void test_repo_init__detect_filemode(void)
|
||||||
{
|
{
|
||||||
#ifdef GIT_WIN32
|
assert_config_entry_on_init("core.filemode", cl_is_chmod_supported());
|
||||||
assert_config_entry_on_init("core.filemode", false);
|
|
||||||
#else
|
|
||||||
assert_config_entry_on_init("core.filemode", true);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CASE_INSENSITIVE_FILESYSTEM (defined GIT_WIN32 || defined __APPLE__)
|
|
||||||
|
|
||||||
void test_repo_init__detect_ignorecase(void)
|
void test_repo_init__detect_ignorecase(void)
|
||||||
{
|
{
|
||||||
#if CASE_INSENSITIVE_FILESYSTEM
|
struct stat st;
|
||||||
assert_config_entry_on_init("core.ignorecase", true);
|
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
|
#else
|
||||||
assert_config_entry_on_init("core.ignorecase", GIT_ENOTFOUND);
|
assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,13 +276,7 @@ void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
|
|||||||
|
|
||||||
void test_repo_init__reinit_overwrites_filemode(void)
|
void test_repo_init__reinit_overwrites_filemode(void)
|
||||||
{
|
{
|
||||||
int expected, current_value;
|
int expected = cl_is_chmod_supported(), current_value;
|
||||||
|
|
||||||
#ifdef GIT_WIN32
|
|
||||||
expected = false;
|
|
||||||
#else
|
|
||||||
expected = true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Init a new repo */
|
/* Init a new repo */
|
||||||
cl_set_cleanup(&cleanup_repository, "overwrite.git");
|
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_git_pass(git_path_lstat(git_repository_path(_repo), &st));
|
||||||
cl_assert(S_ISDIR(st.st_mode));
|
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_git_pass(git_reference_lookup(&ref, _repo, "HEAD"));
|
||||||
cl_assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
|
cl_assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
|
||||||
|
@ -69,14 +69,14 @@ static void test_status(
|
|||||||
actual = git_status_byindex(status_list, i);
|
actual = git_status_byindex(status_list, i);
|
||||||
expected = &expected_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 :
|
oldname = actual->head_to_index ? actual->head_to_index->old_file.path :
|
||||||
actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL;
|
actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL;
|
||||||
|
|
||||||
newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path :
|
newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path :
|
||||||
actual->head_to_index ? actual->head_to_index->new_file.path : NULL;
|
actual->head_to_index ? actual->head_to_index->new_file.path : NULL;
|
||||||
|
|
||||||
|
cl_assert_equal_i_fmt(expected->status, actual->status, "%04x");
|
||||||
|
|
||||||
if (oldname)
|
if (oldname)
|
||||||
cl_assert(git__strcmp(oldname, expected->oldname) == 0);
|
cl_assert(git__strcmp(oldname, expected->oldname) == 0);
|
||||||
else
|
else
|
||||||
@ -507,14 +507,14 @@ void test_status_renames__both_casechange_two(void)
|
|||||||
"untimely.txt", "untimeliest.txt" }
|
"untimely.txt", "untimeliest.txt" }
|
||||||
};
|
};
|
||||||
struct status_entry expected_case[] = {
|
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_INDEX_RENAMED |
|
||||||
GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED,
|
GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED,
|
||||||
"songof7cities.txt", "SONGOF7.txt" },
|
"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,
|
{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
|
||||||
"untimely.txt", "untimeliest.txt" }
|
"untimely.txt", "untimeliest.txt" }
|
||||||
};
|
};
|
||||||
|
@ -119,7 +119,7 @@ void test_status_worktree__purged_worktree(void)
|
|||||||
|
|
||||||
/* first purge the contents of the worktree */
|
/* first purge the contents of the worktree */
|
||||||
cl_git_pass(git_buf_sets(&workdir, git_repository_workdir(repo)));
|
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);
|
git_buf_free(&workdir);
|
||||||
|
|
||||||
/* now get status */
|
/* now get status */
|
||||||
|
@ -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;
|
|||||||
|
|
||||||
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)
|
||||||
|
@ -388,7 +388,8 @@ void test_submodule_status__iterator(void)
|
|||||||
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||||
GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
|
GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
|
||||||
GIT_STATUS_OPT_INCLUDE_IGNORED |
|
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(
|
cl_git_pass(git_status_foreach_ext(
|
||||||
g_repo, &opts, confirm_submodule_status, &exp));
|
g_repo, &opts, confirm_submodule_status, &exp));
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "posix.h"
|
#include "posix.h"
|
||||||
#include "submodule_helpers.h"
|
#include "submodule_helpers.h"
|
||||||
|
#include "git2/sys/repository.h"
|
||||||
|
|
||||||
/* rewrite gitmodules -> .gitmodules
|
/* rewrite gitmodules -> .gitmodules
|
||||||
* rewrite the empty or relative urls inside each module
|
* 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_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_reinit_filesystem(repo, 1));
|
||||||
|
|
||||||
return repo;
|
return repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,5 +121,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_reinit_filesystem(repo, 1));
|
||||||
|
|
||||||
return repo;
|
return repo;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user