summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md8
-rw-r--r--CMakeLists.txt32
-rw-r--r--include/git2/index.h11
-rw-r--r--libgit2.pc.in5
-rw-r--r--src/checkout.c17
-rw-r--r--src/config_file.c39
-rw-r--r--src/diff.c25
-rw-r--r--src/diff_print.c2
-rw-r--r--src/filebuf.c84
-rw-r--r--src/fileops.c251
-rw-r--r--src/fileops.h17
-rw-r--r--src/idxmap.h92
-rw-r--r--src/ignore.c16
-rw-r--r--src/index.c276
-rw-r--r--src/index.h2
-rw-r--r--src/iterator.c183
-rw-r--r--src/odb_loose.c4
-rw-r--r--src/odb_mempack.c8
-rw-r--r--src/oidmap.h2
-rw-r--r--src/path.c23
-rw-r--r--src/path.h8
-rw-r--r--src/refdb_fs.c3
-rw-r--r--src/repository.c36
-rw-r--r--src/submodule.c372
-rw-r--r--src/transaction.c1
-rw-r--r--src/transports/ssh.c4
-rw-r--r--src/win32/path_w32.c23
-rw-r--r--src/win32/posix_w32.c11
-rw-r--r--tests/checkout/index.c2
-rw-r--r--tests/checkout/tree.c84
-rw-r--r--tests/clar_libgit2.c2
-rw-r--r--tests/clone/nonetwork.c9
-rw-r--r--tests/config/global.c6
-rw-r--r--tests/config/write.c21
-rw-r--r--tests/core/buffer.c2
-rw-r--r--tests/core/copy.c8
-rw-r--r--tests/core/dirent.c29
-rw-r--r--tests/core/filebuf.c79
-rw-r--r--tests/core/futils.c68
-rw-r--r--tests/core/mkdir.c128
-rw-r--r--tests/core/stat.c2
-rw-r--r--tests/diff/binary.c21
-rw-r--r--tests/diff/workdir.c43
-rw-r--r--tests/index/bypath.c168
-rw-r--r--tests/index/filemodes.c1
-rw-r--r--tests/index/rename.c36
-rw-r--r--tests/index/tests.c56
-rw-r--r--tests/odb/alternates.c2
-rw-r--r--tests/online/badssl.c27
-rw-r--r--tests/online/push.c3
-rw-r--r--tests/online/push_util.c11
-rw-r--r--tests/online/push_util.h4
-rw-r--r--tests/refs/pack.c2
-rw-r--r--tests/repo/discover.c16
-rw-r--r--tests/repo/init.c6
-rw-r--r--tests/repo/iterator.c6
-rw-r--r--tests/repo/open.c4
-rw-r--r--tests/repo/reservedname.c24
-rw-r--r--tests/resources/sub.git/HEAD1
-rw-r--r--tests/resources/sub.git/config8
-rw-r--r--tests/resources/sub.git/indexbin0 -> 405 bytes
-rw-r--r--tests/resources/sub.git/logs/HEAD1
-rw-r--r--tests/resources/sub.git/logs/refs/heads/master1
-rw-r--r--tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741dbin0 -> 22 bytes
-rw-r--r--tests/resources/sub.git/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623bin0 -> 28 bytes
-rw-r--r--tests/resources/sub.git/objects/94/c7d78d85c933d1d95b56bc2de01833ba8559fbbin0 -> 132 bytes
-rw-r--r--tests/resources/sub.git/objects/b7/a59b3f4ea13b985f8a1e0d3757d5cd3331add8bin0 -> 139 bytes
-rw-r--r--tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773bin0 -> 54 bytes
-rw-r--r--tests/resources/sub.git/refs/heads/master1
-rw-r--r--tests/resources/super/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/super/.gitted/HEAD1
-rw-r--r--tests/resources/super/.gitted/config10
-rw-r--r--tests/resources/super/.gitted/indexbin0 -> 217 bytes
-rw-r--r--tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4bin0 -> 90 bytes
-rw-r--r--tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91bin0 -> 132 bytes
-rw-r--r--tests/resources/super/.gitted/objects/d7/57768b570a83e80d02edcc1032db14573e5034bin0 -> 87 bytes
-rw-r--r--tests/resources/super/.gitted/refs/heads/master1
-rw-r--r--tests/resources/super/gitmodules3
-rw-r--r--tests/status/ignore.c29
-rw-r--r--tests/status/worktree.c10
-rw-r--r--tests/submodule/lookup.c55
-rw-r--r--tests/submodule/status.c10
-rw-r--r--tests/submodule/submodule_helpers.c16
-rw-r--r--tests/submodule/submodule_helpers.h1
-rw-r--r--tests/win32/longpath.c60
85 files changed, 2122 insertions, 512 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8f28cbf3b..6ade3e3b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,9 @@ v0.23 + 1
example `filter=*`. Consumers should examine the attributes parameter
of the `check` function for details.
+* Symlinks are now followed when locking a file, which can be
+ necessary when multiple worktrees share a base repository.
+
### API additions
* `git_config_lock()` has been added, which allow for
@@ -28,6 +31,11 @@ v0.23 + 1
with which to implement the transactional/atomic semantics for the
configuration backend.
+* `git_index_add` will now use the case as provided by the caller on
+ case insensitive systems. Previous versions would keep the case as
+ it existed in the index. This does not affect the higher-level
+ `git_index_add_bypath` or `git_index_add_frombuffer` functions.
+
v0.23
------
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6c03c718c..713640d6a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -59,6 +59,10 @@ IF(MSVC)
# are linking statically
OPTION( STATIC_CRT "Link the static CRT libraries" ON )
+ # If you want to embed a copy of libssh2 into libgit2, pass a
+ # path to libssh2
+ OPTION( EMBED_SSH_PATH "Path to libssh2 to embed (Windows)" OFF )
+
ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE)
@@ -95,6 +99,23 @@ SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.")
SET(LIB_INSTALL_DIR lib CACHE PATH "Where to install libraries to.")
SET(INCLUDE_INSTALL_DIR include CACHE PATH "Where to install headers to.")
+# Set a couple variables to be substituted inside the .pc file.
+# We can't just use LIB_INSTALL_DIR in the .pc file, as passing them as absolue
+# or relative paths is both valid and supported by cmake.
+SET (PKGCONFIG_PREFIX ${CMAKE_INSTALL_PREFIX})
+
+IF(IS_ABSOLUTE ${LIB_INSTALL_DIR})
+ SET (PKGCONFIG_LIBDIR ${LIB_INSTALL_DIR})
+ELSE(IS_ABSOLUTE ${LIB_INSTALL_DIR})
+ SET (PKGCONFIG_LIBDIR "\${prefix}/${LIB_INSTALL_DIR}")
+ENDIF (IS_ABSOLUTE ${LIB_INSTALL_DIR})
+
+IF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
+ SET (PKGCONFIG_INCLUDEDIR ${INCLUDE_INSTALL_DIR})
+ELSE(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
+ SET (PKGCONFIG_INCLUDEDIR "\${prefix}/${INCLUDE_INSTALL_DIR}")
+ENDIF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
+
FUNCTION(TARGET_OS_LIBRARIES target)
IF(WIN32)
TARGET_LINK_LIBRARIES(${target} ws2_32)
@@ -172,6 +193,13 @@ IF (COREFOUNDATION_FOUND)
ENDIF()
+IF (WIN32 AND EMBED_SSH_PATH)
+ FILE(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
+ INCLUDE_DIRECTORIES("${EMBED_SSH_PATH}/include")
+ FILE(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"")
+ ADD_DEFINITIONS(-DGIT_SSH)
+ENDIF()
+
IF (WIN32 AND WINHTTP)
ADD_DEFINITIONS(-DGIT_WINHTTP)
INCLUDE_DIRECTORIES(deps/http-parser)
@@ -501,7 +529,7 @@ ELSE()
ENDIF()
# Compile and link libgit2
-ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
+ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1} ${WIN_RC})
TARGET_LINK_LIBRARIES(git2 ${SECURITY_DIRS})
TARGET_LINK_LIBRARIES(git2 ${COREFOUNDATION_DIRS})
TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
@@ -571,7 +599,7 @@ IF (BUILD_CLAR)
${CLAR_PATH}/clar.c
PROPERTIES OBJECT_DEPENDS ${CLAR_PATH}/clar.suite)
- ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
+ ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1})
TARGET_LINK_LIBRARIES(libgit2_clar ${COREFOUNDATION_DIRS})
TARGET_LINK_LIBRARIES(libgit2_clar ${SECURITY_DIRS})
diff --git a/include/git2/index.h b/include/git2/index.h
index 7caf3ed78..176ba301e 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -643,6 +643,17 @@ GIT_EXTERN(int) git_index_update_all(
*/
GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path);
+/**
+ * Find the first position of any entries matching a prefix. To find the first position
+ * of a path inside a given folder, suffix the prefix with a '/'.
+ *
+ * @param at_pos the address to which the position of the index entry is written (optional)
+ * @param index an existing index object
+ * @param prefix the prefix to search for
+ * @return 0 with valid value in at_pos; an error code otherwise
+ */
+GIT_EXTERN(int) git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix);
+
/**@}*/
/** @name Conflict Index Entry Functions
diff --git a/libgit2.pc.in b/libgit2.pc.in
index 3d825a49f..880266a30 100644
--- a/libgit2.pc.in
+++ b/libgit2.pc.in
@@ -1,5 +1,6 @@
-libdir=@CMAKE_INSTALL_PREFIX@/@LIB_INSTALL_DIR@
-includedir=@CMAKE_INSTALL_PREFIX@/@INCLUDE_INSTALL_DIR@
+prefix=@PKGCONFIG_PREFIX@
+libdir=@PKGCONFIG_LIBDIR@
+includedir=@PKGCONFIG_INCLUDEDIR@
Name: libgit2
Description: The git library, take 2
diff --git a/src/checkout.c b/src/checkout.c
index de48c9e01..2a8bfd558 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -243,6 +243,12 @@ static int checkout_action_common(
if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL)
*action |= CHECKOUT_ACTION__REMOVE;
+ /* if the file is on disk and doesn't match our mode, force update */
+ if (wd &&
+ GIT_PERMS_IS_EXEC(wd->mode) !=
+ GIT_PERMS_IS_EXEC(delta->new_file.mode))
+ *action |= CHECKOUT_ACTION__REMOVE;
+
notify = GIT_CHECKOUT_NOTIFY_UPDATED;
}
@@ -1360,7 +1366,7 @@ static int checkout_mkdir(
mkdir_opts.dir_map = data->mkdir_map;
mkdir_opts.pool = &data->pool;
- error = git_futils_mkdir_ext(
+ error = git_futils_mkdir_relative(
path, base, mode, flags, &mkdir_opts);
data->perfdata.mkdir_calls += mkdir_opts.perfdata.mkdir_calls;
@@ -1500,15 +1506,6 @@ static int blob_content_to_file(
if (error < 0)
return error;
- if (GIT_PERMS_IS_EXEC(mode)) {
- data->perfdata.chmod_calls++;
-
- if ((error = p_chmod(path, mode)) < 0) {
- giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
- return error;
- }
- }
-
if (st) {
data->perfdata.stat_calls++;
diff --git a/src/config_file.c b/src/config_file.c
index a3fec1b34..46f21c0f1 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -1711,6 +1711,7 @@ static const char *quotes_for_value(const char *value)
struct write_data {
git_buf *buf;
+ git_buf buffered_comment;
unsigned int in_section : 1,
preg_replaced : 1;
const char *section;
@@ -1719,16 +1720,21 @@ struct write_data {
const char *value;
};
-static int write_line(struct write_data *write_data, const char *line, size_t line_len)
+static int write_line_to(git_buf *buf, const char *line, size_t line_len)
{
- int result = git_buf_put(write_data->buf, line, line_len);
+ int result = git_buf_put(buf, line, line_len);
if (!result && line_len && line[line_len-1] != '\n')
- result = git_buf_printf(write_data->buf, "\n");
+ result = git_buf_printf(buf, "\n");
return result;
}
+static int write_line(struct write_data *write_data, const char *line, size_t line_len)
+{
+ return write_line_to(write_data->buf, line, line_len);
+}
+
static int write_value(struct write_data *write_data)
{
const char *q;
@@ -1770,6 +1776,14 @@ static int write_on_section(
write_data->in_section = strcmp(current_section, write_data->section) == 0;
+ /*
+ * If there were comments just before this section, dump them as well.
+ */
+ if (!result) {
+ result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size);
+ git_buf_clear(&write_data->buffered_comment);
+ }
+
if (!result)
result = write_line(write_data, line, line_len);
@@ -1787,10 +1801,19 @@ static int write_on_variable(
{
struct write_data *write_data = (struct write_data *)data;
bool has_matched = false;
+ int error;
GIT_UNUSED(reader);
GIT_UNUSED(current_section);
+ /*
+ * If there were comments just before this variable, let's dump them as well.
+ */
+ if ((error = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
+ return error;
+
+ git_buf_clear(&write_data->buffered_comment);
+
/* See if we are to update this name/value pair; first examine name */
if (write_data->in_section &&
strcasecmp(write_data->name, var_name) == 0)
@@ -1825,7 +1848,7 @@ static int write_on_comment(struct reader **reader, const char *line, size_t lin
GIT_UNUSED(reader);
write_data = (struct write_data *)data;
- return write_line(write_data, line, line_len);
+ return write_line_to(&write_data->buffered_comment, line, line_len);
}
static int write_on_eof(struct reader **reader, void *data)
@@ -1835,6 +1858,12 @@ static int write_on_eof(struct reader **reader, void *data)
GIT_UNUSED(reader);
+ /*
+ * If we've buffered comments when reaching EOF, make sure to dump them.
+ */
+ if ((result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
+ return result;
+
/* If we are at the EOF and have not written our value (again, for a
* simple name/value set, not a multivar) then we have never seen the
* section in question and should create a new section and write the
@@ -1892,6 +1921,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
section = git__strndup(key, ldot - key);
write_data.buf = &buf;
+ git_buf_init(&write_data.buffered_comment, 0);
write_data.section = section;
write_data.in_section = 0;
write_data.preg_replaced = 0;
@@ -1901,6 +1931,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
git__free(section);
+ git_buf_free(&write_data.buffered_comment);
if (result < 0) {
git_filebuf_cleanup(&file);
diff --git a/src/diff.c b/src/diff.c
index 32c1d4aa5..d97dcd9d2 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -75,18 +75,27 @@ static int diff_insert_delta(
}
static bool diff_pathspec_match(
- const char **matched_pathspec, git_diff *diff, const char *path)
+ const char **matched_pathspec,
+ git_diff *diff,
+ const git_index_entry *entry)
{
- /* The iterator has filtered out paths for us, so the fact that we're
- * seeing this patch means that it must match the given path list.
+ bool disable_pathspec_match =
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH);
+
+ /* If we're disabling fnmatch, then the iterator has already applied
+ * the filters to the files for us and we don't have to do anything.
+ * However, this only applies to *files* - the iterator will include
+ * directories that we need to recurse into when not autoexpanding,
+ * so we still need to apply the pathspec match to directories.
*/
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH)) {
- *matched_pathspec = path;
+ if ((S_ISLNK(entry->mode) || S_ISREG(entry->mode)) &&
+ disable_pathspec_match) {
+ *matched_pathspec = entry->path;
return true;
}
return git_pathspec__match(
- &diff->pathspec, path, false,
+ &diff->pathspec, entry->path, disable_pathspec_match,
DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
matched_pathspec, NULL);
}
@@ -127,7 +136,7 @@ static int diff_delta__from_one(
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE))
return 0;
- if (!diff_pathspec_match(&matched_pathspec, diff, entry->path))
+ if (!diff_pathspec_match(&matched_pathspec, diff, entry))
return 0;
delta = diff_delta__alloc(diff, status, entry->path);
@@ -768,7 +777,7 @@ static int maybe_modified(
const char *matched_pathspec;
int error = 0;
- if (!diff_pathspec_match(&matched_pathspec, diff, oitem->path))
+ if (!diff_pathspec_match(&matched_pathspec, diff, oitem))
return 0;
memset(&noid, 0, sizeof(noid));
diff --git a/src/diff_print.c b/src/diff_print.c
index d406a441a..bc2d6fab0 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -358,6 +358,7 @@ static int format_binary(
scan += chunk_len;
pi->line.num_lines++;
}
+ git_buf_putc(pi->buf, '\n');
return 0;
}
@@ -416,7 +417,6 @@ static int diff_print_patch_file_binary(
if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
- (error = git_buf_putc(pi->buf, '\n')) < 0 ||
(error = format_binary(pi, binary->old_file.type, binary->old_file.data,
binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
diff --git a/src/filebuf.c b/src/filebuf.c
index 838f4b4d2..2bbc210ba 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -191,6 +191,81 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
return 0;
}
+#define MAX_SYMLINK_DEPTH 5
+
+static int resolve_symlink(git_buf *out, const char *path)
+{
+ int i, error, root;
+ ssize_t ret;
+ struct stat st;
+ git_buf curpath = GIT_BUF_INIT, target = GIT_BUF_INIT;
+
+ if ((error = git_buf_grow(&target, GIT_PATH_MAX + 1)) < 0 ||
+ (error = git_buf_puts(&curpath, path)) < 0)
+ return error;
+
+ for (i = 0; i < MAX_SYMLINK_DEPTH; i++) {
+ error = p_lstat(curpath.ptr, &st);
+ if (error < 0 && errno == ENOENT) {
+ error = git_buf_puts(out, curpath.ptr);
+ goto cleanup;
+ }
+
+ if (error < 0) {
+ giterr_set(GITERR_OS, "failed to stat '%s'", curpath.ptr);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (!S_ISLNK(st.st_mode)) {
+ error = git_buf_puts(out, curpath.ptr);
+ goto cleanup;
+ }
+
+ ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX);
+ if (ret < 0) {
+ giterr_set(GITERR_OS, "failed to read symlink '%s'", curpath.ptr);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (ret == GIT_PATH_MAX) {
+ giterr_set(GITERR_INVALID, "symlink target too long");
+ error = -1;
+ goto cleanup;
+ }
+
+ /* readlink(2) won't NUL-terminate for us */
+ target.ptr[ret] = '\0';
+ target.size = ret;
+
+ root = git_path_root(target.ptr);
+ if (root >= 0) {
+ if ((error = git_buf_puts(&curpath, target.ptr)) < 0)
+ goto cleanup;
+ } else {
+ git_buf dir = GIT_BUF_INIT;
+
+ if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0)
+ goto cleanup;
+
+ git_buf_swap(&curpath, &dir);
+ git_buf_free(&dir);
+
+ if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0)
+ goto cleanup;
+ }
+ }
+
+ giterr_set(GITERR_INVALID, "maximum symlink depth reached");
+ error = -1;
+
+cleanup:
+ git_buf_free(&curpath);
+ git_buf_free(&target);
+ return error;
+}
+
int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
{
int compression, error = -1;
@@ -265,11 +340,14 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
file->path_lock = git_buf_detach(&tmp_path);
GITERR_CHECK_ALLOC(file->path_lock);
} else {
- path_len = strlen(path);
+ git_buf resolved_path = GIT_BUF_INIT;
+
+ if ((error = resolve_symlink(&resolved_path, path)) < 0)
+ goto cleanup;
/* Save the original path of the file */
- file->path_original = git__strdup(path);
- GITERR_CHECK_ALLOC(file->path_original);
+ path_len = resolved_path.size;
+ file->path_original = git_buf_detach(&resolved_path);
/* create the locking path by appending ".lock" to the original */
GITERR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH);
diff --git a/src/fileops.c b/src/fileops.c
index b7b55159f..57d2ce9c3 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -18,7 +18,7 @@ GIT__USE_STRMAP
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{
return git_futils_mkdir(
- file_path, NULL, mode,
+ file_path, mode,
GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
}
@@ -289,97 +289,230 @@ void git_futils_mmap_free(git_map *out)
p_munmap(out);
}
-GIT_INLINE(int) validate_existing(
- const char *make_path,
+GIT_INLINE(int) mkdir_validate_dir(
+ const char *path,
struct stat *st,
mode_t mode,
uint32_t flags,
- struct git_futils_mkdir_perfdata *perfdata)
+ struct git_futils_mkdir_options *opts)
{
+ /* with exclusive create, existing dir is an error */
+ if ((flags & GIT_MKDIR_EXCL) != 0) {
+ giterr_set(GITERR_FILESYSTEM,
+ "Failed to make directory '%s': directory exists", path);
+ return GIT_EEXISTS;
+ }
+
if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) ||
(S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) {
- if (p_unlink(make_path) < 0) {
+ if (p_unlink(path) < 0) {
giterr_set(GITERR_OS, "Failed to remove %s '%s'",
- S_ISLNK(st->st_mode) ? "symlink" : "file", make_path);
+ S_ISLNK(st->st_mode) ? "symlink" : "file", path);
return GIT_EEXISTS;
}
- perfdata->mkdir_calls++;
+ opts->perfdata.mkdir_calls++;
- if (p_mkdir(make_path, mode) < 0) {
- giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path);
+ if (p_mkdir(path, mode) < 0) {
+ giterr_set(GITERR_OS, "Failed to make directory '%s'", path);
return GIT_EEXISTS;
}
}
else if (S_ISLNK(st->st_mode)) {
/* Re-stat the target, make sure it's a directory */
- perfdata->stat_calls++;
+ opts->perfdata.stat_calls++;
- if (p_stat(make_path, st) < 0) {
- giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path);
+ if (p_stat(path, st) < 0) {
+ giterr_set(GITERR_OS, "Failed to make directory '%s'", path);
return GIT_EEXISTS;
}
}
else if (!S_ISDIR(st->st_mode)) {
giterr_set(GITERR_FILESYSTEM,
- "Failed to make directory '%s': directory exists", make_path);
+ "Failed to make directory '%s': directory exists", path);
return GIT_EEXISTS;
}
return 0;
}
-int git_futils_mkdir_ext(
+GIT_INLINE(int) mkdir_validate_mode(
const char *path,
- const char *base,
+ struct stat *st,
+ bool terminal_path,
mode_t mode,
uint32_t flags,
struct git_futils_mkdir_options *opts)
{
- int error = -1;
- git_buf make_path = GIT_BUF_INIT;
- ssize_t root = 0, min_root_len, root_len;
- char lastch = '/', *tail;
- struct stat st;
+ if (((terminal_path && (flags & GIT_MKDIR_CHMOD) != 0) ||
+ (flags & GIT_MKDIR_CHMOD_PATH) != 0) && st->st_mode != mode) {
- /* build path and find "root" where we should start calling mkdir */
- if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
- return -1;
+ opts->perfdata.chmod_calls++;
- if (make_path.size == 0) {
- giterr_set(GITERR_OS, "Attempt to create empty path");
- goto done;
+ if (p_chmod(path, mode) < 0) {
+ giterr_set(GITERR_OS, "failed to set permissions on '%s'", path);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) mkdir_canonicalize(
+ git_buf *path,
+ uint32_t flags)
+{
+ ssize_t root_len;
+
+ if (path->size == 0) {
+ giterr_set(GITERR_OS, "attempt to create empty path");
+ return -1;
}
/* Trim trailing slashes (except the root) */
- if ((root_len = git_path_root(make_path.ptr)) < 0)
+ if ((root_len = git_path_root(path->ptr)) < 0)
root_len = 0;
else
root_len++;
- while (make_path.size > (size_t)root_len &&
- make_path.ptr[make_path.size - 1] == '/')
- make_path.ptr[--make_path.size] = '\0';
+ while (path->size > (size_t)root_len && path->ptr[path->size - 1] == '/')
+ path->ptr[--path->size] = '\0';
/* if we are not supposed to made the last element, truncate it */
if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
- git_path_dirname_r(&make_path, make_path.ptr);
+ git_path_dirname_r(path, path->ptr);
flags |= GIT_MKDIR_SKIP_LAST;
}
if ((flags & GIT_MKDIR_SKIP_LAST) != 0) {
- git_path_dirname_r(&make_path, make_path.ptr);
+ git_path_dirname_r(path, path->ptr);
}
/* We were either given the root path (or trimmed it to
- * the root), we don't have anything to do.
+ * the root), we don't have anything to do.
+ */
+ if (path->size <= (size_t)root_len)
+ git_buf_clear(path);
+
+ return 0;
+}
+
+int git_futils_mkdir(
+ const char *path,
+ mode_t mode,
+ uint32_t flags)
+{
+ git_buf make_path = GIT_BUF_INIT, parent_path = GIT_BUF_INIT;
+ const char *relative;
+ struct git_futils_mkdir_options opts = { 0 };
+ struct stat st;
+ size_t depth = 0;
+ int len = 0, root_len, error;
+
+ if ((error = git_buf_puts(&make_path, path)) < 0 ||
+ (error = mkdir_canonicalize(&make_path, flags)) < 0 ||
+ (error = git_buf_puts(&parent_path, make_path.ptr)) < 0 ||
+ make_path.size == 0)
+ goto done;
+
+ root_len = git_path_root(make_path.ptr);
+
+ /* find the first parent directory that exists. this will be used
+ * as the base to dirname_relative.
*/
- if (make_path.size <= (size_t)root_len) {
- error = 0;
+ for (relative = make_path.ptr; parent_path.size; ) {
+ error = p_lstat(parent_path.ptr, &st);
+
+ if (error == 0) {
+ break;
+ } else if (errno != ENOENT) {
+ giterr_set(GITERR_OS, "failed to stat '%s'", parent_path.ptr);
+ goto done;
+ }
+
+ depth++;
+
+ /* examine the parent of the current path */
+ if ((len = git_path_dirname_r(&parent_path, parent_path.ptr)) < 0) {
+ error = len;
+ goto done;
+ }
+
+ assert(len);
+
+ /* we've walked all the given path's parents and it's either relative
+ * or rooted. either way, give up and make the entire path.
+ */
+ if ((len == 1 && parent_path.ptr[0] == '.') || len == root_len+1) {
+ relative = make_path.ptr;
+ break;
+ }
+
+ relative = make_path.ptr + len + 1;
+
+ /* not recursive? just make this directory relative to its parent. */
+ if ((flags & GIT_MKDIR_PATH) == 0)
+ break;
+ }
+
+ /* we found an item at the location we're trying to create,
+ * validate it.
+ */
+ if (depth == 0) {
+ error = mkdir_validate_dir(make_path.ptr, &st, mode, flags, &opts);
+
+ if (!error)
+ error = mkdir_validate_mode(
+ make_path.ptr, &st, true, mode, flags, &opts);
+
goto done;
}
+ /* we already took `SKIP_LAST` and `SKIP_LAST2` into account when
+ * canonicalizing `make_path`.
+ */
+ flags &= ~(GIT_MKDIR_SKIP_LAST2 | GIT_MKDIR_SKIP_LAST);
+
+ error = git_futils_mkdir_relative(relative,
+ parent_path.size ? parent_path.ptr : NULL, mode, flags, &opts);
+
+done:
+ git_buf_free(&make_path);
+ git_buf_free(&parent_path);
+ return error;
+}
+
+int git_futils_mkdir_r(const char *path, const mode_t mode)
+{
+ return git_futils_mkdir(path, mode, GIT_MKDIR_PATH);
+}
+
+int git_futils_mkdir_relative(
+ const char *relative_path,
+ const char *base,
+ mode_t mode,
+ uint32_t flags,
+ struct git_futils_mkdir_options *opts)
+{
+ git_buf make_path = GIT_BUF_INIT;
+ ssize_t root = 0, min_root_len;
+ char lastch = '/', *tail;
+ struct stat st;
+ struct git_futils_mkdir_options empty_opts = {0};
+ int error;
+
+ if (!opts)
+ opts = &empty_opts;
+
+ /* build path and find "root" where we should start calling mkdir */
+ if (git_path_join_unrooted(&make_path, relative_path, base, &root) < 0)
+ return -1;
+
+ if ((error = mkdir_canonicalize(&make_path, flags)) < 0 ||
+ make_path.size == 0)
+ goto done;
+
/* if we are not supposed to make the whole path, reset root */
if ((flags & GIT_MKDIR_PATH) == 0)
root = git_buf_rfind(&make_path, '/');
@@ -437,32 +570,15 @@ retry_lstat:
goto done;
}
} else {
- /* with exclusive create, existing dir is an error */
- if ((flags & GIT_MKDIR_EXCL) != 0) {
- giterr_set(GITERR_FILESYSTEM, "Failed to make directory '%s': directory exists", make_path.ptr);
- error = GIT_EEXISTS;
+ if ((error = mkdir_validate_dir(
+ make_path.ptr, &st, mode, flags, opts)) < 0)
goto done;
- }
-
- if ((error = validate_existing(
- make_path.ptr, &st, mode, flags, &opts->perfdata)) < 0)
- goto done;
}
/* chmod if requested and necessary */
- if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
- (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
- st.st_mode != mode) {
-
- opts->perfdata.chmod_calls++;
-
- if ((error = p_chmod(make_path.ptr, mode)) < 0 &&
- lastch == '\0') {
- giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
- make_path.ptr);
- goto done;
- }
- }
+ if ((error = mkdir_validate_mode(
+ make_path.ptr, &st, (lastch == '\0'), mode, flags, opts)) < 0)
+ goto done;
if (opts->dir_map && opts->pool) {
char *cache_path;
@@ -501,21 +617,6 @@ done:
return error;
}
-int git_futils_mkdir(
- const char *path,
- const char *base,
- mode_t mode,
- uint32_t flags)
-{
- struct git_futils_mkdir_options options = {0};
- return git_futils_mkdir_ext(path, base, mode, flags, &options);
-}
-
-int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
-{
- return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH);
-}
-
typedef struct {
const char *base;
size_t baselen;
@@ -777,7 +878,7 @@ static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
/* create root directory the first time we need to create a directory */
if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) {
error = git_futils_mkdir(
- info->to_root, NULL, info->dirmode,
+ info->to_root, info->dirmode,
(info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0);
info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT;
@@ -785,9 +886,9 @@ static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
/* create directory with root as base to prevent excess chmods */
if (!error)
- error = git_futils_mkdir(
+ error = git_futils_mkdir_relative(
from->ptr + info->from_prefix, info->to_root,
- info->dirmode, info->mkdir_flags);
+ info->dirmode, info->mkdir_flags, NULL);
return error;
}
diff --git a/src/fileops.h b/src/fileops.h
index 0f6466c59..572ff01a5 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -55,12 +55,9 @@ extern int git_futils_creat_locked(const char *path, const mode_t mode);
extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode);
/**
- * Create a path recursively
- *
- * If a base parameter is being passed, it's expected to be valued with a
- * path pointing to an already existing directory.
+ * Create a path recursively.
*/
-extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode);
+extern int git_futils_mkdir_r(const char *path, const mode_t mode);
/**
* Flags to pass to `git_futils_mkdir`.
@@ -111,20 +108,20 @@ struct git_futils_mkdir_options
* and optionally chmods the directory immediately after (or each part of the
* path if requested).
*
- * @param path The path to create.
+ * @param path The path to create, relative to base.
* @param base Root for relative path. These directories will never be made.
* @param mode The mode to use for created directories.
* @param flags Combination of the mkdir flags above.
- * @param opts Extended options, use `git_futils_mkdir` if you are not interested.
+ * @param opts Extended options, or null.
* @return 0 on success, else error code
*/
-extern int git_futils_mkdir_ext(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts);
+extern int git_futils_mkdir_relative(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts);
/**
- * Create a directory or entire path. Similar to `git_futils_mkdir_withperf`
+ * Create a directory or entire path. Similar to `git_futils_mkdir_relative`
* without performance data.
*/
-extern int git_futils_mkdir(const char *path, const char *base, mode_t mode, uint32_t flags);
+extern int git_futils_mkdir(const char *path, mode_t mode, uint32_t flags);
/**
* Create all the folders required to contain
diff --git a/src/idxmap.h b/src/idxmap.h
new file mode 100644
index 000000000..74304bb97
--- /dev/null
+++ b/src/idxmap.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_idxmap_h__
+#define INCLUDE_idxmap_h__
+
+#include <ctype.h>
+#include "common.h"
+#include "git2/index.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kreallocarray git__reallocarray
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(idx, const git_index_entry *, git_index_entry *)
+__KHASH_TYPE(idxicase, const git_index_entry *, git_index_entry *)
+
+typedef khash_t(idx) git_idxmap;
+typedef khash_t(idxicase) git_idxmap_icase;
+
+typedef khiter_t git_idxmap_iter;
+
+/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */
+static kh_inline khint_t idxentry_hash(const git_index_entry *e)
+{
+ const char *s = e->path;
+ khint_t h = (khint_t)git__tolower(*s);
+ if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s);
+ return h + GIT_IDXENTRY_STAGE(e);
+}
+
+#define idxentry_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcmp(a->path, b->path) == 0)
+#define idxentry_icase_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0)
+
+#define GIT__USE_IDXMAP \
+ __KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal)
+
+#define GIT__USE_IDXMAP_ICASE \
+ __KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal)
+
+#define git_idxmap_alloc(hp) \
+ ((*(hp) = kh_init(idx)) == NULL) ? giterr_set_oom(), -1 : 0
+
+#define git_idxmap_icase_alloc(hp) \
+ ((*(hp) = kh_init(idxicase)) == NULL) ? giterr_set_oom(), -1 : 0
+
+#define git_idxmap_insert(h, key, val, rval) do { \
+ khiter_t __pos = kh_put(idx, h, key, &rval); \
+ if (rval >= 0) { \
+ if (rval == 0) kh_key(h, __pos) = key; \
+ kh_val(h, __pos) = val; \
+ } } while (0)
+
+#define git_idxmap_icase_insert(h, key, val, rval) do { \
+ khiter_t __pos = kh_put(idxicase, h, key, &rval); \
+ if (rval >= 0) { \
+ if (rval == 0) kh_key(h, __pos) = key; \
+ kh_val(h, __pos) = val; \
+ } } while (0)
+
+#define git_idxmap_lookup_index(h, k) kh_get(idx, h, k)
+#define git_idxmap_icase_lookup_index(h, k) kh_get(idxicase, h, k)
+#define git_idxmap_value_at(h, idx) kh_val(h, idx)
+#define git_idxmap_valid_index(h, idx) (idx != kh_end(h))
+#define git_idxmap_has_data(h, idx) kh_exist(h, idx)
+
+#define git_idxmap_free(h) kh_destroy(idx, h), h = NULL
+#define git_idxmap_clear(h) kh_clear(idx, h)
+
+#define git_idxmap_delete_at(h, id) kh_del(idx, h, id)
+#define git_idxmap_icase_delete_at(h, id) kh_del(idxicase, h, id)
+
+#define git_idxmap_delete(h, key) do { \
+ khiter_t __pos = git_idxmap_lookup_index(h, key); \
+ if (git_idxmap_valid_index(h, __pos)) \
+ git_idxmap_delete_at(h, __pos); } while (0)
+
+#define git_idxmap_icase_delete(h, key) do { \
+ khiter_t __pos = git_idxmap_icase_lookup_index(h, key); \
+ if (git_idxmap_valid_index(h, __pos)) \
+ git_idxmap_icase_delete_at(h, __pos); } while (0)
+
+#define git_idxmap_begin kh_begin
+#define git_idxmap_end kh_end
+
+#endif
diff --git a/src/ignore.c b/src/ignore.c
index 0031e4696..aedc1401e 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -89,18 +89,20 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
}
/*
- * If we're dealing with a directory (which we know via the
- * strchr() check) we want to use 'dirname/<star>' as the
- * pattern so p_fnmatch() honours FNM_PATHNAME
+ * When dealing with a directory, we add '/<star>' so
+ * p_fnmatch() honours FNM_PATHNAME. Checking for LEADINGDIR
+ * alone isn't enough as that's also set for nagations, so we
+ * need to check that NEGATIVE is off.
*/
git_buf_clear(&buf);
if (rule->containing_dir) {
git_buf_puts(&buf, rule->containing_dir);
}
- if (!strchr(rule->pattern, '*'))
- error = git_buf_printf(&buf, "%s/*", rule->pattern);
- else
- error = git_buf_puts(&buf, rule->pattern);
+
+ error = git_buf_puts(&buf, rule->pattern);
+
+ if ((rule->flags & (GIT_ATTR_FNMATCH_LEADINGDIR | GIT_ATTR_FNMATCH_NEGATIVE)) == GIT_ATTR_FNMATCH_LEADINGDIR)
+ error = git_buf_PUTS(&buf, "/*");
if (error < 0)
goto out;
diff --git a/src/index.c b/src/index.c
index 53120e49c..2e8934780 100644
--- a/src/index.c
+++ b/src/index.c
@@ -17,6 +17,7 @@
#include "pathspec.h"
#include "ignore.h"
#include "blob.h"
+#include "idxmap.h"
#include "git2/odb.h"
#include "git2/oid.h"
@@ -24,6 +25,32 @@
#include "git2/config.h"
#include "git2/sys/index.h"
+GIT__USE_IDXMAP
+GIT__USE_IDXMAP_ICASE
+
+#define INSERT_IN_MAP_EX(idx, map, e, err) do { \
+ if ((idx)->ignore_case) \
+ git_idxmap_icase_insert((khash_t(idxicase) *) (map), (e), (e), (err)); \
+ else \
+ git_idxmap_insert((map), (e), (e), (err)); \
+ } while (0)
+
+#define INSERT_IN_MAP(idx, e, err) INSERT_IN_MAP_EX(idx, (idx)->entries_map, e, err)
+
+#define LOOKUP_IN_MAP(p, idx, k) do { \
+ if ((idx)->ignore_case) \
+ (p) = git_idxmap_icase_lookup_index((khash_t(idxicase) *) index->entries_map, (k)); \
+ else \
+ (p) = git_idxmap_lookup_index(index->entries_map, (k)); \
+ } while (0)
+
+#define DELETE_IN_MAP(idx, e) do { \
+ if ((idx)->ignore_case) \
+ git_idxmap_icase_delete((khash_t(idxicase) *) (idx)->entries_map, (e)); \
+ else \
+ git_idxmap_delete((idx)->entries_map, (e)); \
+ } while (0)
+
static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths,
unsigned int flags,
git_index_matched_path_cb cb, void *payload);
@@ -425,6 +452,7 @@ int git_index_open(git_index **index_out, const char *index_path)
}
if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 ||
+ git_idxmap_alloc(&index->entries_map) < 0 ||
git_vector_init(&index->names, 8, conflict_name_cmp) < 0 ||
git_vector_init(&index->reuc, 8, reuc_cmp) < 0 ||
git_vector_init(&index->deleted, 8, git_index_entry_cmp) < 0)
@@ -462,6 +490,7 @@ static void index_free(git_index *index)
assert(!git_atomic_get(&index->readers));
git_index_clear(index);
+ git_idxmap_free(index->entries_map);
git_vector_free(&index->entries);
git_vector_free(&index->names);
git_vector_free(&index->reuc);
@@ -508,6 +537,7 @@ static int index_remove_entry(git_index *index, size_t pos)
if (entry != NULL)
git_tree_cache_invalidate_path(index->tree, entry->path);
+ DELETE_IN_MAP(index, entry);
error = git_vector_remove(&index->entries, pos);
if (!error) {
@@ -535,6 +565,7 @@ int git_index_clear(git_index *index)
return -1;
}
+ git_idxmap_clear(index->entries_map);
while (!error && index->entries.length > 0)
error = index_remove_entry(index, index->entries.length - 1);
index_free_deleted(index);
@@ -805,16 +836,21 @@ const git_index_entry *git_index_get_byindex(
const git_index_entry *git_index_get_bypath(
git_index *index, const char *path, int stage)
{
- size_t pos;
+ khiter_t pos;
+ git_index_entry key = {{ 0 }};
assert(index);
- if (index_find(&pos, index, path, 0, stage, true) < 0) {
- giterr_set(GITERR_INDEX, "Index does not contain %s", path);
- return NULL;
- }
+ key.path = path;
+ GIT_IDXENTRY_STAGE_SET(&key, stage);
+
+ LOOKUP_IN_MAP(pos, index, &key);
- return git_index_get_byindex(index, pos);
+ if (git_idxmap_valid_index(index->entries_map, pos))
+ return git_idxmap_value_at(index->entries_map, pos);
+
+ giterr_set(GITERR_INDEX, "Index does not contain %s", path);
+ return NULL;
}
void git_index_entry__init_from_stat(
@@ -941,16 +977,27 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
return 0;
}
-static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src)
+static void index_entry_cpy(
+ git_index_entry *tgt,
+ git_index *index,
+ const git_index_entry *src,
+ bool update_path)
{
const char *tgt_path = tgt->path;
memcpy(tgt, src, sizeof(*tgt));
- tgt->path = tgt_path; /* reset to existing path data */
+
+ /* keep the existing path buffer, but update the path to the one
+ * given by the caller, if we trust it.
+ */
+ tgt->path = tgt_path;
+
+ if (index->ignore_case && update_path)
+ memcpy((char *)tgt->path, src->path, strlen(tgt->path));
}
static int index_entry_dup(
git_index_entry **out,
- git_repository *repo,
+ git_index *index,
const git_index_entry *src)
{
git_index_entry *entry;
@@ -960,10 +1007,10 @@ static int index_entry_dup(
return 0;
}
- if (index_entry_create(&entry, repo, src->path) < 0)
+ if (index_entry_create(&entry, INDEX_OWNER(index), src->path) < 0)
return -1;
- index_entry_cpy(entry, src);
+ index_entry_cpy(entry, index, src, false);
*out = entry;
return 0;
}
@@ -1066,6 +1113,74 @@ static int check_file_directory_collision(git_index *index,
return 0;
}
+static int canonicalize_directory_path(
+ git_index *index, git_index_entry *entry)
+{
+ const git_index_entry *match, *best = NULL;
+ char *search, *sep;
+ size_t pos, search_len, best_len;
+
+ if (!index->ignore_case)
+ return 0;
+
+ /* item already exists in the index, simply re-use the existing case */
+ if ((match = git_index_get_bypath(index, entry->path, 0)) != NULL) {
+ memcpy((char *)entry->path, match->path, strlen(entry->path));
+ return 0;
+ }
+
+ /* nothing to do */
+ if (strchr(entry->path, '/') == NULL)
+ return 0;
+
+ if ((search = git__strdup(entry->path)) == NULL)
+ return -1;
+
+ /* starting at the parent directory and descending to the root, find the
+ * common parent directory.
+ */
+ while (!best && (sep = strrchr(search, '/'))) {
+ sep[1] = '\0';
+
+ search_len = strlen(search);
+
+ git_vector_bsearch2(
+ &pos, &index->entries, index->entries_search_path, search);
+
+ while ((match = git_vector_get(&index->entries, pos))) {
+ if (GIT_IDXENTRY_STAGE(match) != 0) {
+ /* conflicts do not contribute to canonical paths */
+ } else if (memcmp(search, match->path, search_len) == 0) {
+ /* prefer an exact match to the input filename */
+ best = match;
+ best_len = search_len;
+ break;
+ } else if (strncasecmp(search, match->path, search_len) == 0) {
+ /* continue walking, there may be a path with an exact
+ * (case sensitive) match later in the index, but use this
+ * as the best match until that happens.
+ */
+ if (!best) {
+ best = match;
+ best_len = search_len;
+ }
+ } else {
+ break;
+ }
+
+ pos++;
+ }
+
+ sep[0] = '\0';
+ }
+
+ if (best)
+ memcpy((char *)entry->path, best->path, best_len);
+
+ git__free(search);
+ return 0;
+}
+
static int index_no_dups(void **old, void *new)
{
const git_index_entry *entry = new;
@@ -1079,10 +1194,17 @@ static int index_no_dups(void **old, void *new)
* it, then it will return an error **and also free the entry**. When
* it replaces an existing entry, it will update the entry_ptr with the
* actual entry in the index (and free the passed in one).
+ * trust_path is whether we use the given path, or whether (on case
+ * insensitive systems only) we try to canonicalize the given path to
+ * be within an existing directory.
* trust_mode is whether we trust the mode in entry_ptr.
*/
static int index_insert(
- git_index *index, git_index_entry **entry_ptr, int replace, bool trust_mode)
+ git_index *index,
+ git_index_entry **entry_ptr,
+ int replace,
+ bool trust_path,
+ bool trust_mode)
{
int error = 0;
size_t path_length, position;
@@ -1120,8 +1242,14 @@ static int index_insert(
entry->mode = index_merge_mode(index, existing, entry->mode);
}
+ /* canonicalize the directory name */
+ if (!trust_path)
+ error = canonicalize_directory_path(index, entry);
+
/* look for tree / blob name collisions, removing conflicts if requested */
- error = check_file_directory_collision(index, entry, position, replace);
+ if (!error)
+ error = check_file_directory_collision(index, entry, position, replace);
+
if (error < 0)
/* skip changes */;
@@ -1130,7 +1258,7 @@ static int index_insert(
*/
else if (existing) {
if (replace)
- index_entry_cpy(existing, entry);
+ index_entry_cpy(existing, index, entry, trust_path);
index_entry_free(entry);
*entry_ptr = entry = existing;
}
@@ -1140,6 +1268,10 @@ static int index_insert(
* check for dups, this is actually cheaper in the long run.)
*/
error = git_vector_insert_sorted(&index->entries, entry, index_no_dups);
+
+ if (error == 0) {
+ INSERT_IN_MAP(index, entry, error);
+ }
}
if (error < 0) {
@@ -1206,7 +1338,7 @@ int git_index_add_frombuffer(
return -1;
}
- if (index_entry_dup(&entry, INDEX_OWNER(index), source_entry) < 0)
+ if (index_entry_dup(&entry, index, source_entry) < 0)
return -1;
error = git_blob_create_frombuffer(&id, INDEX_OWNER(index), buffer, len);
@@ -1218,7 +1350,7 @@ int git_index_add_frombuffer(
git_oid_cpy(&entry->id, &id);
entry->file_size = len;
- if ((error = index_insert(index, &entry, 1, true)) < 0)
+ if ((error = index_insert(index, &entry, 1, true, true)) < 0)
return error;
/* Adding implies conflict was resolved, move conflict entries to REUC */
@@ -1277,7 +1409,7 @@ int git_index_add_bypath(git_index *index, const char *path)
assert(index && path);
if ((ret = index_entry_init(&entry, index, path)) == 0)
- ret = index_insert(index, &entry, 1, false);
+ ret = index_insert(index, &entry, 1, false, false);
/* If we were given a directory, let's see if it's a submodule */
if (ret < 0 && ret != GIT_EDIRECTORY)
@@ -1303,7 +1435,7 @@ int git_index_add_bypath(git_index *index, const char *path)
if ((ret = add_repo_as_submodule(&entry, index, path)) < 0)
return ret;
- if ((ret = index_insert(index, &entry, 1, false)) < 0)
+ if ((ret = index_insert(index, &entry, 1, false, false)) < 0)
return ret;
} else if (ret < 0) {
return ret;
@@ -1353,8 +1485,8 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
return -1;
}
- if ((ret = index_entry_dup(&entry, INDEX_OWNER(index), source_entry)) < 0 ||
- (ret = index_insert(index, &entry, 1, true)) < 0)
+ if ((ret = index_entry_dup(&entry, index, source_entry)) < 0 ||
+ (ret = index_insert(index, &entry, 1, true, true)) < 0)
return ret;
git_tree_cache_invalidate_path(index->tree, entry->path);
@@ -1365,12 +1497,18 @@ int git_index_remove(git_index *index, const char *path, int stage)
{
int error;
size_t position;
+ git_index_entry remove_key = {{ 0 }};
if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock index");
return -1;
}
+ remove_key.path = path;
+ GIT_IDXENTRY_STAGE_SET(&remove_key, stage);
+
+ DELETE_IN_MAP(index, &remove_key);
+
if (index_find(&position, index, path, 0, stage, false) < 0) {
giterr_set(
GITERR_INDEX, "Index does not contain %s at stage %d", path, stage);
@@ -1420,6 +1558,30 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage)
return error;
}
+int git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix)
+{
+ int error = 0;
+ size_t pos;
+ const git_index_entry *entry;
+
+ if (git_mutex_lock(&index->lock) < 0) {
+ giterr_set(GITERR_OS, "Failed to lock index");
+ return -1;
+ }
+
+ index_find(&pos, index, prefix, strlen(prefix), GIT_INDEX_STAGE_ANY, false);
+ entry = git_vector_get(&index->entries, pos);
+ if (!entry || git__prefixcmp(entry->path, prefix) != 0)
+ error = GIT_ENOTFOUND;
+
+ if (!error && at_pos)
+ *at_pos = pos;
+
+ git_mutex_unlock(&index->lock);
+
+ return error;
+}
+
int git_index__find_pos(
size_t *out, git_index *index, const char *path, size_t path_len, int stage)
{
@@ -1473,9 +1635,9 @@ int git_index_conflict_add(git_index *index,
assert (index);
- if ((ret = index_entry_dup(&entries[0], INDEX_OWNER(index), ancestor_entry)) < 0 ||
- (ret = index_entry_dup(&entries[1], INDEX_OWNER(index), our_entry)) < 0 ||
- (ret = index_entry_dup(&entries[2], INDEX_OWNER(index), their_entry)) < 0)
+ if ((ret = index_entry_dup(&entries[0], index, ancestor_entry)) < 0 ||
+ (ret = index_entry_dup(&entries[1], index, our_entry)) < 0 ||
+ (ret = index_entry_dup(&entries[2], index, their_entry)) < 0)
goto on_error;
/* Validate entries */
@@ -1509,7 +1671,7 @@ int git_index_conflict_add(git_index *index,
/* Make sure stage is correct */
GIT_IDXENTRY_STAGE_SET(entries[i], i + 1);
- if ((ret = index_insert(index, &entries[i], 0, true)) < 0)
+ if ((ret = index_insert(index, &entries[i], 0, true, true)) < 0)
goto on_error;
entries[i] = NULL; /* don't free if later entry fails */
@@ -2083,7 +2245,7 @@ static size_t read_entry(
entry.path = (char *)path_ptr;
- if (index_entry_dup(out, INDEX_OWNER(index), &entry) < 0)
+ if (index_entry_dup(out, index, &entry) < 0)
return 0;
return entry_size;
@@ -2181,6 +2343,11 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
assert(!index->entries.length);
+ if (index->ignore_case)
+ kh_resize(idxicase, (khash_t(idxicase) *) index->entries_map, header.entry_count);
+ else
+ kh_resize(idx, index->entries_map, header.entry_count);
+
/* Parse all the entries */
for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
git_index_entry *entry;
@@ -2197,6 +2364,13 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
goto done;
}
+ INSERT_IN_MAP(index, entry, error);
+
+ if (error < 0) {
+ index_entry_free(entry);
+ goto done;
+ }
+
seek_forward(entry_size);
}
@@ -2588,7 +2762,7 @@ static int read_tree_cb(
entry->mode == old_entry->mode &&
git_oid_equal(&entry->id, &old_entry->id))
{
- index_entry_cpy(entry, old_entry);
+ index_entry_cpy(entry, data->index, old_entry, false);
entry->flags_extended = 0;
}
@@ -2611,7 +2785,13 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
{
int error = 0;
git_vector entries = GIT_VECTOR_INIT;
+ git_idxmap *entries_map;
read_tree_data data;
+ size_t i;
+ git_index_entry *e;
+
+ if (git_idxmap_alloc(&entries_map) < 0)
+ return -1;
git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */
@@ -2626,23 +2806,41 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
if (index_sort_if_needed(index, true) < 0)
return -1;
- error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data);
+ if ((error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data)) < 0)
+ goto cleanup;
- if (!error) {
- git_vector_sort(&entries);
+ if (index->ignore_case)
+ kh_resize(idxicase, (khash_t(idxicase) *) entries_map, entries.length);
+ else
+ kh_resize(idx, entries_map, entries.length);
- if ((error = git_index_clear(index)) < 0)
- /* well, this isn't good */;
- else if (git_mutex_lock(&index->lock) < 0) {
- giterr_set(GITERR_OS, "Unable to acquire index lock");
- error = -1;
- } else {
- git_vector_swap(&entries, &index->entries);
- git_mutex_unlock(&index->lock);
+ git_vector_foreach(&entries, i, e) {
+ INSERT_IN_MAP_EX(index, entries_map, e, error);
+
+ if (error < 0) {
+ giterr_set(GITERR_INDEX, "failed to insert entry into map");
+ return error;
}
}
+ error = 0;
+
+ git_vector_sort(&entries);
+
+ if ((error = git_index_clear(index)) < 0)
+ /* well, this isn't good */;
+ else if (git_mutex_lock(&index->lock) < 0) {
+ giterr_set(GITERR_OS, "Unable to acquire index lock");
+ error = -1;
+ } else {
+ git_vector_swap(&entries, &index->entries);
+ entries_map = git__swap(index->entries_map, entries_map);
+ git_mutex_unlock(&index->lock);
+ }
+
+cleanup:
git_vector_free(&entries);
+ git_idxmap_free(entries_map);
if (error < 0)
return error;
@@ -2696,7 +2894,7 @@ int git_index_read_index(
if (diff < 0) {
git_vector_insert(&remove_entries, (git_index_entry *)old_entry);
} else if (diff > 0) {
- if ((error = index_entry_dup(&entry, git_index_owner(index), new_entry)) < 0)
+ if ((error = index_entry_dup(&entry, index, new_entry)) < 0)
goto done;
git_vector_insert(&new_entries, entry);
@@ -2707,7 +2905,7 @@ int git_index_read_index(
if (git_oid_equal(&old_entry->id, &new_entry->id)) {
git_vector_insert(&new_entries, (git_index_entry *)old_entry);
} else {
- if ((error = index_entry_dup(&entry, git_index_owner(index), new_entry)) < 0)
+ if ((error = index_entry_dup(&entry, index, new_entry)) < 0)
goto done;
git_vector_insert(&new_entries, entry);
diff --git a/src/index.h b/src/index.h
index 9c60b015c..546e677be 100644
--- a/src/index.h
+++ b/src/index.h
@@ -10,6 +10,7 @@
#include "fileops.h"
#include "filebuf.h"
#include "vector.h"
+#include "idxmap.h"
#include "tree-cache.h"
#include "git2/odb.h"
#include "git2/index.h"
@@ -25,6 +26,7 @@ struct git_index {
git_oid checksum; /* checksum at the end of the file */
git_vector entries;
+ git_idxmap *entries_map;
git_mutex lock; /* lock held while entries is being changed */
git_vector deleted; /* deleted entries if readers > 0 */
diff --git a/src/iterator.c b/src/iterator.c
index e35c8dc85..e3a2abf66 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -640,8 +640,53 @@ static int tree_iterator__current_internal(
return 0;
}
-static int tree_iterator__advance(
- const git_index_entry **out, git_iterator *self);
+static int tree_iterator__advance_into_internal(git_iterator *self)
+{
+ int error = 0;
+ tree_iterator *ti = (tree_iterator *)self;
+
+ if (tree_iterator__at_tree(ti))
+ error = tree_iterator__push_frame(ti);
+
+ return error;
+}
+
+static int tree_iterator__advance_internal(git_iterator *self)
+{
+ int error;
+ tree_iterator *ti = (tree_iterator *)self;
+ tree_iterator_frame *tf = ti->head;
+
+ if (tf->current >= tf->n_entries)
+ return GIT_ITEROVER;
+
+ if (!iterator__has_been_accessed(ti))
+ return 0;
+
+ if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
+ tree_iterator__at_tree(ti))
+ return tree_iterator__advance_into_internal(self);
+
+ if (ti->path_has_filename) {
+ git_buf_rtruncate_at_char(&ti->path, '/');
+ ti->path_has_filename = ti->entry_is_current = false;
+ }
+
+ /* scan forward and up, advancing in frame or popping frame when done */
+ while (!tree_iterator__move_to_next(ti, tf) &&
+ tree_iterator__pop_frame(ti, false))
+ tf = ti->head;
+
+ /* find next and load trees */
+ if ((error = tree_iterator__set_next(ti, tf)) < 0)
+ return error;
+
+ /* deal with include_trees / auto_expand as needed */
+ if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
+ return tree_iterator__advance_into_internal(self);
+
+ return 0;
+}
static int tree_iterator__current(
const git_index_entry **out, git_iterator *self)
@@ -659,7 +704,7 @@ static int tree_iterator__current(
self, entry->path, strlen(entry->path));
if (m != ITERATOR_PATHLIST_MATCH) {
- if ((error = tree_iterator__advance(&entry, self)) < 0)
+ if ((error = tree_iterator__advance_internal(self)) < 0)
return error;
entry = NULL;
@@ -673,60 +718,29 @@ static int tree_iterator__current(
return error;
}
-static int tree_iterator__advance_into(
+static int tree_iterator__advance(
const git_index_entry **entry, git_iterator *self)
{
- int error = 0;
- tree_iterator *ti = (tree_iterator *)self;
+ int error = tree_iterator__advance_internal(self);
iterator__clear_entry(entry);
- if (tree_iterator__at_tree(ti))
- error = tree_iterator__push_frame(ti);
-
- if (!error && entry)
- error = tree_iterator__current(entry, self);
+ if (error < 0)
+ return error;
- return error;
+ return tree_iterator__current(entry, self);
}
-static int tree_iterator__advance(
+static int tree_iterator__advance_into(
const git_index_entry **entry, git_iterator *self)
{
- int error;
- tree_iterator *ti = (tree_iterator *)self;
- tree_iterator_frame *tf = ti->head;
+ int error = tree_iterator__advance_into_internal(self);
iterator__clear_entry(entry);
- if (tf->current >= tf->n_entries)
- return GIT_ITEROVER;
-
- if (!iterator__has_been_accessed(ti))
- return tree_iterator__current(entry, self);
-
- if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
- tree_iterator__at_tree(ti))
- return tree_iterator__advance_into(entry, self);
-
- if (ti->path_has_filename) {
- git_buf_rtruncate_at_char(&ti->path, '/');
- ti->path_has_filename = ti->entry_is_current = false;
- }
-
- /* scan forward and up, advancing in frame or popping frame when done */
- while (!tree_iterator__move_to_next(ti, tf) &&
- tree_iterator__pop_frame(ti, false))
- tf = ti->head;
-
- /* find next and load trees */
- if ((error = tree_iterator__set_next(ti, tf)) < 0)
+ if (error < 0)
return error;
- /* deal with include_trees / auto_expand as needed */
- if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
- return tree_iterator__advance_into(entry, self);
-
return tree_iterator__current(entry, self);
}
@@ -1418,19 +1432,14 @@ static int fs_iterator__advance_into(
return error;
}
-static int fs_iterator__advance_over(
- const git_index_entry **entry, git_iterator *self)
+static void fs_iterator__advance_over_internal(git_iterator *self)
{
- int error = 0;
fs_iterator *fi = (fs_iterator *)self;
fs_iterator_frame *ff;
fs_iterator_path_with_stat *next;
- if (entry != NULL)
- *entry = NULL;
-
while (fi->entry.path != NULL) {
- ff = fi->stack;
+ ff = fi->stack;
next = git_vector_get(&ff->entries, ++ff->index);
if (next != NULL)
@@ -1438,8 +1447,19 @@ static int fs_iterator__advance_over(
fs_iterator__pop_frame(fi, ff, false);
}
+}
- error = fs_iterator__update_entry(fi);
+static int fs_iterator__advance_over(
+ const git_index_entry **entry, git_iterator *self)
+{
+ int error;
+
+ if (entry != NULL)
+ *entry = NULL;
+
+ fs_iterator__advance_over_internal(self);
+
+ error = fs_iterator__update_entry((fs_iterator *)self);
if (!error && entry != NULL)
error = fs_iterator__current(entry, self);
@@ -1516,41 +1536,50 @@ static int fs_iterator__update_entry(fs_iterator *fi)
{
fs_iterator_path_with_stat *ps;
- memset(&fi->entry, 0, sizeof(fi->entry));
+ while (true) {
+ memset(&fi->entry, 0, sizeof(fi->entry));
- if (!fi->stack)
- return GIT_ITEROVER;
+ if (!fi->stack)
+ return GIT_ITEROVER;
- ps = git_vector_get(&fi->stack->entries, fi->stack->index);
- if (!ps)
- return GIT_ITEROVER;
+ ps = git_vector_get(&fi->stack->entries, fi->stack->index);
+ if (!ps)
+ return GIT_ITEROVER;
- git_buf_truncate(&fi->path, fi->root_len);
- if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
- return -1;
+ git_buf_truncate(&fi->path, fi->root_len);
+ if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
+ return -1;
- if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
- return GIT_ITEROVER;
+ if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
+ return GIT_ITEROVER;
- fi->entry.path = ps->path;
- fi->pathlist_match = ps->pathlist_match;
- git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
+ fi->entry.path = ps->path;
+ fi->pathlist_match = ps->pathlist_match;
+ git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
- /* need different mode here to keep directories during iteration */
- fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
+ /* need different mode here to keep directories during iteration */
+ fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
- /* allow wrapper to check/update the entry (can force skip) */
- if (fi->update_entry_cb &&
- fi->update_entry_cb(fi) == GIT_ENOTFOUND)
- return fs_iterator__advance_over(NULL, (git_iterator *)fi);
+ /* allow wrapper to check/update the entry (can force skip) */
+ if (fi->update_entry_cb &&
+ fi->update_entry_cb(fi) == GIT_ENOTFOUND) {
+ fs_iterator__advance_over_internal(&fi->base);
+ continue;
+ }
- /* if this is a tree and trees aren't included, then skip */
- if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
- int error = fs_iterator__advance_into(NULL, (git_iterator *)fi);
- if (error != GIT_ENOTFOUND)
- return error;
- giterr_clear();
- return fs_iterator__advance_over(NULL, (git_iterator *)fi);
+ /* if this is a tree and trees aren't included, then skip */
+ if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
+ int error = fs_iterator__advance_into(NULL, &fi->base);
+
+ if (error != GIT_ENOTFOUND)
+ return error;
+
+ giterr_clear();
+ fs_iterator__advance_over_internal(&fi->base);
+ continue;
+ }
+
+ break;
}
return 0;
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 99b8f7c91..730c4b1e1 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -84,9 +84,9 @@ static int object_file_name(
static int object_mkdir(const git_buf *name, const loose_backend *be)
{
- return git_futils_mkdir(
+ return git_futils_mkdir_relative(
name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode,
- GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
+ GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL);
}
static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
diff --git a/src/odb_mempack.c b/src/odb_mempack.c
index 34355270f..594a2784c 100644
--- a/src/odb_mempack.c
+++ b/src/odb_mempack.c
@@ -154,12 +154,16 @@ void git_mempack_reset(git_odb_backend *_backend)
});
git_array_clear(db->commits);
+
+ git_oidmap_clear(db->objects);
}
static void impl__free(git_odb_backend *_backend)
{
- git_mempack_reset(_backend);
- git__free(_backend);
+ struct memory_packer_db *db = (struct memory_packer_db *)_backend;
+
+ git_oidmap_free(db->objects);
+ git__free(db);
}
int git_mempack_new(git_odb_backend **out)
diff --git a/src/oidmap.h b/src/oidmap.h
index d2c451e7f..2cf208f53 100644
--- a/src/oidmap.h
+++ b/src/oidmap.h
@@ -49,4 +49,6 @@ GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid)
#define git_oidmap_size(h) kh_size(h)
+#define git_oidmap_clear(h) kh_clear(oid, h)
+
#endif
diff --git a/src/path.c b/src/path.c
index 9ce5d2978..72cb289e0 100644
--- a/src/path.c
+++ b/src/path.c
@@ -526,6 +526,17 @@ bool git_path_isfile(const char *path)
return S_ISREG(st.st_mode) != 0;
}
+bool git_path_islink(const char *path)
+{
+ struct stat st;
+
+ assert(path);
+ if (p_lstat(path, &st) < 0)
+ return false;
+
+ return S_ISLNK(st.st_mode) != 0;
+}
+
#ifdef GIT_WIN32
bool git_path_is_empty_dir(const char *path)
@@ -1166,7 +1177,11 @@ static int diriter_update_paths(git_path_diriter *diriter)
diriter->path[path_len-1] = L'\0';
git_buf_truncate(&diriter->path_utf8, diriter->parent_utf8_len);
- git_buf_putc(&diriter->path_utf8, '/');
+
+ if (diriter->parent_utf8_len > 0 &&
+ diriter->path_utf8.ptr[diriter->parent_utf8_len-1] != '/')
+ git_buf_putc(&diriter->path_utf8, '/');
+
git_buf_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len);
if (git_buf_oom(&diriter->path_utf8))
@@ -1315,7 +1330,11 @@ int git_path_diriter_next(git_path_diriter *diriter)
#endif
git_buf_truncate(&diriter->path, diriter->parent_len);
- git_buf_putc(&diriter->path, '/');
+
+ if (diriter->parent_len > 0 &&
+ diriter->path.ptr[diriter->parent_len-1] != '/')
+ git_buf_putc(&diriter->path, '/');
+
git_buf_put(&diriter->path, filename, filename_len);
if (git_buf_oom(&diriter->path))
diff --git a/src/path.h b/src/path.h
index 971603ea7..7e156fce8 100644
--- a/src/path.h
+++ b/src/path.h
@@ -72,7 +72,7 @@ extern const char *git_path_topdir(const char *path);
* This will return a number >= 0 which is the offset to the start of the
* path, if the path is rooted (i.e. "/rooted/path" returns 0 and
* "c:/windows/rooted/path" returns 2). If the path is not rooted, this
- * returns < 0.
+ * returns -1.
*/
extern int git_path_root(const char *path);
@@ -169,6 +169,12 @@ extern bool git_path_isdir(const char *path);
extern bool git_path_isfile(const char *path);
/**
+ * Check if the given path points to a symbolic link.
+ * @return true or false
+ */
+extern bool git_path_islink(const char *path);
+
+/**
* Check if the given path is a directory, and is empty.
*/
extern bool git_path_is_empty_dir(const char *path);
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 1ddce4649..921f7862b 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -1413,7 +1413,8 @@ static int setup_namespace(git_buf *path, git_repository *repo)
git__free(parts);
/* Make sure that the folder with the namespace exists */
- if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0)
+ if (git_futils_mkdir_relative(git_buf_cstr(path), repo->path_repository,
+ 0777, GIT_MKDIR_PATH, NULL) < 0)
return -1;
/* Return root of the namespaced path, i.e. without the trailing '/refs' */
diff --git a/src/repository.c b/src/repository.c
index 0f37cfbfe..77145cfc8 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -908,12 +908,28 @@ bool git_repository__reserved_names(
buf->size = git_repository__reserved_names_win32[i].size;
}
- /* Try to add any repo-specific reserved names */
+ /* Try to add any repo-specific reserved names - the gitlink file
+ * within a submodule or the repository (if the repository directory
+ * is beneath the workdir). These are typically `.git`, but should
+ * be protected in case they are not. Note, repo and workdir paths
+ * are always prettified to end in `/`, so a prefixcmp is safe.
+ */
if (!repo->is_bare) {
- const char *reserved_path = repo->path_gitlink ?
- repo->path_gitlink : repo->path_repository;
+ int (*prefixcmp)(const char *, const char *);
+ int error, ignorecase;
- if (reserved_names_add8dot3(repo, reserved_path) < 0)
+ error = git_repository__cvar(
+ &ignorecase, repo, GIT_CVAR_IGNORECASE);
+ prefixcmp = (error || ignorecase) ? git__prefixcmp_icase :
+ git__prefixcmp;
+
+ if (repo->path_gitlink &&
+ reserved_names_add8dot3(repo, repo->path_gitlink) < 0)
+ goto on_error;
+
+ if (repo->path_repository &&
+ prefixcmp(repo->path_repository, repo->workdir) == 0 &&
+ reserved_names_add8dot3(repo, repo->path_repository) < 0)
goto on_error;
}
}
@@ -1441,8 +1457,8 @@ static int repo_init_structure(
if (chmod)
mkdir_flags |= GIT_MKDIR_CHMOD;
- error = git_futils_mkdir(
- tpl->path, repo_dir, dmode, mkdir_flags);
+ error = git_futils_mkdir_relative(
+ tpl->path, repo_dir, dmode, mkdir_flags, NULL);
}
else if (!external_tpl) {
const char *content = tpl->content;
@@ -1464,7 +1480,7 @@ static int mkdir_parent(git_buf *buf, uint32_t mode, bool skip2)
* don't try to set gid or grant world write access
*/
return git_futils_mkdir(
- buf->ptr, NULL, mode & ~(S_ISGID | 0002),
+ buf->ptr, mode & ~(S_ISGID | 0002),
GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR |
(skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST));
}
@@ -1568,14 +1584,14 @@ static int repo_init_directories(
/* create path #4 */
if (wd_path->size > 0 &&
(error = git_futils_mkdir(
- wd_path->ptr, NULL, dirmode & ~S_ISGID,
+ wd_path->ptr, dirmode & ~S_ISGID,
GIT_MKDIR_VERIFY_DIR)) < 0)
return error;
/* create path #2 (if not the same as #4) */
if (!natural_wd &&
(error = git_futils_mkdir(
- repo_path->ptr, NULL, dirmode & ~S_ISGID,
+ repo_path->ptr, dirmode & ~S_ISGID,
GIT_MKDIR_VERIFY_DIR | GIT_MKDIR_SKIP_LAST)) < 0)
return error;
}
@@ -1585,7 +1601,7 @@ static int repo_init_directories(
has_dotgit)
{
/* create path #1 */
- error = git_futils_mkdir(repo_path->ptr, NULL, dirmode,
+ error = git_futils_mkdir(repo_path->ptr, dirmode,
GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0));
}
diff --git a/src/submodule.c b/src/submodule.c
index 7f52c3616..3fd338843 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -89,9 +89,11 @@ __KHASH_IMPL(
static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name);
static git_config_backend *open_gitmodules(git_repository *repo, int gitmod);
+static git_config *gitmodules_snapshot(git_repository *repo);
static int get_url_base(git_buf *url, git_repository *repo);
static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo);
-static int submodule_load_from_config(const git_config_entry *, void *);
+static int submodule_load_each(const git_config_entry *entry, void *payload);
+static int submodule_read_config(git_submodule *sm, git_config *cfg);
static int submodule_load_from_wd_lite(git_submodule *);
static void submodule_get_index_status(unsigned int *, git_submodule *);
static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t);
@@ -144,6 +146,43 @@ static int find_by_path(const git_config_entry *entry, void *payload)
return 0;
}
+/**
+ * Find out the name of a submodule from its path
+ */
+static int name_from_path(git_buf *out, git_config *cfg, const char *path)
+{
+ const char *key = "submodule\\..*\\.path";
+ git_config_iterator *iter;
+ git_config_entry *entry;
+ int error;
+
+ if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0)
+ return error;
+
+ while ((error = git_config_next(&entry, iter)) == 0) {
+ const char *fdot, *ldot;
+ /* TODO: this should maybe be strcasecmp on a case-insensitive fs */
+ if (strcmp(path, entry->value) != 0)
+ continue;
+
+ fdot = strchr(entry->name, '.');
+ ldot = strrchr(entry->name, '.');
+
+ git_buf_clear(out);
+ git_buf_put(out, fdot + 1, ldot - fdot - 1);
+ goto cleanup;
+ }
+
+ if (error == GIT_ITEROVER) {
+ giterr_set(GITERR_SUBMODULE, "could not find a submodule name for '%s'", path);
+ error = GIT_ENOTFOUND;
+ }
+
+cleanup:
+ git_config_iterator_free(iter);
+ return error;
+}
+
int git_submodule_lookup(
git_submodule **out, /* NULL if user only wants to test existence */
git_repository *repo,
@@ -190,6 +229,7 @@ int git_submodule_lookup(
if (error < 0) {
git_submodule_free(sm);
+ git_buf_free(&path);
return error;
}
@@ -280,11 +320,12 @@ done:
return 0;
}
-static int submodules_from_index(git_strmap *map, git_index *idx)
+static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg)
{
int error;
git_iterator *i;
const git_index_entry *entry;
+ git_buf name = GIT_BUF_INIT;
if ((error = git_iterator_for_index(&i, idx, NULL)) < 0)
return error;
@@ -293,6 +334,11 @@ static int submodules_from_index(git_strmap *map, git_index *idx)
khiter_t pos = git_strmap_lookup_index(map, entry->path);
git_submodule *sm;
+ git_buf_clear(&name);
+ if (!name_from_path(&name, cfg, entry->path)) {
+ git_strmap_lookup_index(map, name.ptr);
+ }
+
if (git_strmap_valid_index(map, pos)) {
sm = git_strmap_value_at(map, pos);
@@ -301,7 +347,7 @@ static int submodules_from_index(git_strmap *map, git_index *idx)
else
sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
} else if (S_ISGITLINK(entry->mode)) {
- if (!submodule_get_or_create(&sm, git_index_owner(idx), map, entry->path)) {
+ if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name.ptr ? name.ptr : entry->path)) {
submodule_update_from_index_entry(sm, entry);
git_submodule_free(sm);
}
@@ -311,16 +357,18 @@ static int submodules_from_index(git_strmap *map, git_index *idx)
if (error == GIT_ITEROVER)
error = 0;
+ git_buf_free(&name);
git_iterator_free(i);
return error;
}
-static int submodules_from_head(git_strmap *map, git_tree *head)
+static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg)
{
int error;
git_iterator *i;
const git_index_entry *entry;
+ git_buf name = GIT_BUF_INIT;
if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
return error;
@@ -329,6 +377,11 @@ static int submodules_from_head(git_strmap *map, git_tree *head)
khiter_t pos = git_strmap_lookup_index(map, entry->path);
git_submodule *sm;
+ git_buf_clear(&name);
+ if (!name_from_path(&name, cfg, entry->path)) {
+ git_strmap_lookup_index(map, name.ptr);
+ }
+
if (git_strmap_valid_index(map, pos)) {
sm = git_strmap_value_at(map, pos);
@@ -337,7 +390,7 @@ static int submodules_from_head(git_strmap *map, git_tree *head)
else
sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
} else if (S_ISGITLINK(entry->mode)) {
- if (!submodule_get_or_create(&sm, git_tree_owner(head), map, entry->path)) {
+ if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name.ptr ? name.ptr : entry->path)) {
submodule_update_from_head_data(
sm, entry->mode, &entry->id);
git_submodule_free(sm);
@@ -348,6 +401,7 @@ static int submodules_from_head(git_strmap *map, git_tree *head)
if (error == GIT_ITEROVER)
error = 0;
+ git_buf_free(&name);
git_iterator_free(i);
return error;
@@ -355,8 +409,7 @@ static int submodules_from_head(git_strmap *map, git_tree *head)
/* If have_sm is true, sm is populated, otherwise map an repo are. */
typedef struct {
- int have_sm;
- git_submodule *sm;
+ git_config *mods;
git_strmap *map;
git_repository *repo;
} lfc_data;
@@ -369,7 +422,7 @@ static int all_submodules(git_repository *repo, git_strmap *map)
const char *wd = NULL;
git_buf path = GIT_BUF_INIT;
git_submodule *sm;
- git_config_backend *mods = NULL;
+ git_config *mods = NULL;
uint32_t mask;
assert(repo && map);
@@ -400,24 +453,28 @@ static int all_submodules(git_repository *repo, git_strmap *map)
GIT_SUBMODULE_STATUS__WD_FLAGS |
GIT_SUBMODULE_STATUS__WD_OID_VALID;
+ /* add submodule information from .gitmodules */
+ if (wd) {
+ lfc_data data = { 0 };
+ data.map = map;
+ data.repo = repo;
+
+ if ((mods = gitmodules_snapshot(repo)) == NULL)
+ goto cleanup;
+
+ data.mods = mods;
+ if ((error = git_config_foreach(
+ mods, submodule_load_each, &data)) < 0)
+ goto cleanup;
+ }
/* add back submodule information from index */
if (idx) {
- if ((error = submodules_from_index(map, idx)) < 0)
+ if ((error = submodules_from_index(map, idx, mods)) < 0)
goto cleanup;
}
/* add submodule information from HEAD */
if (head) {
- if ((error = submodules_from_head(map, head)) < 0)
- goto cleanup;
- }
- /* add submodule information from .gitmodules */
- if (wd) {
- lfc_data data = { 0 };
- data.map = map;
- data.repo = repo;
- if ((mods = open_gitmodules(repo, false)) != NULL &&
- (error = git_config_file_foreach(
- mods, submodule_load_from_config, &data)) < 0)
+ if ((error = submodules_from_head(map, head, mods)) < 0)
goto cleanup;
}
/* shallow scan submodules in work tree as needed */
@@ -428,7 +485,7 @@ static int all_submodules(git_repository *repo, git_strmap *map)
}
cleanup:
- git_config_file_free(mods);
+ git_config_free(mods);
/* TODO: if we got an error, mark submodule config as invalid? */
git_index_free(idx);
git_tree_free(head);
@@ -1370,8 +1427,7 @@ static int submodule_update_head(git_submodule *submodule)
int git_submodule_reload(git_submodule *sm, int force)
{
int error = 0;
- git_config_backend *mods;
- lfc_data data = { 0 };
+ git_config *mods;
GIT_UNUSED(force);
@@ -1390,28 +1446,14 @@ int git_submodule_reload(git_submodule *sm, int force)
return error;
/* refresh config data */
- mods = open_gitmodules(sm->repo, GITMODULES_EXISTING);
+ mods = gitmodules_snapshot(sm->repo);
if (mods != NULL) {
- git_buf path = GIT_BUF_INIT;
-
- git_buf_sets(&path, "submodule\\.");
- git_buf_text_puts_escape_regex(&path, sm->name);
- git_buf_puts(&path, "\\..*");
-
- if (git_buf_oom(&path)) {
- error = -1;
- } else {
- data.have_sm = 1;
- data.sm = sm;
- error = git_config_file_foreach_match(
- mods, path.ptr, submodule_load_from_config, &data);
- }
-
- git_buf_free(&path);
- git_config_file_free(mods);
+ error = submodule_read_config(sm, mods);
+ git_config_free(mods);
- if (error < 0)
+ if (error < 0) {
return error;
+ }
}
/* refresh wd data */
@@ -1627,134 +1669,149 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value)
return 0;
}
-static int submodule_load_from_config(
- const git_config_entry *entry, void *payload)
+static int get_value(const char **out, git_config *cfg, git_buf *buf, const char *name, const char *field)
{
- const char *namestart, *property;
- const char *key = entry->name, *value = entry->value, *path;
- char *alternate = NULL, *replaced = NULL;
- git_buf name = GIT_BUF_INIT;
- lfc_data *data = payload;
- git_submodule *sm;
- int error = 0;
-
- if (git__prefixcmp(key, "submodule.") != 0)
- return 0;
-
- namestart = key + strlen("submodule.");
- property = strrchr(namestart, '.');
-
- if (!property || (property == namestart))
- return 0;
-
- property++;
- path = !strcasecmp(property, "path") ? value : NULL;
+ int error;
- if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0)
- goto done;
+ git_buf_clear(buf);
- if (data->have_sm) {
- sm = data->sm;
- } else {
- khiter_t pos;
- git_strmap *map = data->map;
- pos = git_strmap_lookup_index(map, path ? path : name.ptr);
- if (git_strmap_valid_index(map, pos)) {
- sm = git_strmap_value_at(map, pos);
- } else {
- if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0)
- goto done;
+ if ((error = git_buf_printf(buf, "submodule.%s.%s", name, field)) < 0 ||
+ (error = git_config_get_string(out, cfg, buf->ptr)) < 0)
+ return error;
- git_strmap_insert(map, sm->name, sm, error);
- assert(error != 0);
- if (error < 0)
- goto done;
- error = 0;
- }
- }
+ return error;
+}
- sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
+static int submodule_read_config(git_submodule *sm, git_config *cfg)
+{
+ git_buf key = GIT_BUF_INIT;
+ const char *value;
+ int error, in_config = 0;
- /* Only from config might we get differing names & paths. If so, then
- * update the submodule and insert under the alternative key.
+ /*
+ * TODO: Look up path in index and if it is present but not a GITLINK
+ * then this should be deleted (at least to match git's behavior)
*/
- /* TODO: if case insensitive filesystem, then the following strcmps
+ if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) {
+ in_config = 1;
+ /*
+ * TODO: if case insensitive filesystem, then the following strcmp
* should be strcasecmp
*/
-
- if (strcmp(sm->name, name.ptr) != 0) { /* name changed */
- if (sm->path && !strcmp(sm->path, name.ptr)) { /* already set as path */
- replaced = sm->name;
- sm->name = sm->path;
- } else {
- if (sm->name != sm->path)
- replaced = sm->name;
- alternate = sm->name = git_buf_detach(&name);
- }
- }
- else if (path && strcmp(path, sm->path) != 0) { /* path changed */
- if (!strcmp(sm->name, value)) { /* already set as name */
- replaced = sm->path;
- sm->path = sm->name;
- } else {
+ if (strcmp(sm->name, value) != 0) {
if (sm->path != sm->name)
- replaced = sm->path;
- if ((alternate = git__strdup(value)) == NULL) {
- error = -1;
- goto done;
- }
- sm->path = alternate;
+ git__free(sm->path);
+ sm->path = git__strdup(value);
+ GITERR_CHECK_ALLOC(sm->path);
}
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
}
- /* Deregister under name being replaced */
- if (replaced) {
- git__free(replaced);
+ if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) {
+ in_config = 1;
+ sm->url = git__strdup(value);
+ GITERR_CHECK_ALLOC(sm->url);
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
}
- /* TODO: Look up path in index and if it is present but not a GITLINK
- * then this should be deleted (at least to match git's behavior)
- */
-
- if (path)
- goto done;
-
- /* copy other properties into submodule entry */
- if (strcasecmp(property, "url") == 0) {
- git__free(sm->url);
- sm->url = NULL;
-
- if (value != NULL && (sm->url = git__strdup(value)) == NULL) {
- error = -1;
- goto done;
- }
+ if ((error = get_value(&value, cfg, &key, sm->name, "branch")) == 0) {
+ in_config = 1;
+ sm->branch = git__strdup(value);
+ GITERR_CHECK_ALLOC(sm->branch);
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
}
- else if (strcasecmp(property, "branch") == 0) {
- git__free(sm->branch);
- sm->branch = NULL;
- if (value != NULL && (sm->branch = git__strdup(value)) == NULL) {
- error = -1;
- goto done;
- }
- }
- else if (strcasecmp(property, "update") == 0) {
+ if ((error = get_value(&value, cfg, &key, sm->name, "update")) == 0) {
+ in_config = 1;
if ((error = git_submodule_parse_update(&sm->update, value)) < 0)
- goto done;
+ goto cleanup;
sm->update_default = sm->update;
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
}
- else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) {
+
+ if ((error = get_value(&value, cfg, &key, sm->name, "fetchRecurseSubmodules")) == 0) {
+ in_config = 1;
if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0)
- goto done;
+ goto cleanup;
sm->fetch_recurse_default = sm->fetch_recurse;
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
}
- else if (strcasecmp(property, "ignore") == 0) {
+
+ if ((error = get_value(&value, cfg, &key, sm->name, "ignore")) == 0) {
+ in_config = 1;
if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0)
- goto done;
+ goto cleanup;
sm->ignore_default = sm->ignore;
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
}
- /* ignore other unknown submodule properties */
+
+ if (in_config)
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
+
+ error = 0;
+
+cleanup:
+ git_buf_free(&key);
+ return error;
+}
+
+static int submodule_load_each(const git_config_entry *entry, void *payload)
+{
+ lfc_data *data = payload;
+ const char *namestart, *property;
+ git_strmap_iter pos;
+ git_strmap *map = data->map;
+ git_buf name = GIT_BUF_INIT;
+ git_submodule *sm;
+ int error;
+
+ if (git__prefixcmp(entry->name, "submodule.") != 0)
+ return 0;
+
+ namestart = entry->name + strlen("submodule.");
+ property = strrchr(namestart, '.');
+
+ if (!property || (property == namestart))
+ return 0;
+
+ property++;
+
+ if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0)
+ return error;
+
+ /*
+ * Now that we have the submodule's name, we can use that to
+ * figure out whether it's in the map. If it's not, we create
+ * a new submodule, load the config and insert it. If it's
+ * already inserted, we've already loaded it, so we skip.
+ */
+ pos = git_strmap_lookup_index(map, name.ptr);
+ if (git_strmap_valid_index(map, pos)) {
+ error = 0;
+ goto done;
+ }
+
+ if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0)
+ goto done;
+
+ if ((error = submodule_read_config(sm, data->mods)) < 0) {
+ git_submodule_free(sm);
+ goto done;
+ }
+
+ git_strmap_insert(map, sm->name, sm, error);
+ assert(error != 0);
+ if (error < 0)
+ goto done;
+
+ error = 0;
done:
git_buf_free(&name);
@@ -1778,6 +1835,35 @@ static int submodule_load_from_wd_lite(git_submodule *sm)
return 0;
}
+/**
+ * Returns a snapshot of $WORK_TREE/.gitmodules.
+ *
+ * We ignore any errors and just pretend the file isn't there.
+ */
+static git_config *gitmodules_snapshot(git_repository *repo)
+{
+ const char *workdir = git_repository_workdir(repo);
+ git_config *mods = NULL, *snap = NULL;
+ git_buf path = GIT_BUF_INIT;
+
+ if (workdir != NULL) {
+ if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
+ return NULL;
+
+ if (git_config_open_ondisk(&mods, path.ptr) < 0)
+ mods = NULL;
+ }
+
+ git_buf_free(&path);
+
+ if (mods) {
+ git_config_snapshot(&snap, mods);
+ git_config_free(mods);
+ }
+
+ return snap;
+}
+
static git_config_backend *open_gitmodules(
git_repository *repo,
int okay_to_create)
diff --git a/src/transaction.c b/src/transaction.c
index e9639bf97..92e134e5b 100644
--- a/src/transaction.c
+++ b/src/transaction.c
@@ -331,7 +331,6 @@ int git_transaction_commit(git_transaction *tx)
if (tx->type == TRANSACTION_CONFIG) {
error = git_config_unlock(tx->cfg, true);
- git_config_free(tx->cfg);
tx->cfg = NULL;
return error;
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 8f5a7164b..ffa4a24a7 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -756,8 +756,10 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use
list = libssh2_userauth_list(session, username, strlen(username));
/* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
- if (list == NULL && !libssh2_userauth_authenticated(session))
+ if (list == NULL && !libssh2_userauth_authenticated(session)) {
+ ssh_error(session, "Failed to retrieve list of SSH authentication methods");
return -1;
+ }
ptr = list;
while (ptr) {
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c
index 118e8bcc5..40b95c33b 100644
--- a/src/win32/path_w32.c
+++ b/src/win32/path_w32.c
@@ -198,13 +198,13 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
/* See if this is an absolute path (beginning with a drive letter) */
if (path__is_absolute(src)) {
if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
- return -1;
+ goto on_error;
}
/* File-prefixed NT-style paths beginning with \\?\ */
else if (path__is_nt_namespace(src)) {
/* Skip the NT prefix, the destination already contains it */
if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0)
- return -1;
+ goto on_error;
}
/* UNC paths */
else if (path__is_unc(src)) {
@@ -213,36 +213,43 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
/* Skip the leading "\\" */
if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0)
- return -1;
+ goto on_error;
}
/* Absolute paths omitting the drive letter */
else if (src[0] == '\\' || src[0] == '/') {
if (path__cwd(dest, MAX_PATH) < 0)
- return -1;
+ goto on_error;
if (!path__is_absolute(dest)) {
errno = ENOENT;
- return -1;
+ goto on_error;
}
/* Skip the drive letter specification ("C:") */
if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0)
- return -1;
+ goto on_error;
}
/* Relative paths */
else {
int cwd_len;
if ((cwd_len = git_win32_path__cwd(dest, MAX_PATH)) < 0)
- return -1;
+ goto on_error;
dest[cwd_len++] = L'\\';
if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0)
- return -1;
+ goto on_error;
}
return git_win32_path_canonicalize(out);
+
+on_error:
+ /* set windows error code so we can use its error message */
+ if (errno == ENAMETOOLONG)
+ SetLastError(ERROR_FILENAME_EXCED_RANGE);
+
+ return -1;
}
int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index c909af6cc..414cb4701 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -148,12 +148,19 @@ static int lstat_w(
return git_win32__file_attribute_to_stat(buf, &fdata, path);
}
- errno = ENOENT;
+ switch (GetLastError()) {
+ case ERROR_ACCESS_DENIED:
+ errno = EACCES;
+ break;
+ default:
+ errno = ENOENT;
+ break;
+ }
/* To match POSIX behavior, set ENOTDIR when any of the folders in the
* file path is a regular file, otherwise set ENOENT.
*/
- if (posix_enotdir) {
+ if (errno == ENOENT && posix_enotdir) {
size_t path_len = wcslen(path);
/* scan up path until we find an existing item */
diff --git a/tests/checkout/index.c b/tests/checkout/index.c
index 0d220e141..9fa901867 100644
--- a/tests/checkout/index.c
+++ b/tests/checkout/index.c
@@ -63,7 +63,7 @@ void test_checkout_index__can_remove_untracked_files(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
- git_futils_mkdir("./testrepo/dir/subdir/subsubdir", NULL, 0755, GIT_MKDIR_PATH);
+ git_futils_mkdir("./testrepo/dir/subdir/subsubdir", 0755, GIT_MKDIR_PATH);
cl_git_mkfile("./testrepo/dir/one", "one\n");
cl_git_mkfile("./testrepo/dir/subdir/two", "two\n");
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index be4019822..5680b86df 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -946,7 +946,7 @@ void test_checkout_tree__filemode_preserved_in_index(void)
cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0));
- cl_assert_equal_i(0100755, entry->mode);
+ cl_assert(GIT_PERMS_IS_EXEC(entry->mode));
git_commit_free(commit);
@@ -957,7 +957,7 @@ void test_checkout_tree__filemode_preserved_in_index(void)
cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
- cl_assert_equal_i(0100644, entry->mode);
+ cl_assert(!GIT_PERMS_IS_EXEC(entry->mode));
git_commit_free(commit);
@@ -968,7 +968,18 @@ void test_checkout_tree__filemode_preserved_in_index(void)
cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
- cl_assert_equal_i(0100755, entry->mode);
+ cl_assert(GIT_PERMS_IS_EXEC(entry->mode));
+
+ git_commit_free(commit);
+
+
+ /* Finally, check out the text file again and check that the exec bit is cleared */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
+ cl_assert(!GIT_PERMS_IS_EXEC(entry->mode));
git_commit_free(commit);
@@ -976,6 +987,73 @@ void test_checkout_tree__filemode_preserved_in_index(void)
git_index_free(index);
}
+mode_t read_filemode(const char *path)
+{
+ git_buf fullpath = GIT_BUF_INIT;
+ struct stat st;
+ mode_t result;
+
+ git_buf_joinpath(&fullpath, "testrepo", path);
+ cl_must_pass(p_stat(fullpath.ptr, &st));
+
+ result = GIT_PERMS_IS_EXEC(st.st_mode) ?
+ GIT_FILEMODE_BLOB_EXECUTABLE : GIT_FILEMODE_BLOB;
+
+ git_buf_free(&fullpath);
+
+ return result;
+}
+
+void test_checkout_tree__filemode_preserved_in_workdir(void)
+{
+#ifndef GIT_WIN32
+ git_oid executable_oid;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ /* test a freshly added executable */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(GIT_PERMS_IS_EXEC(read_filemode("executable.txt")));
+
+ git_commit_free(commit);
+
+
+ /* Now start with a commit which has a text file */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+ git_commit_free(commit);
+
+
+ /* And then check out to a commit which converts the text file to an executable */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+ git_commit_free(commit);
+
+
+ /* Finally, check out the text file again and check that the exec bit is cleared */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+ git_commit_free(commit);
+#endif
+}
+
void test_checkout_tree__removes_conflicts(void)
{
git_oid commit_id;
diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c
index cc687baeb..61442f88b 100644
--- a/tests/clar_libgit2.c
+++ b/tests/clar_libgit2.c
@@ -299,6 +299,8 @@ const char* cl_git_path_url(const char *path)
in_buf++;
}
+ cl_assert(url_buf.size < 4096);
+
strncpy(url, git_buf_cstr(&url_buf), 4096);
git_buf_free(&url_buf);
git_buf_free(&path_buf);
diff --git a/tests/clone/nonetwork.c b/tests/clone/nonetwork.c
index 44a503818..7ebf19f46 100644
--- a/tests/clone/nonetwork.c
+++ b/tests/clone/nonetwork.c
@@ -297,16 +297,19 @@ static void assert_correct_reflog(const char *name)
{
git_reflog *log;
const git_reflog_entry *entry;
- char expected_log_message[128] = {0};
+ git_buf expected_message = GIT_BUF_INIT;
- sprintf(expected_log_message, "clone: from %s", cl_git_fixture_url("testrepo.git"));
+ git_buf_printf(&expected_message,
+ "clone: from %s", cl_git_fixture_url("testrepo.git"));
cl_git_pass(git_reflog_read(&log, g_repo, name));
cl_assert_equal_i(1, git_reflog_entrycount(log));
entry = git_reflog_entry_byindex(log, 0);
- cl_assert_equal_s(expected_log_message, git_reflog_entry_message(entry));
+ cl_assert_equal_s(expected_message.ptr, git_reflog_entry_message(entry));
git_reflog_free(log);
+
+ git_buf_free(&expected_message);
}
void test_clone_nonetwork__clone_updates_reflog_properly(void)
diff --git a/tests/config/global.c b/tests/config/global.c
index 4481308d6..b5e83fec0 100644
--- a/tests/config/global.c
+++ b/tests/config/global.c
@@ -6,17 +6,17 @@ void test_config_global__initialize(void)
{
git_buf path = GIT_BUF_INIT;
- cl_git_pass(git_futils_mkdir_r("home", NULL, 0777));
+ cl_git_pass(git_futils_mkdir_r("home", 0777));
cl_git_pass(git_path_prettify(&path, "home", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
- cl_git_pass(git_futils_mkdir_r("xdg/git", NULL, 0777));
+ cl_git_pass(git_futils_mkdir_r("xdg/git", 0777));
cl_git_pass(git_path_prettify(&path, "xdg/git", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
- cl_git_pass(git_futils_mkdir_r("etc", NULL, 0777));
+ cl_git_pass(git_futils_mkdir_r("etc", 0777));
cl_git_pass(git_path_prettify(&path, "etc", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
diff --git a/tests/config/write.c b/tests/config/write.c
index 3d9b1a16a..e634aa326 100644
--- a/tests/config/write.c
+++ b/tests/config/write.c
@@ -530,6 +530,9 @@ void test_config_write__outside_change(void)
git_config_free(cfg);
}
+#define FOO_COMMENT \
+ "; another comment!\n"
+
#define SECTION_FOO \
"\n" \
" \n" \
@@ -537,7 +540,8 @@ void test_config_write__outside_change(void)
" # here's a comment\n" \
"\tname = \"value\"\n" \
" name2 = \"value2\"\n" \
- "; another comment!\n"
+
+#define SECTION_FOO_WITH_COMMENT SECTION_FOO FOO_COMMENT
#define SECTION_BAR \
"[section \"bar\"]\t\n" \
@@ -553,7 +557,7 @@ void test_config_write__preserves_whitespace_and_comments(void)
git_buf newfile = GIT_BUF_INIT;
/* This config can occur after removing and re-adding the origin remote */
- const char *file_content = SECTION_FOO SECTION_BAR;
+ const char *file_content = SECTION_FOO_WITH_COMMENT SECTION_BAR;
/* Write the test config and make sure the expected entry exists */
cl_git_mkfile(file_name, file_content);
@@ -567,9 +571,10 @@ void test_config_write__preserves_whitespace_and_comments(void)
cl_assert_equal_strn(SECTION_FOO, n, strlen(SECTION_FOO));
n += strlen(SECTION_FOO);
-
cl_assert_equal_strn("\tother = otherval\n", n, strlen("\tother = otherval\n"));
n += strlen("\tother = otherval\n");
+ cl_assert_equal_strn(FOO_COMMENT, n, strlen(FOO_COMMENT));
+ n += strlen(FOO_COMMENT);
cl_assert_equal_strn(SECTION_BAR, n, strlen(SECTION_BAR));
n += strlen(SECTION_BAR);
@@ -670,6 +675,16 @@ void test_config_write__locking(void)
git_transaction_free(tx);
/* Now that we've unlocked it, we should see both updates */
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
+ cl_assert_equal_s("other value", entry->value);
+ git_config_entry_free(entry);
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section2.name3"));
+ cl_assert_equal_s("more value", entry->value);
+ git_config_entry_free(entry);
+
+ git_config_free(cfg);
+
+ /* We should also see the changes after reopening the config */
cl_git_pass(git_config_open_ondisk(&cfg, filename));
cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
cl_assert_equal_s("other value", entry->value);
diff --git a/tests/core/buffer.c b/tests/core/buffer.c
index 0e7026a9c..9872af7f4 100644
--- a/tests/core/buffer.c
+++ b/tests/core/buffer.c
@@ -929,7 +929,7 @@ void test_core_buffer__similarity_metric(void)
cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
- cl_git_pass(git_futils_mkdir("scratch", NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("scratch", 0755, GIT_MKDIR_PATH));
cl_git_mkfile("scratch/testdata", SIMILARITY_TEST_DATA_1);
cl_git_pass(git_hashsig_create_fromfile(
&b, "scratch/testdata", GIT_HASHSIG_NORMAL));
diff --git a/tests/core/copy.c b/tests/core/copy.c
index 04b2dfab5..967748cc5 100644
--- a/tests/core/copy.c
+++ b/tests/core/copy.c
@@ -25,7 +25,7 @@ void test_core_copy__file_in_dir(void)
struct stat st;
const char *content = "This is some other stuff to copy\n";
- cl_git_pass(git_futils_mkdir("an_dir/in_a_dir", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("an_dir/in_a_dir", 0775, GIT_MKDIR_PATH));
cl_git_mkfile("an_dir/in_a_dir/copy_me", content);
cl_assert(git_path_isdir("an_dir"));
@@ -60,9 +60,9 @@ void test_core_copy__tree(void)
struct stat st;
const char *content = "File content\n";
- cl_git_pass(git_futils_mkdir("src/b", NULL, 0775, GIT_MKDIR_PATH));
- cl_git_pass(git_futils_mkdir("src/c/d", NULL, 0775, GIT_MKDIR_PATH));
- cl_git_pass(git_futils_mkdir("src/c/e", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/b", 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/c/d", 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/c/e", 0775, GIT_MKDIR_PATH));
cl_git_mkfile("src/f1", content);
cl_git_mkfile("src/b/f2", content);
diff --git a/tests/core/dirent.c b/tests/core/dirent.c
index d95e44196..2bd60269d 100644
--- a/tests/core/dirent.c
+++ b/tests/core/dirent.c
@@ -275,3 +275,32 @@ void test_core_dirent__diriter_with_fullname(void)
check_counts(&sub);
}
+
+void test_core_dirent__diriter_at_directory_root(void)
+{
+ git_path_diriter diriter = GIT_PATH_DIRITER_INIT;
+ const char *sandbox_path, *path;
+ char *root_path;
+ size_t path_len;
+ int root_offset, error;
+
+ sandbox_path = clar_sandbox_path();
+ cl_assert((root_offset = git_path_root(sandbox_path)) >= 0);
+
+ cl_assert(root_path = git__calloc(1, root_offset + 2));
+ strncpy(root_path, sandbox_path, root_offset + 1);
+
+ cl_git_pass(git_path_diriter_init(&diriter, root_path, 0));
+
+ while ((error = git_path_diriter_next(&diriter)) == 0) {
+ cl_git_pass(git_path_diriter_fullpath(&path, &path_len, &diriter));
+
+ cl_assert(path_len > (size_t)(root_offset + 1));
+ cl_assert(path[root_offset+1] != '/');
+ }
+
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_path_diriter_free(&diriter);
+ git__free(root_path);
+}
diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c
index 3f7dc8569..915e3cc34 100644
--- a/tests/core/filebuf.c
+++ b/tests/core/filebuf.c
@@ -151,3 +151,82 @@ void test_core_filebuf__rename_error(void)
cl_assert_equal_i(false, git_path_exists(test_lock));
}
+
+void test_core_filebuf__symlink_follow(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const char *dir = "linkdir", *source = "linkdir/link";
+
+#ifdef GIT_WIN32
+ cl_skip();
+#endif
+
+ cl_git_pass(p_mkdir(dir, 0777));
+ cl_git_pass(p_symlink("target", source));
+
+ cl_git_pass(git_filebuf_open(&file, source, 0, 0666));
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+ cl_assert_equal_i(true, git_path_exists("linkdir/target.lock"));
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert_equal_i(true, git_path_exists("linkdir/target"));
+
+ git_filebuf_cleanup(&file);
+
+ /* The second time around, the target file does exist */
+ cl_git_pass(git_filebuf_open(&file, source, 0, 0666));
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+ cl_assert_equal_i(true, git_path_exists("linkdir/target.lock"));
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert_equal_i(true, git_path_exists("linkdir/target"));
+
+ git_filebuf_cleanup(&file);
+ cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_core_filebuf__symlink_depth(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const char *dir = "linkdir", *source = "linkdir/link";
+
+#ifdef GIT_WIN32
+ cl_skip();
+#endif
+
+ cl_git_pass(p_mkdir(dir, 0777));
+ /* Endless loop */
+ cl_git_pass(p_symlink("link", source));
+
+ cl_git_fail(git_filebuf_open(&file, source, 0, 0666));
+
+ cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_core_filebuf__hidden_file(void)
+{
+#ifndef GIT_WIN32
+ cl_skip();
+#else
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char *dir = "hidden", *test = "hidden/test";
+ bool hidden;
+
+ cl_git_pass(p_mkdir(dir, 0666));
+ cl_git_mkfile(test, "dummy content");
+
+ cl_git_pass(git_win32__set_hidden(test, true));
+ cl_git_pass(git_win32__hidden(&hidden, test));
+ cl_assert(hidden);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+ cl_git_pass(git_filebuf_commit(&file));
+
+ git_filebuf_cleanup(&file);
+#endif
+}
diff --git a/tests/core/futils.c b/tests/core/futils.c
new file mode 100644
index 000000000..e7f7154ed
--- /dev/null
+++ b/tests/core/futils.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+
+// Fixture setup and teardown
+void test_core_futils__initialize(void)
+{
+ cl_must_pass(p_mkdir("futils", 0777));
+}
+
+void test_core_futils__cleanup(void)
+{
+ cl_fixture_cleanup("futils");
+}
+
+void test_core_futils__writebuffer(void)
+{
+ git_buf out = GIT_BUF_INIT,
+ append = GIT_BUF_INIT;
+
+ /* create a new file */
+ git_buf_puts(&out, "hello!\n");
+ git_buf_printf(&out, "this is a %s\n", "test");
+
+ cl_git_pass(git_futils_writebuffer(&out, "futils/test-file", O_RDWR|O_CREAT, 0666));
+
+ cl_assert_equal_file(out.ptr, out.size, "futils/test-file");
+
+ /* append some more data */
+ git_buf_puts(&append, "And some more!\n");
+ git_buf_put(&out, append.ptr, append.size);
+
+ cl_git_pass(git_futils_writebuffer(&append, "futils/test-file", O_RDWR|O_APPEND, 0666));
+
+ cl_assert_equal_file(out.ptr, out.size, "futils/test-file");
+
+ git_buf_free(&out);
+ git_buf_free(&append);
+}
+
+void test_core_futils__write_hidden_file(void)
+{
+#ifndef GIT_WIN32
+ cl_skip();
+#else
+ git_buf out = GIT_BUF_INIT, append = GIT_BUF_INIT;
+ bool hidden;
+
+ git_buf_puts(&out, "hidden file.\n");
+ git_futils_writebuffer(&out, "futils/test-file", O_RDWR | O_CREAT, 0666);
+
+ cl_git_pass(git_win32__set_hidden("futils/test-file", true));
+
+ /* append some more data */
+ git_buf_puts(&append, "And some more!\n");
+ git_buf_put(&out, append.ptr, append.size);
+
+ cl_git_pass(git_futils_writebuffer(&append, "futils/test-file", O_RDWR | O_APPEND, 0666));
+
+ cl_assert_equal_file(out.ptr, out.size, "futils/test-file");
+
+ cl_git_pass(git_win32__hidden(&hidden, "futils/test-file"));
+ cl_assert(hidden);
+
+ git_buf_free(&out);
+ git_buf_free(&append);
+#endif
+}
+
diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c
index f76fe1da9..96c972396 100644
--- a/tests/core/mkdir.c
+++ b/tests/core/mkdir.c
@@ -13,43 +13,82 @@ static void cleanup_basic_dirs(void *ref)
git_futils_rmdir_r("d4", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
}
+void test_core_mkdir__absolute(void)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ cl_set_cleanup(cleanup_basic_dirs, NULL);
+
+ git_buf_joinpath(&path, clar_sandbox_path(), "d0");
+
+ /* make a directory */
+ cl_assert(!git_path_isdir(path.ptr));
+ cl_git_pass(git_futils_mkdir(path.ptr, 0755, 0));
+ cl_assert(git_path_isdir(path.ptr));
+
+ git_buf_joinpath(&path, path.ptr, "subdir");
+ cl_assert(!git_path_isdir(path.ptr));
+ cl_git_pass(git_futils_mkdir(path.ptr, 0755, 0));
+ cl_assert(git_path_isdir(path.ptr));
+
+ /* ensure mkdir_r works for a single subdir */
+ git_buf_joinpath(&path, path.ptr, "another");
+ cl_assert(!git_path_isdir(path.ptr));
+ cl_git_pass(git_futils_mkdir_r(path.ptr, 0755));
+ cl_assert(git_path_isdir(path.ptr));
+
+ /* ensure mkdir_r works */
+ git_buf_joinpath(&path, clar_sandbox_path(), "d1/foo/bar/asdf");
+ cl_assert(!git_path_isdir(path.ptr));
+ cl_git_pass(git_futils_mkdir_r(path.ptr, 0755));
+ cl_assert(git_path_isdir(path.ptr));
+
+ /* ensure we don't imply recursive */
+ git_buf_joinpath(&path, clar_sandbox_path(), "d2/foo/bar/asdf");
+ cl_assert(!git_path_isdir(path.ptr));
+ cl_git_fail(git_futils_mkdir(path.ptr, 0755, 0));
+ cl_assert(!git_path_isdir(path.ptr));
+
+ git_buf_free(&path);
+}
+
void test_core_mkdir__basic(void)
{
cl_set_cleanup(cleanup_basic_dirs, NULL);
/* make a directory */
cl_assert(!git_path_isdir("d0"));
- cl_git_pass(git_futils_mkdir("d0", NULL, 0755, 0));
+ cl_git_pass(git_futils_mkdir("d0", 0755, 0));
cl_assert(git_path_isdir("d0"));
/* make a path */
cl_assert(!git_path_isdir("d1"));
- cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", 0755, GIT_MKDIR_PATH));
cl_assert(git_path_isdir("d1"));
cl_assert(git_path_isdir("d1/d1.1"));
cl_assert(git_path_isdir("d1/d1.1/d1.2"));
/* make a dir exclusively */
cl_assert(!git_path_isdir("d2"));
- cl_git_pass(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
+ cl_git_pass(git_futils_mkdir("d2", 0755, GIT_MKDIR_EXCL));
cl_assert(git_path_isdir("d2"));
/* make exclusive failure */
- cl_git_fail(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
+ cl_git_fail(git_futils_mkdir("d2", 0755, GIT_MKDIR_EXCL));
/* make a path exclusively */
cl_assert(!git_path_isdir("d3"));
- cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
cl_assert(git_path_isdir("d3"));
cl_assert(git_path_isdir("d3/d3.1/d3.2"));
/* make exclusive path failure */
- cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
/* ??? Should EXCL only apply to the last item in the path? */
/* path with trailing slash? */
cl_assert(!git_path_isdir("d4"));
- cl_git_pass(git_futils_mkdir("d4/d4.1/", NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("d4/d4.1/", 0755, GIT_MKDIR_PATH));
cl_assert(git_path_isdir("d4/d4.1"));
}
@@ -65,38 +104,38 @@ void test_core_mkdir__with_base(void)
cl_set_cleanup(cleanup_basedir, NULL);
- cl_git_pass(git_futils_mkdir(BASEDIR, NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir(BASEDIR, 0755, GIT_MKDIR_PATH));
- cl_git_pass(git_futils_mkdir("a", BASEDIR, 0755, 0));
+ cl_git_pass(git_futils_mkdir_relative("a", BASEDIR, 0755, 0, NULL));
cl_assert(git_path_isdir(BASEDIR "/a"));
- cl_git_pass(git_futils_mkdir("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir_relative("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH, NULL));
cl_assert(git_path_isdir(BASEDIR "/b/b1/b2"));
/* exclusive with existing base */
- cl_git_pass(git_futils_mkdir("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ cl_git_pass(git_futils_mkdir_relative("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL));
/* fail: exclusive with duplicated suffix */
- cl_git_fail(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ cl_git_fail(git_futils_mkdir_relative("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL));
/* fail: exclusive with any duplicated component */
- cl_git_fail(git_futils_mkdir("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ cl_git_fail(git_futils_mkdir_relative("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL));
/* success: exclusive without path */
- cl_git_pass(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL));
+ cl_git_pass(git_futils_mkdir_relative("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL, NULL));
/* path with shorter base and existing dirs */
- cl_git_pass(git_futils_mkdir("dir/here/d/", "base", 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir_relative("dir/here/d/", "base", 0755, GIT_MKDIR_PATH, NULL));
cl_assert(git_path_isdir("base/dir/here/d"));
/* fail: path with shorter base and existing dirs */
- cl_git_fail(git_futils_mkdir("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ cl_git_fail(git_futils_mkdir_relative("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL));
/* fail: base with missing components */
- cl_git_fail(git_futils_mkdir("f/", "base/missing", 0755, GIT_MKDIR_PATH));
+ cl_git_fail(git_futils_mkdir_relative("f/", "base/missing", 0755, GIT_MKDIR_PATH, NULL));
/* success: shift missing component to path */
- cl_git_pass(git_futils_mkdir("missing/f/", "base/", 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir_relative("missing/f/", "base/", 0755, GIT_MKDIR_PATH, NULL));
}
static void cleanup_chmod_root(void *ref)
@@ -135,9 +174,9 @@ void test_core_mkdir__chmods(void)
cl_set_cleanup(cleanup_chmod_root, old);
- cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0));
+ cl_git_pass(git_futils_mkdir("r", 0777, 0));
- cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH, NULL));
cl_git_pass(git_path_lstat("r/mode", &st));
check_mode(0755, st.st_mode);
@@ -146,7 +185,7 @@ void test_core_mkdir__chmods(void)
cl_git_pass(git_path_lstat("r/mode/is/important", &st));
check_mode(0755, st.st_mode);
- cl_git_pass(git_futils_mkdir("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
+ cl_git_pass(git_futils_mkdir_relative("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD, NULL));
cl_git_pass(git_path_lstat("r/mode2", &st));
check_mode(0755, st.st_mode);
@@ -155,7 +194,7 @@ void test_core_mkdir__chmods(void)
cl_git_pass(git_path_lstat("r/mode2/is2/important2", &st));
check_mode(0777, st.st_mode);
- cl_git_pass(git_futils_mkdir("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
+ cl_git_pass(git_futils_mkdir_relative("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH, NULL));
cl_git_pass(git_path_lstat("r/mode3", &st));
check_mode(0777, st.st_mode);
@@ -166,7 +205,7 @@ void test_core_mkdir__chmods(void)
/* test that we chmod existing dir */
- cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
+ cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD, NULL));
cl_git_pass(git_path_lstat("r/mode", &st));
check_mode(0755, st.st_mode);
@@ -177,7 +216,7 @@ void test_core_mkdir__chmods(void)
/* test that we chmod even existing dirs if CHMOD_PATH is set */
- cl_git_pass(git_futils_mkdir("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
+ cl_git_pass(git_futils_mkdir_relative("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH, NULL));
cl_git_pass(git_path_lstat("r/mode2", &st));
check_mode(0777, st.st_mode);
@@ -187,6 +226,40 @@ void test_core_mkdir__chmods(void)
check_mode(0777, st.st_mode);
}
+void test_core_mkdir__keeps_parent_symlinks(void)
+{
+#ifndef GIT_WIN32
+ git_buf path = GIT_BUF_INIT;
+
+ cl_set_cleanup(cleanup_basic_dirs, NULL);
+
+ /* make a directory */
+ cl_assert(!git_path_isdir("d0"));
+ cl_git_pass(git_futils_mkdir("d0", 0755, 0));
+ cl_assert(git_path_isdir("d0"));
+
+ cl_must_pass(symlink("d0", "d1"));
+ cl_assert(git_path_islink("d1"));
+
+ cl_git_pass(git_futils_mkdir("d1/foo/bar", 0755, GIT_MKDIR_PATH|GIT_MKDIR_REMOVE_SYMLINKS));
+ cl_assert(git_path_islink("d1"));
+ cl_assert(git_path_isdir("d1/foo/bar"));
+ cl_assert(git_path_isdir("d0/foo/bar"));
+
+ cl_must_pass(symlink("d0", "d2"));
+ cl_assert(git_path_islink("d2"));
+
+ git_buf_joinpath(&path, clar_sandbox_path(), "d2/other/dir");
+
+ cl_git_pass(git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_PATH|GIT_MKDIR_REMOVE_SYMLINKS));
+ cl_assert(git_path_islink("d2"));
+ cl_assert(git_path_isdir("d2/other/dir"));
+ cl_assert(git_path_isdir("d0/other/dir"));
+
+ git_buf_free(&path);
+#endif
+}
+
void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void)
{
struct stat st;
@@ -200,8 +273,8 @@ void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void)
*old = p_umask(022);
cl_set_cleanup(cleanup_chmod_root, old);
- cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0));
- cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("r", 0777, 0));
+ cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH, NULL));
cl_git_pass(git_path_lstat("r/mode", &st));
check_mode(0755, st.st_mode);
@@ -210,10 +283,9 @@ void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void)
check_mode(0111, st.st_mode);
cl_git_pass(
- git_futils_mkdir("mode/is/okay/inside", "r", 0777, GIT_MKDIR_PATH));
+ git_futils_mkdir_relative("mode/is/okay/inside", "r", 0777, GIT_MKDIR_PATH, NULL));
cl_git_pass(git_path_lstat("r/mode/is/okay/inside", &st));
check_mode(0755, st.st_mode);
cl_must_pass(p_chmod("r/mode", 0777));
}
-
diff --git a/tests/core/stat.c b/tests/core/stat.c
index bd9b990e3..ef2e45a15 100644
--- a/tests/core/stat.c
+++ b/tests/core/stat.c
@@ -5,7 +5,7 @@
void test_core_stat__initialize(void)
{
- cl_git_pass(git_futils_mkdir("root/d1/d2", NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("root/d1/d2", 0755, GIT_MKDIR_PATH));
cl_git_mkfile("root/file", "whatever\n");
cl_git_mkfile("root/d1/file", "whatever\n");
}
diff --git a/tests/diff/binary.c b/tests/diff/binary.c
index 5298e9ebb..173a5994e 100644
--- a/tests/diff/binary.c
+++ b/tests/diff/binary.c
@@ -96,7 +96,8 @@ void test_diff_binary__add(void)
"Kc${Nk-~s>u4FC%O\n"
"\n" \
"literal 0\n" \
- "Hc$@<O00001\n";
+ "Hc$@<O00001\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
@@ -136,7 +137,8 @@ void test_diff_binary__modify(void)
"Mc${NkU}WL~000&M4gdfE\n" \
"\n" \
"literal 3\n" \
- "Kc${Nk-~s>u4FC%O\n";
+ "Kc${Nk-~s>u4FC%O\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY;
@@ -177,7 +179,8 @@ void test_diff_binary__delete(void)
"Hc$@<O00001\n" \
"\n" \
"literal 3\n" \
- "Kc${Nk-~s>u4FC%O\n";
+ "Kc${Nk-~s>u4FC%O\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
@@ -208,7 +211,8 @@ void test_diff_binary__delta(void)
"delta 198\n" \
"zc$}LmI8{(0BqLQJI6p64AwNwaIJGP_Pr*5}Br~;mqJ$<Jl;sX*mF<MGCYv&*L7AHu\n" \
"zGA1*^gt?gYVN82wTbPO_W)+x<&1+cP;HrPHR>PQ;Y(X&QMK*C5^Br3bjG4d=XI^5@\n" \
- "JfH567LIF3FM2!Fd\n";
+ "JfH567LIF3FM2!Fd\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
@@ -249,7 +253,8 @@ void test_diff_binary__delta_append(void)
"nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
"\n" \
"delta 7\n" \
- "Oc%18D`@*{63ljhg(E~C7\n";
+ "Oc%18D`@*{63ljhg(E~C7\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
@@ -314,7 +319,8 @@ void test_diff_binary__index_to_workdir(void)
"nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
"\n" \
"delta 7\n" \
- "Oc%18D`@*{63ljhg(E~C7\n";
+ "Oc%18D`@*{63ljhg(E~C7\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
@@ -379,7 +385,8 @@ void test_diff_binary__print_patch_from_diff(void)
"nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
"\n" \
"delta 7\n" \
- "Oc%18D`@*{63ljhg(E~C7\n";
+ "Oc%18D`@*{63ljhg(E~C7\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c
index e87769170..89f9483a6 100644
--- a/tests/diff/workdir.c
+++ b/tests/diff/workdir.c
@@ -2054,3 +2054,46 @@ void test_diff_workdir__only_writes_index_when_necessary(void)
git_index_free(index);
}
+void test_diff_workdir__to_index_pathlist(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_vector pathlist = GIT_VECTOR_INIT;
+
+ git_vector_insert(&pathlist, "foobar/asdf");
+ git_vector_insert(&pathlist, "subdir/asdf");
+ git_vector_insert(&pathlist, "ignored/asdf");
+
+ g_repo = cl_git_sandbox_init("status");
+
+ cl_git_mkfile("status/.gitignore", ".gitignore\n" "ignored/\n");
+
+ cl_must_pass(p_mkdir("status/foobar", 0777));
+ cl_git_mkfile("status/foobar/one", "one\n");
+
+ cl_must_pass(p_mkdir("status/ignored", 0777));
+ cl_git_mkfile("status/ignored/one", "one\n");
+ cl_git_mkfile("status/ignored/two", "two\n");
+ cl_git_mkfile("status/ignored/three", "three\n");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ opts.flags = GIT_DIFF_INCLUDE_IGNORED;
+ opts.pathspec.strings = pathlist.contents;
+ opts.pathspec.count = pathlist.length;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ git_index_free(index);
+ git_vector_free(&pathlist);
+}
+
diff --git a/tests/index/bypath.c b/tests/index/bypath.c
index b607e1732..b152b0917 100644
--- a/tests/index/bypath.c
+++ b/tests/index/bypath.c
@@ -72,3 +72,171 @@ void test_index_bypath__add_hidden(void)
cl_assert_equal_i(GIT_FILEMODE_BLOB, entry->mode);
#endif
}
+
+void test_index_bypath__add_keeps_existing_case(void)
+{
+ const git_index_entry *entry;
+
+ if (!cl_repo_get_bool(g_repo, "core.ignorecase"))
+ clar__skip();
+
+ cl_git_mkfile("submod2/just_a_dir/file1.txt", "This is a file");
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/file1.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/file1.txt", 0));
+ cl_assert_equal_s("just_a_dir/file1.txt", entry->path);
+
+ cl_git_rewritefile("submod2/just_a_dir/file1.txt", "Updated!");
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/FILE1.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/FILE1.txt", 0));
+ cl_assert_equal_s("just_a_dir/file1.txt", entry->path);
+}
+
+void test_index_bypath__add_honors_existing_case(void)
+{
+ const git_index_entry *entry;
+
+ if (!cl_repo_get_bool(g_repo, "core.ignorecase"))
+ clar__skip();
+
+ cl_git_mkfile("submod2/just_a_dir/file1.txt", "This is a file");
+ cl_git_mkfile("submod2/just_a_dir/file2.txt", "This is another file");
+ cl_git_mkfile("submod2/just_a_dir/file3.txt", "This is another file");
+ cl_git_mkfile("submod2/just_a_dir/file4.txt", "And another file");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/File1.txt"));
+ cl_git_pass(git_index_add_bypath(g_idx, "JUST_A_DIR/file2.txt"));
+ cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/FILE3.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/File1.txt", 0));
+ cl_assert_equal_s("just_a_dir/File1.txt", entry->path);
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "JUST_A_DIR/file2.txt", 0));
+ cl_assert_equal_s("just_a_dir/file2.txt", entry->path);
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/FILE3.txt", 0));
+ cl_assert_equal_s("just_a_dir/FILE3.txt", entry->path);
+
+ cl_git_rewritefile("submod2/just_a_dir/file3.txt", "Rewritten");
+ cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/file3.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/file3.txt", 0));
+ cl_assert_equal_s("just_a_dir/FILE3.txt", entry->path);
+}
+
+void test_index_bypath__add_honors_existing_case_2(void)
+{
+ git_index_entry dummy = { { 0 } };
+ const git_index_entry *entry;
+
+ if (!cl_repo_get_bool(g_repo, "core.ignorecase"))
+ clar__skip();
+
+ dummy.mode = GIT_FILEMODE_BLOB;
+
+ /* note that `git_index_add` does no checking to canonical directories */
+ dummy.path = "Just_a_dir/file0.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "just_a_dir/fileA.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "Just_A_Dir/fileB.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "JUST_A_DIR/fileC.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "just_A_dir/fileD.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "JUST_a_DIR/fileE.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ cl_git_mkfile("submod2/just_a_dir/file1.txt", "This is a file");
+ cl_git_mkfile("submod2/just_a_dir/file2.txt", "This is another file");
+ cl_git_mkfile("submod2/just_a_dir/file3.txt", "This is another file");
+ cl_git_mkfile("submod2/just_a_dir/file4.txt", "And another file");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/File1.txt"));
+ cl_git_pass(git_index_add_bypath(g_idx, "JUST_A_DIR/file2.txt"));
+ cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/FILE3.txt"));
+ cl_git_pass(git_index_add_bypath(g_idx, "JusT_A_DIR/FILE4.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/File1.txt", 0));
+ cl_assert_equal_s("just_a_dir/File1.txt", entry->path);
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "JUST_A_DIR/file2.txt", 0));
+ cl_assert_equal_s("JUST_A_DIR/file2.txt", entry->path);
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/FILE3.txt", 0));
+ cl_assert_equal_s("Just_A_Dir/FILE3.txt", entry->path);
+
+ cl_git_rewritefile("submod2/just_a_dir/file3.txt", "Rewritten");
+ cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/file3.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/file3.txt", 0));
+ cl_assert_equal_s("Just_A_Dir/FILE3.txt", entry->path);
+}
+
+void test_index_bypath__add_honors_existing_case_3(void)
+{
+ git_index_entry dummy = { { 0 } };
+ const git_index_entry *entry;
+
+ if (!cl_repo_get_bool(g_repo, "core.ignorecase"))
+ clar__skip();
+
+ dummy.mode = GIT_FILEMODE_BLOB;
+
+ dummy.path = "just_a_dir/filea.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "Just_A_Dir/fileB.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "just_A_DIR/FILEC.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "Just_a_DIR/FileD.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ cl_git_mkfile("submod2/JuSt_A_DiR/fILEE.txt", "This is a file");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/fILEE.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "JUST_A_DIR/fILEE.txt", 0));
+ cl_assert_equal_s("just_a_dir/fILEE.txt", entry->path);
+}
+
+void test_index_bypath__add_honors_existing_case_4(void)
+{
+ git_index_entry dummy = { { 0 } };
+ const git_index_entry *entry;
+
+ if (!cl_repo_get_bool(g_repo, "core.ignorecase"))
+ clar__skip();
+
+ dummy.mode = GIT_FILEMODE_BLOB;
+
+ dummy.path = "just_a_dir/a/b/c/d/e/file1.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "just_a_dir/a/B/C/D/E/file2.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ cl_must_pass(p_mkdir("submod2/just_a_dir/a", 0777));
+ cl_must_pass(p_mkdir("submod2/just_a_dir/a/b", 0777));
+ cl_must_pass(p_mkdir("submod2/just_a_dir/a/b/z", 0777));
+ cl_must_pass(p_mkdir("submod2/just_a_dir/a/b/z/y", 0777));
+ cl_must_pass(p_mkdir("submod2/just_a_dir/a/b/z/y/x", 0777));
+
+ cl_git_mkfile("submod2/just_a_dir/a/b/z/y/x/FOO.txt", "This is a file");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/A/b/Z/y/X/foo.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/A/b/Z/y/X/foo.txt", 0));
+ cl_assert_equal_s("just_a_dir/a/b/Z/y/X/foo.txt", entry->path);
+}
+
diff --git a/tests/index/filemodes.c b/tests/index/filemodes.c
index b3907996b..6442d7755 100644
--- a/tests/index/filemodes.c
+++ b/tests/index/filemodes.c
@@ -239,6 +239,7 @@ void test_index_filemodes__invalid(void)
cl_git_pass(git_repository_index(&index, g_repo));
+ GIT_IDXENTRY_STAGE_SET(&entry, 0);
entry.path = "foo";
entry.mode = GIT_OBJ_BLOB;
cl_git_fail(git_index_add(index, &entry));
diff --git a/tests/index/rename.c b/tests/index/rename.c
index dd3cfa732..86eaf0053 100644
--- a/tests/index/rename.c
+++ b/tests/index/rename.c
@@ -48,3 +48,39 @@ void test_index_rename__single_file(void)
cl_fixture_cleanup("rename");
}
+
+void test_index_rename__casechanging(void)
+{
+ git_repository *repo;
+ git_index *index;
+ const git_index_entry *entry;
+ git_index_entry new = {{0}};
+
+ p_mkdir("rename", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./rename", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_mkfile("./rename/lame.name.txt", "new_file\n");
+
+ cl_git_pass(git_index_add_bypath(index, "lame.name.txt"));
+ cl_assert_equal_i(1, git_index_entrycount(index));
+ cl_assert((entry = git_index_get_bypath(index, "lame.name.txt", 0)));
+
+ memcpy(&new, entry, sizeof(git_index_entry));
+ new.path = "LAME.name.TXT";
+
+ cl_git_pass(git_index_add(index, &new));
+ cl_assert((entry = git_index_get_bypath(index, "LAME.name.TXT", 0)));
+
+ if (cl_repo_get_bool(repo, "core.ignorecase"))
+ cl_assert_equal_i(1, git_index_entrycount(index));
+ else
+ cl_assert_equal_i(2, git_index_entrycount(index));
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("rename");
+}
+
diff --git a/tests/index/tests.c b/tests/index/tests.c
index e1ff12ad0..1498196b2 100644
--- a/tests/index/tests.c
+++ b/tests/index/tests.c
@@ -155,6 +155,27 @@ void test_index_tests__find_in_empty(void)
git_index_free(index);
}
+void test_index_tests__find_prefix(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ size_t pos;
+
+ cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+
+ cl_git_pass(git_index_find_prefix(&pos, index, "src"));
+ entry = git_index_get_byindex(index, pos);
+ cl_assert(git__strcmp(entry->path, "src/block-sha1/sha1.c") == 0);
+
+ cl_git_pass(git_index_find_prefix(&pos, index, "src/co"));
+ entry = git_index_get_byindex(index, pos);
+ cl_assert(git__strcmp(entry->path, "src/commit.c") == 0);
+
+ cl_assert(GIT_ENOTFOUND == git_index_find_prefix(NULL, index, "blah"));
+
+ git_index_free(index);
+}
+
void test_index_tests__write(void)
{
git_index *index;
@@ -731,7 +752,7 @@ void test_index_tests__reload_from_disk(void)
cl_set_cleanup(&cleanup_myrepo, NULL);
- cl_git_pass(git_futils_mkdir("./myrepo", NULL, 0777, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("./myrepo", 0777, GIT_MKDIR_PATH));
cl_git_mkfile("./myrepo/a.txt", "a\n");
cl_git_mkfile("./myrepo/b.txt", "b\n");
@@ -792,10 +813,43 @@ void test_index_tests__reload_while_ignoring_case(void)
cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEXCAP_IGNORE_CASE));
cl_git_pass(git_index_read(index, true));
cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(git_index_get_bypath(index, ".HEADER", 0));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, ".header", 0));
cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE));
cl_git_pass(git_index_read(index, true));
cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(git_index_get_bypath(index, ".HEADER", 0));
+ cl_assert(git_index_get_bypath(index, ".header", 0));
+
+ git_index_free(index);
+}
+
+void test_index_tests__change_icase_on_instance(void)
+{
+ git_index *index;
+ unsigned int caps;
+ const git_index_entry *e;
+
+ cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+
+ caps = git_index_caps(index);
+ cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEXCAP_IGNORE_CASE));
+ cl_assert_equal_i(false, index->ignore_case);
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(e = git_index_get_bypath(index, "src/common.h", 0));
+ cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "SRC/Common.h", 0));
+ cl_assert(e = git_index_get_bypath(index, "COPYING", 0));
+ cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "copying", 0));
+
+ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE));
+ cl_assert_equal_i(true, index->ignore_case);
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(e = git_index_get_bypath(index, "COPYING", 0));
+ cl_assert_equal_s("COPYING", e->path);
+ cl_assert(e = git_index_get_bypath(index, "copying", 0));
+ cl_assert_equal_s("COPYING", e->path);
git_index_free(index);
}
diff --git a/tests/odb/alternates.c b/tests/odb/alternates.c
index c75f6feaa..b5c0e79c0 100644
--- a/tests/odb/alternates.c
+++ b/tests/odb/alternates.c
@@ -29,7 +29,7 @@ static void init_linked_repo(const char *path, const char *alternate)
cl_git_pass(git_path_prettify(&destpath, alternate, NULL));
cl_git_pass(git_buf_joinpath(&destpath, destpath.ptr, "objects"));
cl_git_pass(git_buf_joinpath(&filepath, git_repository_path(repo), "objects/info"));
- cl_git_pass(git_futils_mkdir(filepath.ptr, NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir(filepath.ptr, 0755, GIT_MKDIR_PATH));
cl_git_pass(git_buf_joinpath(&filepath, filepath.ptr , "alternates"));
cl_git_pass(git_filebuf_open(&file, git_buf_cstr(&filepath), 0, 0666));
diff --git a/tests/online/badssl.c b/tests/online/badssl.c
new file mode 100644
index 000000000..850468320
--- /dev/null
+++ b/tests/online/badssl.c
@@ -0,0 +1,27 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+
+static git_repository *g_repo;
+
+#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
+
+void test_online_badssl__expired(void)
+{
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "https://expired.badssl.com/fake.git", "./fake", NULL));
+}
+
+void test_online_badssl__wrong_host(void)
+{
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "https://wrong.host.badssl.com/fake.git", "./fake", NULL));
+}
+
+void test_online_badssl__self_signed(void)
+{
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", NULL));
+}
+
+#endif
diff --git a/tests/online/push.c b/tests/online/push.c
index 13d364d30..77c437622 100644
--- a/tests/online/push.c
+++ b/tests/online/push.c
@@ -91,7 +91,6 @@ static int cred_acquire_cb(
/**
* git_push_status_foreach callback that records status entries.
- * @param data (git_vector *) of push_status instances
*/
static int record_push_status_cb(const char *ref, const char *msg, void *payload)
{
@@ -299,7 +298,7 @@ static void verify_update_tips_callback(git_remote *remote, expected_ref expecte
goto failed;
}
- if (git_oid_cmp(expected_refs[i].oid, tip->new_oid) != 0) {
+ if (git_oid_cmp(expected_refs[i].oid, &tip->new_oid) != 0) {
git_buf_printf(&msg, "Updated tip ID does not match expected ID");
failed = 1;
goto failed;
diff --git a/tests/online/push_util.c b/tests/online/push_util.c
index cd483c7c0..eafec2f05 100644
--- a/tests/online/push_util.c
+++ b/tests/online/push_util.c
@@ -9,8 +9,6 @@ const git_oid OID_ZERO = {{ 0 }};
void updated_tip_free(updated_tip *t)
{
git__free(t->name);
- git__free(t->old_oid);
- git__free(t->new_oid);
git__free(t);
}
@@ -46,14 +44,11 @@ int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *
updated_tip *t;
record_callbacks_data *record_data = (record_callbacks_data *)data;
- cl_assert(t = git__malloc(sizeof(*t)));
+ cl_assert(t = git__calloc(1, sizeof(*t)));
cl_assert(t->name = git__strdup(refname));
- cl_assert(t->old_oid = git__malloc(sizeof(*t->old_oid)));
- git_oid_cpy(t->old_oid, a);
-
- cl_assert(t->new_oid = git__malloc(sizeof(*t->new_oid)));
- git_oid_cpy(t->new_oid, b);
+ git_oid_cpy(&t->old_oid, a);
+ git_oid_cpy(&t->new_oid, b);
git_vector_insert(&record_data->updated_tips, t);
diff --git a/tests/online/push_util.h b/tests/online/push_util.h
index 822341bd2..570873cfe 100644
--- a/tests/online/push_util.h
+++ b/tests/online/push_util.h
@@ -16,8 +16,8 @@ extern const git_oid OID_ZERO;
typedef struct {
char *name;
- git_oid *old_oid;
- git_oid *new_oid;
+ git_oid old_oid;
+ git_oid new_oid;
} updated_tip;
typedef struct {
diff --git a/tests/refs/pack.c b/tests/refs/pack.c
index 7dfaf6d8f..bda86f69a 100644
--- a/tests/refs/pack.c
+++ b/tests/refs/pack.c
@@ -36,7 +36,7 @@ void test_refs_pack__empty(void)
git_buf temp_path = GIT_BUF_INIT;
cl_git_pass(git_buf_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir"));
- cl_git_pass(git_futils_mkdir_r(temp_path.ptr, NULL, GIT_REFS_DIR_MODE));
+ cl_git_pass(git_futils_mkdir_r(temp_path.ptr, GIT_REFS_DIR_MODE));
git_buf_free(&temp_path);
packall();
diff --git a/tests/repo/discover.c b/tests/repo/discover.c
index 7904b6496..86bd7458f 100644
--- a/tests/repo/discover.c
+++ b/tests/repo/discover.c
@@ -77,7 +77,7 @@ void test_repo_discover__0(void)
const char *ceiling_dirs;
const mode_t mode = 0777;
- git_futils_mkdir_r(DISCOVER_FOLDER, NULL, mode);
+ git_futils_mkdir_r(DISCOVER_FOLDER, mode);
append_ceiling_dir(&ceiling_dirs_buf, TEMP_REPO_FOLDER);
ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
@@ -88,15 +88,15 @@ void test_repo_discover__0(void)
git_repository_free(repo);
cl_git_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0));
- cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode));
+ cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
cl_git_pass(git_repository_discover(&sub_repository_path, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
- cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode));
+ cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, &sub_repository_path);
ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, &sub_repository_path);
ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, &sub_repository_path);
- cl_git_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, NULL, mode));
+ cl_git_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, mode));
write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT);
write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT);
write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../");
@@ -105,13 +105,13 @@ void test_repo_discover__0(void)
ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, &sub_repository_path);
ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, &repository_path);
- cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, NULL, mode));
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, mode));
write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:");
- cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, NULL, mode));
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, mode));
write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:");
- cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, NULL, mode));
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, mode));
write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n");
- cl_git_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, NULL, mode));
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, mode));
write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist");
cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs));
cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs));
diff --git a/tests/repo/init.c b/tests/repo/init.c
index 929d74180..7a9ec20f1 100644
--- a/tests/repo/init.c
+++ b/tests/repo/init.c
@@ -99,7 +99,7 @@ void test_repo_init__bare_repo_escaping_current_workdir(void)
cl_git_pass(git_path_prettify_dir(&path_current_workdir, ".", NULL));
cl_git_pass(git_buf_joinpath(&path_repository, git_buf_cstr(&path_current_workdir), "a/b/c"));
- cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), NULL, GIT_DIR_MODE));
+ cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), GIT_DIR_MODE));
/* Change the current working directory */
cl_git_pass(chdir(git_buf_cstr(&path_repository)));
@@ -312,7 +312,7 @@ void test_repo_init__extended_0(void)
cl_git_fail(git_repository_init_ext(&_repo, "extended", &opts));
/* make the directory first, then it should succeed */
- cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0));
+ cl_git_pass(git_futils_mkdir("extended", 0775, 0));
cl_git_pass(git_repository_init_ext(&_repo, "extended", &opts));
cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/extended/"));
@@ -631,7 +631,7 @@ void test_repo_init__can_reinit_an_initialized_repository(void)
cl_set_cleanup(&cleanup_repository, "extended");
- cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0));
+ cl_git_pass(git_futils_mkdir("extended", 0775, 0));
cl_git_pass(git_repository_init(&_repo, "extended", false));
cl_git_pass(git_repository_init(&reinit, "extended", false));
diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c
index 9c4cc9ea4..83b824691 100644
--- a/tests/repo/iterator.c
+++ b/tests/repo/iterator.c
@@ -848,14 +848,14 @@ static void build_workdir_tree(const char *root, int dirs, int subs)
for (i = 0; i < dirs; ++i) {
if (i % 2 == 0) {
p_snprintf(buf, sizeof(buf), "%s/dir%02d", root, i);
- cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir(buf, 0775, GIT_MKDIR_PATH));
p_snprintf(buf, sizeof(buf), "%s/dir%02d/file", root, i);
cl_git_mkfile(buf, buf);
buf[strlen(buf) - 5] = '\0';
} else {
p_snprintf(buf, sizeof(buf), "%s/DIR%02d", root, i);
- cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir(buf, 0775, GIT_MKDIR_PATH));
}
for (j = 0; j < subs; ++j) {
@@ -865,7 +865,7 @@ static void build_workdir_tree(const char *root, int dirs, int subs)
case 2: p_snprintf(sub, sizeof(sub), "%s/Sub%02d", buf, j); break;
case 3: p_snprintf(sub, sizeof(sub), "%s/SUB%02d", buf, j); break;
}
- cl_git_pass(git_futils_mkdir(sub, NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir(sub, 0775, GIT_MKDIR_PATH));
if (j % 2 == 0) {
size_t sublen = strlen(sub);
diff --git a/tests/repo/open.c b/tests/repo/open.c
index eb459e51d..d3d087231 100644
--- a/tests/repo/open.c
+++ b/tests/repo/open.c
@@ -91,7 +91,7 @@ static void make_gitlink_dir(const char *dir, const char *linktext)
{
git_buf path = GIT_BUF_INIT;
- cl_git_pass(git_futils_mkdir(dir, NULL, 0777, GIT_MKDIR_VERIFY_DIR));
+ cl_git_pass(git_futils_mkdir(dir, 0777, GIT_MKDIR_VERIFY_DIR));
cl_git_pass(git_buf_joinpath(&path, dir, ".git"));
cl_git_rewritefile(path.ptr, linktext);
git_buf_free(&path);
@@ -222,7 +222,7 @@ void test_repo_open__bad_gitlinks(void)
cl_git_sandbox_init("attr");
cl_git_pass(p_mkdir("invalid", 0777));
- cl_git_pass(git_futils_mkdir_r("invalid2/.git", NULL, 0777));
+ cl_git_pass(git_futils_mkdir_r("invalid2/.git", 0777));
for (scan = bad_links; *scan != NULL; scan++) {
make_gitlink_dir("alternate", *scan);
diff --git a/tests/repo/reservedname.c b/tests/repo/reservedname.c
index faea0cc2b..2a5b38239 100644
--- a/tests/repo/reservedname.c
+++ b/tests/repo/reservedname.c
@@ -106,3 +106,27 @@ void test_repo_reservedname__submodule_pointer(void)
git_repository_free(sub_repo);
#endif
}
+
+/* Like the `submodule_pointer` test (above), this ensures that we do not
+ * follow the gitlink to the submodule's repository location and treat that
+ * as a reserved name. This tests at an initial submodule update, where the
+ * submodule repo is being created.
+ */
+void test_repo_reservedname__submodule_pointer_during_create(void)
+{
+ git_repository *repo;
+ git_submodule *sm;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ git_buf url = GIT_BUF_INIT;
+
+ repo = setup_fixture_super();
+
+ cl_git_pass(git_buf_joinpath(&url, clar_sandbox_path(), "sub.git"));
+ cl_repo_set_string(repo, "submodule.sub.url", url.ptr);
+
+ cl_git_pass(git_submodule_lookup(&sm, repo, "sub"));
+ cl_git_pass(git_submodule_update(sm, 1, &update_options));
+
+ git_submodule_free(sm);
+ git_buf_free(&url);
+}
diff --git a/tests/resources/sub.git/HEAD b/tests/resources/sub.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/sub.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/sub.git/config b/tests/resources/sub.git/config
new file mode 100644
index 000000000..78387c50b
--- /dev/null
+++ b/tests/resources/sub.git/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/sub.git/index b/tests/resources/sub.git/index
new file mode 100644
index 000000000..54be69e33
--- /dev/null
+++ b/tests/resources/sub.git/index
Binary files differ
diff --git a/tests/resources/sub.git/logs/HEAD b/tests/resources/sub.git/logs/HEAD
new file mode 100644
index 000000000..f636268f6
--- /dev/null
+++ b/tests/resources/sub.git/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 b7a59b3f4ea13b985f8a1e0d3757d5cd3331add8 Edward Thomson <ethomson@microsoft.com> 1442522322 -0400 commit (initial): Initial revision
diff --git a/tests/resources/sub.git/logs/refs/heads/master b/tests/resources/sub.git/logs/refs/heads/master
new file mode 100644
index 000000000..f636268f6
--- /dev/null
+++ b/tests/resources/sub.git/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 b7a59b3f4ea13b985f8a1e0d3757d5cd3331add8 Edward Thomson <ethomson@microsoft.com> 1442522322 -0400 commit (initial): Initial revision
diff --git a/tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741d b/tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741d
new file mode 100644
index 000000000..a095b3fb8
--- /dev/null
+++ b/tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741d
Binary files differ
diff --git a/tests/resources/sub.git/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 b/tests/resources/sub.git/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623
new file mode 100644
index 000000000..ef8316670
--- /dev/null
+++ b/tests/resources/sub.git/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623
Binary files differ
diff --git a/tests/resources/sub.git/objects/94/c7d78d85c933d1d95b56bc2de01833ba8559fb b/tests/resources/sub.git/objects/94/c7d78d85c933d1d95b56bc2de01833ba8559fb
new file mode 100644
index 000000000..9adc11d71
--- /dev/null
+++ b/tests/resources/sub.git/objects/94/c7d78d85c933d1d95b56bc2de01833ba8559fb
Binary files differ
diff --git a/tests/resources/sub.git/objects/b7/a59b3f4ea13b985f8a1e0d3757d5cd3331add8 b/tests/resources/sub.git/objects/b7/a59b3f4ea13b985f8a1e0d3757d5cd3331add8
new file mode 100644
index 000000000..221b55de7
--- /dev/null
+++ b/tests/resources/sub.git/objects/b7/a59b3f4ea13b985f8a1e0d3757d5cd3331add8
Binary files differ
diff --git a/tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773 b/tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773
new file mode 100644
index 000000000..d9bb9c84d
--- /dev/null
+++ b/tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773
Binary files differ
diff --git a/tests/resources/sub.git/refs/heads/master b/tests/resources/sub.git/refs/heads/master
new file mode 100644
index 000000000..0e4d6e2a7
--- /dev/null
+++ b/tests/resources/sub.git/refs/heads/master
@@ -0,0 +1 @@
+b7a59b3f4ea13b985f8a1e0d3757d5cd3331add8
diff --git a/tests/resources/super/.gitted/COMMIT_EDITMSG b/tests/resources/super/.gitted/COMMIT_EDITMSG
new file mode 100644
index 000000000..e2d6b8987
--- /dev/null
+++ b/tests/resources/super/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+submodule
diff --git a/tests/resources/super/.gitted/HEAD b/tests/resources/super/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/super/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/super/.gitted/config b/tests/resources/super/.gitted/config
new file mode 100644
index 000000000..06a8b7790
--- /dev/null
+++ b/tests/resources/super/.gitted/config
@@ -0,0 +1,10 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
+[submodule "sub"]
+ url = ../sub.git
diff --git a/tests/resources/super/.gitted/index b/tests/resources/super/.gitted/index
new file mode 100644
index 000000000..cc2ffffb9
--- /dev/null
+++ b/tests/resources/super/.gitted/index
Binary files differ
diff --git a/tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4 b/tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4
new file mode 100644
index 000000000..727d3a696
--- /dev/null
+++ b/tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4
Binary files differ
diff --git a/tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91 b/tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91
new file mode 100644
index 000000000..7fd889d5f
--- /dev/null
+++ b/tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91
Binary files differ
diff --git a/tests/resources/super/.gitted/objects/d7/57768b570a83e80d02edcc1032db14573e5034 b/tests/resources/super/.gitted/objects/d7/57768b570a83e80d02edcc1032db14573e5034
new file mode 100644
index 000000000..f26c68c54
--- /dev/null
+++ b/tests/resources/super/.gitted/objects/d7/57768b570a83e80d02edcc1032db14573e5034
Binary files differ
diff --git a/tests/resources/super/.gitted/refs/heads/master b/tests/resources/super/.gitted/refs/heads/master
new file mode 100644
index 000000000..663a9dcd9
--- /dev/null
+++ b/tests/resources/super/.gitted/refs/heads/master
@@ -0,0 +1 @@
+79d0d58ca6aa1688a073d280169908454cad5b91
diff --git a/tests/resources/super/gitmodules b/tests/resources/super/gitmodules
new file mode 100644
index 000000000..a3d8f7f5a
--- /dev/null
+++ b/tests/resources/super/gitmodules
@@ -0,0 +1,3 @@
+[submodule "sub"]
+ path = sub
+ url = ../sub.git
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
index ba1d69a99..c318046da 100644
--- a/tests/status/ignore.c
+++ b/tests/status/ignore.c
@@ -148,7 +148,7 @@ void test_status_ignore__ignore_pattern_contains_space(void)
cl_git_pass(git_status_file(&flags, g_repo, "foo bar.txt"));
cl_assert(flags == GIT_STATUS_IGNORED);
- cl_git_pass(git_futils_mkdir_r("empty_standard_repo/foo", NULL, mode));
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/foo", mode));
cl_git_mkfile("empty_standard_repo/foo/look-ma.txt", "I'm not going to be ignored!");
cl_git_pass(git_status_file(&flags, g_repo, "foo/look-ma.txt"));
@@ -206,7 +206,7 @@ void test_status_ignore__subdirectories(void)
* used a rooted path for an ignore, so I changed this behavior.
*/
cl_git_pass(git_futils_mkdir_r(
- "empty_standard_repo/test/ignore_me", NULL, 0775));
+ "empty_standard_repo/test/ignore_me", 0775));
cl_git_mkfile(
"empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!");
cl_git_mkfile(
@@ -230,9 +230,9 @@ static void make_test_data(const char *reponame, const char **files)
g_repo = cl_git_sandbox_init(reponame);
for (scan = files; *scan != NULL; ++scan) {
- cl_git_pass(git_futils_mkdir(
+ cl_git_pass(git_futils_mkdir_relative(
*scan + repolen, reponame,
- 0777, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST));
+ 0777, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST, NULL));
cl_git_mkfile(*scan, "contents");
}
}
@@ -612,7 +612,7 @@ void test_status_ignore__issue_1766_negated_ignores(void)
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(git_futils_mkdir_r(
- "empty_standard_repo/a", NULL, 0775));
+ "empty_standard_repo/a", 0775));
cl_git_mkfile(
"empty_standard_repo/a/.gitignore", "*\n!.gitignore\n");
cl_git_mkfile(
@@ -622,7 +622,7 @@ void test_status_ignore__issue_1766_negated_ignores(void)
assert_is_ignored("a/ignoreme");
cl_git_pass(git_futils_mkdir_r(
- "empty_standard_repo/b", NULL, 0775));
+ "empty_standard_repo/b", 0775));
cl_git_mkfile(
"empty_standard_repo/b/.gitignore", "*\n!.gitignore\n");
cl_git_mkfile(
@@ -1022,3 +1022,20 @@ void test_status_ignore__negate_exact_previous(void)
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, ".buildpath"));
cl_assert_equal_i(1, ignored);
}
+
+void test_status_ignore__negate_starstar(void)
+{
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/.gitignore",
+ "code/projects/**/packages/*\n"
+ "!code/projects/**/packages/repositories.config");
+
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/code/projects/foo/bar/packages", 0777));
+ cl_git_mkfile("empty_standard_repo/code/projects/foo/bar/packages/repositories.config", "");
+
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "code/projects/foo/bar/packages/repositories.config"));
+ cl_assert_equal_i(0, ignored);
+}
diff --git a/tests/status/worktree.c b/tests/status/worktree.c
index 75c7b71b0..fc4afc6be 100644
--- a/tests/status/worktree.c
+++ b/tests/status/worktree.c
@@ -195,7 +195,7 @@ void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void)
cl_git_pass(p_rename("status/subdir", "status/current_file"));
cl_git_pass(p_rename("status/swap", "status/subdir"));
cl_git_mkfile("status/.new_file", "dummy");
- cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", NULL, 0777));
+ cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", 0777));
cl_git_mkfile("status/zzz_new_dir/new_file", "dummy");
cl_git_mkfile("status/zzz_new_file", "dummy");
@@ -917,7 +917,7 @@ void test_status_worktree__long_filenames(void)
// Create directory with amazingly long filename
sprintf(path, "empty_standard_repo/%s", longname);
- cl_git_pass(git_futils_mkdir_r(path, NULL, 0777));
+ cl_git_pass(git_futils_mkdir_r(path, 0777));
sprintf(path, "empty_standard_repo/%s/foo", longname);
cl_git_mkfile(path, "dummy");
@@ -1007,7 +1007,7 @@ void test_status_worktree__unreadable(void)
status_entry_counts counts = {0};
/* Create directory with no read permission */
- cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777));
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777));
cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
p_chmod("empty_standard_repo/no_permission", 0644);
@@ -1041,7 +1041,7 @@ void test_status_worktree__unreadable_not_included(void)
status_entry_counts counts = {0};
/* Create directory with no read permission */
- cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777));
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777));
cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
p_chmod("empty_standard_repo/no_permission", 0644);
@@ -1074,7 +1074,7 @@ void test_status_worktree__unreadable_as_untracked(void)
status_entry_counts counts = {0};
/* Create directory with no read permission */
- cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777));
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777));
cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
p_chmod("empty_standard_repo/no_permission", 0644);
diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c
index ecea694e5..38e0fa314 100644
--- a/tests/submodule/lookup.c
+++ b/tests/submodule/lookup.c
@@ -333,3 +333,58 @@ void test_submodule_lookup__prefix_name(void)
git_submodule_free(sm);
}
+
+void test_submodule_lookup__renamed(void)
+{
+ const char *newpath = "sm_actually_changed";
+ git_index *idx;
+ sm_lookup_data data;
+
+ cl_git_pass(git_repository_index__weakptr(&idx, g_repo));
+
+ /* We're replicating 'git mv sm_unchanged sm_actually_changed' in this test */
+
+ cl_git_pass(p_rename("submod2/sm_unchanged", "submod2/sm_actually_changed"));
+
+ /* Change the path in .gitmodules and stage it*/
+ {
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
+ cl_git_pass(git_config_set_string(cfg, "submodule.sm_unchanged.path", newpath));
+ git_config_free(cfg);
+
+ cl_git_pass(git_index_add_bypath(idx, ".gitmodules"));
+ }
+
+ /* Change the worktree info in the the submodule's config */
+ {
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.git/modules/sm_unchanged/config"));
+ cl_git_pass(git_config_set_string(cfg, "core.worktree", "../../../sm_actually_changed"));
+ git_config_free(cfg);
+ }
+
+ /* Rename the entry in the index */
+ {
+ const git_index_entry *e;
+ git_index_entry entry = {{ 0 }};
+
+ e = git_index_get_bypath(idx, "sm_unchanged", 0);
+ cl_assert(e);
+ cl_assert_equal_i(GIT_FILEMODE_COMMIT, e->mode);
+
+ entry.path = newpath;
+ entry.mode = GIT_FILEMODE_COMMIT;
+ git_oid_cpy(&entry.id, &e->id);
+
+ cl_git_pass(git_index_remove(idx, "sm_unchanged", 0));
+ cl_git_pass(git_index_add(idx, &entry));
+ cl_git_pass(git_index_write(idx));
+ }
+
+ memset(&data, 0, sizeof(data));
+ cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+ cl_assert_equal_i(8, data.count);
+}
diff --git a/tests/submodule/status.c b/tests/submodule/status.c
index 5f4e62053..10f385ce9 100644
--- a/tests/submodule/status.c
+++ b/tests/submodule/status.c
@@ -92,7 +92,7 @@ void test_submodule_status__ignore_none(void)
cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
/* now mkdir sm_unchanged to test uninitialized */
- cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
+ cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
status = get_submodule_status(g_repo, "sm_unchanged");
cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
@@ -141,7 +141,7 @@ void test_submodule_status__ignore_untracked(void)
cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
/* now mkdir sm_unchanged to test uninitialized */
- cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
+ cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
@@ -185,7 +185,7 @@ void test_submodule_status__ignore_dirty(void)
cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
/* now mkdir sm_unchanged to test uninitialized */
- cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
+ cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
@@ -229,7 +229,7 @@ void test_submodule_status__ignore_all(void)
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
/* now mkdir sm_unchanged to test uninitialized */
- cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
+ cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
@@ -338,7 +338,7 @@ void test_submodule_status__untracked_dirs_containing_ignored_files(void)
"submod2/.git/modules/sm_unchanged/info/exclude", "\n*.ignored\n");
cl_git_pass(
- git_futils_mkdir("sm_unchanged/directory", "submod2", 0755, 0));
+ git_futils_mkdir_relative("sm_unchanged/directory", "submod2", 0755, 0, NULL));
cl_git_mkfile(
"submod2/sm_unchanged/directory/i_am.ignored",
"ignore this file, please\n");
diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c
index 1dc687231..cde69d92d 100644
--- a/tests/submodule/submodule_helpers.c
+++ b/tests/submodule/submodule_helpers.c
@@ -126,6 +126,22 @@ git_repository *setup_fixture_submod2(void)
return repo;
}
+git_repository *setup_fixture_super(void)
+{
+ git_repository *repo = cl_git_sandbox_init("super");
+
+ cl_fixture_sandbox("sub.git");
+ p_mkdir("super/sub", 0777);
+
+ rewrite_gitmodules(git_repository_workdir(repo));
+
+ cl_set_cleanup(cleanup_fixture_submodules, "sub.git");
+
+ cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+ return repo;
+}
+
git_repository *setup_fixture_submodule_simple(void)
{
git_repository *repo = cl_git_sandbox_init("submodule_simple");
diff --git a/tests/submodule/submodule_helpers.h b/tests/submodule/submodule_helpers.h
index 1493f245f..1191ab35b 100644
--- a/tests/submodule/submodule_helpers.h
+++ b/tests/submodule/submodule_helpers.h
@@ -4,6 +4,7 @@ extern void rewrite_gitmodules(const char *workdir);
extern git_repository *setup_fixture_submodules(void);
extern git_repository *setup_fixture_submod2(void);
extern git_repository *setup_fixture_submodule_simple(void);
+extern git_repository *setup_fixture_super(void);
extern unsigned int get_submodule_status(git_repository *, const char *);
diff --git a/tests/win32/longpath.c b/tests/win32/longpath.c
new file mode 100644
index 000000000..6de7d389a
--- /dev/null
+++ b/tests/win32/longpath.c
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "clone.h"
+#include "buffer.h"
+#include "fileops.h"
+
+static git_buf path = GIT_BUF_INIT;
+
+void test_win32_longpath__initialize(void)
+{
+#ifdef GIT_WIN32
+ const char *base = clar_sandbox_path();
+ size_t base_len = strlen(base);
+ size_t remain = MAX_PATH - base_len;
+ size_t i;
+
+ git_buf_clear(&path);
+ git_buf_puts(&path, base);
+ git_buf_putc(&path, '/');
+
+ cl_assert(remain < (MAX_PATH - 5));
+
+ for (i = 0; i < (remain - 5); i++)
+ git_buf_putc(&path, 'a');
+#endif
+}
+
+void test_win32_longpath__cleanup(void)
+{
+ git_buf_free(&path);
+}
+
+#ifdef GIT_WIN32
+void assert_name_too_long(void)
+{
+ const git_error *err;
+ size_t expected_len, actual_len;
+ const char *expected_msg;
+
+ err = giterr_last();
+ actual_len = strlen(err->message);
+
+ expected_msg = git_win32_get_error_message(ERROR_FILENAME_EXCED_RANGE);
+ expected_len = strlen(expected_msg);
+
+ /* check the suffix */
+ cl_assert_equal_s(expected_msg, err->message + (actual_len - expected_len));
+}
+#endif
+
+void test_win32_longpath__errmsg_on_checkout(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo;
+
+ cl_git_fail(git_clone(&repo, cl_fixture("testrepo.git"), path.ptr, NULL));
+ assert_name_too_long();
+#endif
+}