Merge branch 'pr/4228'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d5bf21f..ca52db7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -238,6 +238,7 @@
IF (WIN32 AND WINHTTP)
ADD_DEFINITIONS(-DGIT_WINHTTP)
+ ADD_DEFINITIONS(-DGIT_HTTPS)
# Since MinGW does not come with headers or an import library for winhttp,
# we have to include a private header and generate our own import library
@@ -546,11 +547,13 @@
IF (SECURITY_FOUND)
ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT)
+ ADD_DEFINITIONS(-DGIT_HTTPS)
INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR})
ENDIF ()
IF (OPENSSL_FOUND)
ADD_DEFINITIONS(-DGIT_OPENSSL)
+ ADD_DEFINITIONS(-DGIT_HTTPS)
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
ENDIF()
diff --git a/README.md b/README.md
index 472261c..1bbd371 100644
--- a/README.md
+++ b/README.md
@@ -43,10 +43,9 @@
What It Can Do
==============
-The goal of this library is to allow its users the ability to handle Git data in
-their applications from their programming language of choice, as is used in
-production for many applications including the GitHub.com site, in Plastic SCM
-and also powering Microsoft's Visual Studio tools for Git.
+libgit2 provides you with the ability to manage Git repositories in the
+programming language of your choice. It's used in production to power many
+applications including GitHub.com, Plastic SCM and Visual Studio Team Services.
It does not aim to replace the git tool or its user-facing commands. Some APIs
resemble the plumbing commands as those align closely with the concepts of the
diff --git a/examples/network/common.c b/examples/network/common.c
index d123eed..1a81a10 100644
--- a/examples/network/common.c
+++ b/examples/network/common.c
@@ -1,5 +1,7 @@
#include "common.h"
#include <stdio.h>
+#include <string.h>
+#include <errno.h>
/* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/
* with permission of the original author, Martin Pool.
@@ -20,15 +22,27 @@
unsigned int UNUSED(allowed_types),
void * UNUSED(payload))
{
- char username[128] = {0};
- char password[128] = {0};
+ char *username = NULL, *password = NULL;
+ int error;
printf("Username: ");
- scanf("%s", username);
+ if (getline(&username, NULL, stdin) < 0) {
+ fprintf(stderr, "Unable to read username: %s", strerror(errno));
+ return -1;
+ }
/* Yup. Right there on your terminal. Careful where you copy/paste output. */
printf("Password: ");
- scanf("%s", password);
+ if (getline(&password, NULL, stdin) < 0) {
+ fprintf(stderr, "Unable to read password: %s", strerror(errno));
+ free(username);
+ return -1;
+ }
- return git_cred_userpass_plaintext_new(out, username, password);
+ error = git_cred_userpass_plaintext_new(out, username, password);
+
+ free(username);
+ free(password);
+
+ return error;
}
diff --git a/include/git2/common.h b/include/git2/common.h
index 6d20920..d83e8c3 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -182,6 +182,7 @@
GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION,
GIT_OPT_GET_WINDOWS_SHAREMODE,
GIT_OPT_SET_WINDOWS_SHAREMODE,
+ GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION,
} git_libgit2_opt_t;
/**
@@ -337,6 +338,13 @@
* > is written to permanent storage, not simply cached. This
* > defaults to disabled.
*
+ * opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, int enabled)
+ *
+ * > Enable strict verification of object hashsums when reading
+ * > objects from disk. This may impact performance due to an
+ * > additional checksum calculation on each object. This defaults
+ * > to enabled.
+ *
* @param option Option key
* @param ... value to set the option
* @return 0 on success, <0 on failure
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 71bff0f..6f55802 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -54,6 +54,7 @@
GIT_PASSTHROUGH = -30, /**< Internal only */
GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
GIT_RETRY = -32, /**< Internal only */
+ GIT_EMISMATCH = -33, /**< Hashsum mismatch in object */
} git_error_code;
/**
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 244794e..e9e4e5b 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -715,8 +715,8 @@
* Peform all the steps from a push.
*
* @param remote the remote to push to
- * @param refspecs the refspecs to use for pushing. If none are
- * passed, the configured refspecs will be used
+ * @param refspecs the refspecs to use for pushing. If NULL or an empty
+ * array, the configured refspecs will be used
* @param opts options to use for this push
*/
GIT_EXTERN(int) git_remote_push(git_remote *remote,
diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h
index 60e38b2..a395de5 100644
--- a/include/git2/sys/transport.h
+++ b/include/git2/sys/transport.h
@@ -241,6 +241,16 @@
*/
GIT_EXTERN(int) git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods);
+/**
+ * Get a copy of the proxy options
+ *
+ * The url is copied and must be freed by the caller.
+ *
+ * @param out options struct to fill
+ * @param transport the transport to extract the data from.
+ */
+GIT_EXTERN(int) git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport);
+
/*
*** End of base transport interface ***
*** Begin interface for subtransports for the smart transport ***
diff --git a/src/attr_file.c b/src/attr_file.c
index 0bb761d..e30ea5e 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -395,9 +395,13 @@
if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
bool samename;
- /* for attribute checks or root ignore checks, fail match */
+ /*
+ * for attribute checks or checks at the root of this match's
+ * containing_dir (or root of the repository if no containing_dir),
+ * do not match.
+ */
if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) ||
- path->basename == path->path)
+ path->basename == relpath)
return false;
flags |= FNM_LEADING_DIR;
diff --git a/src/attrcache.c b/src/attrcache.c
index 4df14ee..5416189 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -290,14 +290,16 @@
const char *cfgval = entry->value;
/* expand leading ~/ as needed */
- if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' &&
- !git_sysdir_find_global_file(&buf, &cfgval[2]))
- *out = git_buf_detach(&buf);
- else if (cfgval)
+ if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') {
+ if (! (error = git_sysdir_expand_global_file(&buf, &cfgval[2])))
+ *out = git_buf_detach(&buf);
+ } else if (cfgval) {
*out = git__strdup(cfgval);
+ }
}
- else if (!git_sysdir_find_xdg_file(&buf, fallback))
+ else if (!git_sysdir_find_xdg_file(&buf, fallback)) {
*out = git_buf_detach(&buf);
+ }
git_config_entry_free(entry);
git_buf_free(&buf);
diff --git a/src/branch.c b/src/branch.c
index 7a83b83..fe4955a 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -130,14 +130,16 @@
static int branch_equals(git_repository *repo, const char *path, void *payload)
{
git_reference *branch = (git_reference *) payload;
- git_reference *head;
- int equal;
+ git_reference *head = NULL;
+ int equal = 0;
if (git_reference__read_head(&head, repo, path) < 0 ||
- git_reference_type(head) != GIT_REF_SYMBOLIC)
- return 0;
+ git_reference_type(head) != GIT_REF_SYMBOLIC)
+ goto done;
equal = !git__strcmp(head->target.symbolic, branch->name);
+
+done:
git_reference_free(head);
return equal;
}
diff --git a/src/config.c b/src/config.c
index cbcea2e..169a628 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1358,9 +1358,6 @@
int git_config_parse_path(git_buf *out, const char *value)
{
- int error = 0;
- const git_buf *home;
-
assert(out && value);
git_buf_sanitize(out);
@@ -1371,16 +1368,7 @@
return -1;
}
- if ((error = git_sysdir_get(&home, GIT_SYSDIR_GLOBAL)) < 0)
- return error;
-
- git_buf_sets(out, home->ptr);
- git_buf_puts(out, value + 1);
-
- if (git_buf_oom(out))
- return -1;
-
- return 0;
+ return git_sysdir_expand_global_file(out, value[1] ? &value[2] : NULL);
}
return git_buf_sets(out, value);
diff --git a/src/config_file.c b/src/config_file.c
index 7df43c8..2302d33 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -1256,7 +1256,7 @@
{
/* From the user's home */
if (path[0] == '~' && path[1] == '/')
- return git_sysdir_find_global_file(out, &path[1]);
+ return git_sysdir_expand_global_file(out, &path[1]);
return git_path_join_unrooted(out, path, dir, NULL);
}
diff --git a/src/global.c b/src/global.c
index e2ad8fe..afa57e1 100644
--- a/src/global.c
+++ b/src/global.c
@@ -22,7 +22,7 @@
git_mutex git__mwindow_mutex;
-#define MAX_SHUTDOWN_CB 8
+#define MAX_SHUTDOWN_CB 9
static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
static git_atomic git__n_shutdown_callbacks;
diff --git a/src/odb.c b/src/odb.c
index cf321f5..b66324f 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -31,6 +31,8 @@
#define GIT_ALTERNATES_MAX_DEPTH 5
+bool git_odb__strict_hash_verification = true;
+
typedef struct
{
git_odb_backend *backend;
@@ -998,7 +1000,9 @@
size_t i;
git_rawobj raw;
git_odb_object *object;
+ git_oid hashed;
bool found = false;
+ int error = 0;
if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0)
found = true;
@@ -1011,7 +1015,7 @@
continue;
if (b->read != NULL) {
- int error = b->read(&raw.data, &raw.len, &raw.type, b, id);
+ error = b->read(&raw.data, &raw.len, &raw.type, b, id);
if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND)
continue;
@@ -1025,12 +1029,26 @@
if (!found)
return GIT_ENOTFOUND;
+ if (git_odb__strict_hash_verification) {
+ if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0)
+ goto out;
+
+ if (!git_oid_equal(id, &hashed)) {
+ error = git_odb__error_mismatch(id, &hashed);
+ goto out;
+ }
+ }
+
giterr_clear();
if ((object = odb_object__alloc(id, &raw)) == NULL)
- return -1;
+ goto out;
*out = git_cache_store_raw(odb_cache(db), object);
- return 0;
+
+out:
+ if (error)
+ git__free(raw.data);
+ return error;
}
int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
@@ -1081,9 +1099,9 @@
const git_oid *key, size_t len, bool only_refreshed)
{
size_t i;
- int error = GIT_ENOTFOUND;
+ int error = 0;
git_oid found_full_oid = {{0}};
- git_rawobj raw;
+ git_rawobj raw = {0};
void *data = NULL;
bool found = false;
git_odb_object *object;
@@ -1102,14 +1120,22 @@
continue;
if (error)
- return error;
+ goto out;
git__free(data);
data = raw.data;
if (found && git_oid__cmp(&full_oid, &found_full_oid)) {
- git__free(raw.data);
- return git_odb__error_ambiguous("multiple matches for prefix");
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_printf(&buf, "multiple matches for prefix: %s",
+ git_oid_tostr_s(&full_oid));
+ git_buf_printf(&buf, " %s",
+ git_oid_tostr_s(&found_full_oid));
+
+ error = git_odb__error_ambiguous(buf.ptr);
+ git_buf_free(&buf);
+ goto out;
}
found_full_oid = full_oid;
@@ -1120,11 +1146,28 @@
if (!found)
return GIT_ENOTFOUND;
+ if (git_odb__strict_hash_verification) {
+ git_oid hash;
+
+ if ((error = git_odb_hash(&hash, raw.data, raw.len, raw.type)) < 0)
+ goto out;
+
+ if (!git_oid_equal(&found_full_oid, &hash)) {
+ error = git_odb__error_mismatch(&found_full_oid, &hash);
+ goto out;
+ }
+ }
+
if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
- return -1;
+ goto out;
*out = git_cache_store_raw(odb_cache(db), object);
- return 0;
+
+out:
+ if (error)
+ git__free(raw.data);
+
+ return error;
}
int git_odb_read_prefix(
@@ -1283,9 +1326,9 @@
{
giterr_set(GITERR_ODB,
"cannot %s - "
- "Invalid length. %"PRIuZ" was expected. The "
- "total size of the received chunks amounts to %"PRIuZ".",
- action, stream->declared_size, stream->received_bytes);
+ "Invalid length. %"PRIdZ" was expected. The "
+ "total size of the received chunks amounts to %"PRIdZ".",
+ action, stream->declared_size, stream->received_bytes);
return -1;
}
@@ -1411,6 +1454,19 @@
return 0;
}
+int git_odb__error_mismatch(const git_oid *expected, const git_oid *actual)
+{
+ char expected_oid[GIT_OID_HEXSZ + 1], actual_oid[GIT_OID_HEXSZ + 1];
+
+ git_oid_tostr(expected_oid, sizeof(expected_oid), expected);
+ git_oid_tostr(actual_oid, sizeof(actual_oid), actual);
+
+ giterr_set(GITERR_ODB, "object hash mismatch - expected %s but got %s",
+ expected_oid, actual_oid);
+
+ return GIT_EMISMATCH;
+}
+
int git_odb__error_notfound(
const char *message, const git_oid *oid, size_t oid_len)
{
diff --git a/src/odb.h b/src/odb.h
index 4f548bb..61d687a 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -20,6 +20,8 @@
#define GIT_OBJECT_DIR_MODE 0777
#define GIT_OBJECT_FILE_MODE 0444
+extern bool git_odb__strict_hash_verification;
+
/* DO NOT EXPORT */
typedef struct {
void *data; /**< Raw, decompressed object data. */
@@ -96,6 +98,12 @@
*/
int git_odb__hashlink(git_oid *out, const char *path);
+/**
+ * Generate a GIT_EMISMATCH error for the ODB.
+ */
+int git_odb__error_mismatch(
+ const git_oid *expected, const git_oid *actual);
+
/*
* Generate a GIT_ENOTFOUND error for the ODB.
*/
diff --git a/src/odb_loose.c b/src/odb_loose.c
index e14af4f..a97ac25 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -205,6 +205,11 @@
return inflate(s, 0);
}
+static void abort_inflate(z_stream *s)
+{
+ inflateEnd(s);
+}
+
static int finish_inflate(z_stream *s)
{
int status = Z_OK;
@@ -367,6 +372,7 @@
(used = get_object_header(&hdr, head)) == 0 ||
!git_object_typeisloose(hdr.type))
{
+ abort_inflate(&zs);
giterr_set(GITERR_ODB, "failed to inflate disk object");
return -1;
}
diff --git a/src/refs.c b/src/refs.c
index 31410b7..632a529 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -277,8 +277,8 @@
}
out:
- free(name);
- git_buf_clear(&reference);
+ git__free(name);
+ git_buf_free(&reference);
return error;
}
diff --git a/src/remote.c b/src/remote.c
index d3132f7..bd8b3cf 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -192,7 +192,7 @@
static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
{
git_remote *remote;
- git_config *config = NULL;
+ git_config *config_ro = NULL, *config_rw;
git_buf canonical_url = GIT_BUF_INIT;
git_buf var = GIT_BUF_INIT;
int error = -1;
@@ -200,7 +200,7 @@
/* name is optional */
assert(out && repo && url);
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ if ((error = git_repository_config_snapshot(&config_ro, repo)) < 0)
return error;
remote = git__calloc(1, sizeof(git_remote));
@@ -212,7 +212,8 @@
(error = canonicalize_url(&canonical_url, url)) < 0)
goto on_error;
- remote->url = apply_insteadof(repo->_config, canonical_url.ptr, GIT_DIRECTION_FETCH);
+ remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH);
+ GITERR_CHECK_ALLOC(remote->url);
if (name != NULL) {
remote->name = git__strdup(name);
@@ -221,7 +222,8 @@
if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0)
goto on_error;
- if ((error = git_config_set_string(config, var.ptr, canonical_url.ptr)) < 0)
+ if ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 ||
+ (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0)
goto on_error;
}
@@ -233,10 +235,7 @@
if (name && (error = write_add_refspec(repo, name, fetch, true)) < 0)
goto on_error;
- if ((error = git_repository_config_snapshot(&config, repo)) < 0)
- goto on_error;
-
- if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
+ if ((error = lookup_remote_prune_config(remote, config_ro, name)) < 0)
goto on_error;
/* Move the data over to where the matching functions can find them */
@@ -260,7 +259,7 @@
if (error)
git_remote_free(remote);
- git_config_free(config);
+ git_config_free(config_ro);
git_buf_free(&canonical_url);
git_buf_free(&var);
return error;
@@ -2412,7 +2411,7 @@
proxy = &opts->proxy_opts;
}
- assert(remote && refspecs);
+ assert(remote);
if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
return error;
diff --git a/src/repository.c b/src/repository.c
index 707b7b7..d0a38cc 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -422,10 +422,10 @@
}
static int find_repo(
- git_buf *repo_path,
- git_buf *parent_path,
- git_buf *link_path,
- git_buf *common_path,
+ git_buf *gitdir_path,
+ git_buf *workdir_path,
+ git_buf *gitlink_path,
+ git_buf *commondir_path,
const char *start_path,
uint32_t flags,
const char *ceiling_dirs)
@@ -440,7 +440,7 @@
bool in_dot_git;
size_t ceiling_offset = 0;
- git_buf_free(repo_path);
+ git_buf_clear(gitdir_path);
error = git_path_prettify(&path, start_path, NULL);
if (error < 0)
@@ -482,13 +482,13 @@
if (S_ISDIR(st.st_mode)) {
if (valid_repository_path(&path, &common_link)) {
git_path_to_dir(&path);
- git_buf_set(repo_path, path.ptr, path.size);
+ git_buf_set(gitdir_path, path.ptr, path.size);
- if (link_path)
- git_buf_attach(link_path,
+ if (gitlink_path)
+ git_buf_attach(gitlink_path,
git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0);
- if (common_path)
- git_buf_swap(&common_link, common_path);
+ if (commondir_path)
+ git_buf_swap(&common_link, commondir_path);
break;
}
@@ -498,12 +498,12 @@
if (error < 0)
break;
if (valid_repository_path(&repo_link, &common_link)) {
- git_buf_swap(repo_path, &repo_link);
+ git_buf_swap(gitdir_path, &repo_link);
- if (link_path)
- error = git_buf_put(link_path, path.ptr, path.size);
- if (common_path)
- git_buf_swap(&common_link, common_path);
+ if (gitlink_path)
+ error = git_buf_put(gitlink_path, path.ptr, path.size);
+ if (commondir_path)
+ git_buf_swap(&common_link, commondir_path);
}
break;
}
@@ -529,20 +529,20 @@
break;
}
- if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
- if (!git_buf_len(repo_path))
- git_buf_clear(parent_path);
+ if (!error && workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
+ if (!git_buf_len(gitdir_path))
+ git_buf_clear(workdir_path);
else {
- git_path_dirname_r(parent_path, path.ptr);
- git_path_to_dir(parent_path);
+ git_path_dirname_r(workdir_path, path.ptr);
+ git_path_to_dir(workdir_path);
}
- if (git_buf_oom(parent_path))
+ if (git_buf_oom(workdir_path))
return -1;
}
/* If we didn't find the repository, and we don't have any other error
* to report, report that. */
- if (!git_buf_len(repo_path) && !error) {
+ if (!git_buf_len(gitdir_path) && !error) {
giterr_set(GITERR_REPOSITORY,
"could not find repository from '%s'", start_path);
error = GIT_ENOTFOUND;
@@ -758,6 +758,29 @@
return error;
}
+static int repo_is_worktree(unsigned *out, const git_repository *repo)
+{
+ git_buf gitdir_link = GIT_BUF_INIT;
+ int error;
+
+ /* Worktrees cannot have the same commondir and gitdir */
+ if (repo->commondir && repo->gitdir
+ && !strcmp(repo->commondir, repo->gitdir)) {
+ *out = 0;
+ return 0;
+ }
+
+ if ((error = git_buf_joinpath(&gitdir_link, repo->gitdir, "gitdir")) < 0)
+ return -1;
+
+ /* A 'gitdir' file inside a git directory is currently
+ * only used when the repository is a working tree. */
+ *out = !!git_path_exists(gitdir_link.ptr);
+
+ git_buf_free(&gitdir_link);
+ return error;
+}
+
int git_repository_open_ext(
git_repository **repo_ptr,
const char *start_path,
@@ -765,8 +788,9 @@
const char *ceiling_dirs)
{
int error;
- git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT,
- link_path = GIT_BUF_INIT, common_path = GIT_BUF_INIT;
+ unsigned is_worktree;
+ git_buf gitdir = GIT_BUF_INIT, workdir = GIT_BUF_INIT,
+ gitlink = GIT_BUF_INIT, commondir = GIT_BUF_INIT;
git_repository *repo;
git_config *config = NULL;
@@ -777,7 +801,7 @@
*repo_ptr = NULL;
error = find_repo(
- &path, &parent, &link_path, &common_path, start_path, flags, ceiling_dirs);
+ &gitdir, &workdir, &gitlink, &commondir, start_path, flags, ceiling_dirs);
if (error < 0 || !repo_ptr)
return error;
@@ -785,24 +809,21 @@
repo = repository_alloc();
GITERR_CHECK_ALLOC(repo);
- repo->gitdir = git_buf_detach(&path);
+ repo->gitdir = git_buf_detach(&gitdir);
GITERR_CHECK_ALLOC(repo->gitdir);
- if (link_path.size) {
- repo->gitlink = git_buf_detach(&link_path);
+ if (gitlink.size) {
+ repo->gitlink = git_buf_detach(&gitlink);
GITERR_CHECK_ALLOC(repo->gitlink);
}
- if (common_path.size) {
- repo->commondir = git_buf_detach(&common_path);
+ if (commondir.size) {
+ repo->commondir = git_buf_detach(&commondir);
GITERR_CHECK_ALLOC(repo->commondir);
}
- if ((error = git_buf_joinpath(&path, repo->gitdir, "gitdir")) < 0)
+ if ((error = repo_is_worktree(&is_worktree, repo)) < 0)
goto cleanup;
- /* A 'gitdir' file inside a git directory is currently
- * only used when the repository is a working tree. */
- if (git_path_exists(path.ptr))
- repo->is_worktree = 1;
+ repo->is_worktree = is_worktree;
/*
* We'd like to have the config, but git doesn't particularly
@@ -822,13 +843,13 @@
if (config &&
((error = load_config_data(repo, config)) < 0 ||
- (error = load_workdir(repo, config, &parent)) < 0))
+ (error = load_workdir(repo, config, &workdir)) < 0))
goto cleanup;
}
cleanup:
- git_buf_free(&path);
- git_buf_free(&parent);
+ git_buf_free(&gitdir);
+ git_buf_free(&workdir);
git_config_free(config);
if (error < 0)
@@ -2132,7 +2153,8 @@
out:
if (error)
git_reference_free(head);
- git_buf_clear(&path);
+
+ git_buf_free(&path);
return error;
}
@@ -2531,7 +2553,9 @@
git_buf_puts(out, " to ");
- if (git_reference__is_branch(new))
+ if (git_reference__is_branch(new) ||
+ git_reference__is_tag(new) ||
+ git_reference__is_remote(new))
git_buf_puts(out, git_reference__shorthand(new));
else
git_buf_puts(out, new);
@@ -2542,6 +2566,41 @@
return 0;
}
+static int detach(git_repository *repo, const git_oid *id, const char *new)
+{
+ int error;
+ git_buf log_message = GIT_BUF_INIT;
+ git_object *object = NULL, *peeled = NULL;
+ git_reference *new_head = NULL, *current = NULL;
+
+ assert(repo && id);
+
+ if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0)
+ return error;
+
+ if ((error = git_object_lookup(&object, repo, id, GIT_OBJ_ANY)) < 0)
+ goto cleanup;
+
+ if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0)
+ goto cleanup;
+
+ if (new == NULL)
+ new = git_oid_tostr_s(git_object_id(peeled));
+
+ if ((error = checkout_message(&log_message, current, new)) < 0)
+ goto cleanup;
+
+ error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message));
+
+cleanup:
+ git_buf_free(&log_message);
+ git_object_free(object);
+ git_object_free(peeled);
+ git_reference_free(current);
+ git_reference_free(new_head);
+ return error;
+}
+
int git_repository_set_head(
git_repository* repo,
const char* refname)
@@ -2575,7 +2634,8 @@
error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE,
git_reference_name(ref), true, git_buf_cstr(&log_message));
} else {
- error = git_repository_set_head_detached(repo, git_reference_target(ref));
+ error = detach(repo, git_reference_target(ref),
+ git_reference_is_tag(ref) || git_reference_is_remote(ref) ? refname : NULL);
}
} else if (git_reference__is_branch(refname)) {
error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname,
@@ -2590,41 +2650,6 @@
return error;
}
-static int detach(git_repository *repo, const git_oid *id, const char *from)
-{
- int error;
- git_buf log_message = GIT_BUF_INIT;
- git_object *object = NULL, *peeled = NULL;
- git_reference *new_head = NULL, *current = NULL;
-
- assert(repo && id);
-
- if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0)
- return error;
-
- if ((error = git_object_lookup(&object, repo, id, GIT_OBJ_ANY)) < 0)
- goto cleanup;
-
- if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0)
- goto cleanup;
-
- if (from == NULL)
- from = git_oid_tostr_s(git_object_id(peeled));
-
- if ((error = checkout_message(&log_message, current, from)) < 0)
- goto cleanup;
-
- error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message));
-
-cleanup:
- git_buf_free(&log_message);
- git_object_free(object);
- git_object_free(peeled);
- git_reference_free(current);
- git_reference_free(new_head);
- return error;
-}
-
int git_repository_set_head_detached(
git_repository* repo,
const git_oid* commitish)
diff --git a/src/revparse.c b/src/revparse.c
index d5511b4..fd6bd1e 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -892,6 +892,17 @@
const char *rstr;
revspec->flags = GIT_REVPARSE_RANGE;
+ /*
+ * Following git.git, don't allow '..' because it makes command line
+ * arguments which can be either paths or revisions ambiguous when the
+ * path is almost certainly intended. The empty range '...' is still
+ * allowed.
+ */
+ if (!git__strcmp(spec, "..")) {
+ giterr_set(GITERR_INVALID, "Invalid pattern '..'");
+ return GIT_EINVALIDSPEC;
+ }
+
lstr = git__substrdup(spec, dotdot - spec);
rstr = dotdot + 2;
if (dotdot[2] == '.') {
@@ -899,9 +910,17 @@
rstr++;
}
- error = git_revparse_single(&revspec->from, repo, lstr);
- if (!error)
- error = git_revparse_single(&revspec->to, repo, rstr);
+ error = git_revparse_single(
+ &revspec->from,
+ repo,
+ *lstr == '\0' ? "HEAD" : lstr);
+
+ if (!error) {
+ error = git_revparse_single(
+ &revspec->to,
+ repo,
+ *rstr == '\0' ? "HEAD" : rstr);
+ }
git__free((void*)lstr);
} else {
diff --git a/src/settings.c b/src/settings.c
index 07ac16a..25c5aae 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -15,6 +15,7 @@
#include "cache.h"
#include "global.h"
#include "object.h"
+#include "odb.h"
#include "refs.h"
#include "transports/smart.h"
@@ -31,7 +32,7 @@
#ifdef GIT_THREADS
| GIT_FEATURE_THREADS
#endif
-#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
+#ifdef GIT_HTTPS
| GIT_FEATURE_HTTPS
#endif
#if defined(GIT_SSH)
@@ -243,6 +244,10 @@
#endif
break;
+ case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION:
+ git_odb__strict_hash_verification = (va_arg(ap, int) != 0);
+ break;
+
default:
giterr_set(GITERR_INVALID, "invalid option key");
error = -1;
diff --git a/src/signature.c b/src/signature.c
index e792a52..a56b8a2 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -228,8 +228,11 @@
const char *time_start = email_end + 2;
const char *time_end;
- if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0)
+ if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0) {
+ git__free(sig->name);
+ git__free(sig->email);
return signature_error("invalid Unix timestamp");
+ }
/* do we have a timezone? */
if (time_end + 1 < buffer_end) {
diff --git a/src/sysdir.c b/src/sysdir.c
index ed11221..9312a7e 100644
--- a/src/sysdir.c
+++ b/src/sysdir.c
@@ -275,3 +275,14 @@
path, NULL, GIT_SYSDIR_TEMPLATE, "template");
}
+int git_sysdir_expand_global_file(git_buf *path, const char *filename)
+{
+ int error;
+
+ if ((error = git_sysdir_find_global_file(path, NULL)) == 0) {
+ if (filename)
+ error = git_buf_joinpath(path, path->ptr, filename);
+ }
+
+ return error;
+}
diff --git a/src/sysdir.h b/src/sysdir.h
index 1187898..79f2381 100644
--- a/src/sysdir.h
+++ b/src/sysdir.h
@@ -55,6 +55,18 @@
*/
extern int git_sysdir_find_template_dir(git_buf *path);
+/**
+ * Expand the name of a "global" file (i.e. one in a user's home
+ * directory). Unlike `find_global_file` (above), this makes no
+ * attempt to check for the existence of the file, and is useful if
+ * you want the full path regardless of existence.
+ *
+ * @param path buffer to write the full path into
+ * @param filename name of file in the home directory
+ * @return 0 on success or -1 on error
+ */
+extern int git_sysdir_expand_global_file(git_buf *path, const char *filename);
+
typedef enum {
GIT_SYSDIR_SYSTEM = 0,
GIT_SYSDIR_GLOBAL = 1,
diff --git a/src/transports/http.c b/src/transports/http.c
index 949e857..cb4a6d0 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -575,6 +575,9 @@
if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &url)) < 0)
return error;
+ opts.credentials = t->owner->proxy.credentials;
+ opts.certificate_check = t->owner->proxy.certificate_check;
+ opts.payload = t->owner->proxy.payload;
opts.type = GIT_PROXY_SPECIFIED;
opts.url = url;
error = git_stream_set_proxy(t->io, &opts);
diff --git a/src/transports/smart.c b/src/transports/smart.c
index e4aa26d..a96fdf6 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -472,6 +472,12 @@
return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload);
}
+int git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport)
+{
+ transport_smart *t = (transport_smart *) transport;
+ return git_proxy_options_dup(out, &t->proxy);
+}
+
int git_transport_smart(git_transport **out, git_remote *owner, void *param)
{
transport_smart *t;
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 44d02e5..4c55e3f 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -9,6 +9,7 @@
#include <libssh2.h>
#endif
+#include "global.h"
#include "git2.h"
#include "buffer.h"
#include "netops.h"
@@ -893,11 +894,22 @@
#endif
}
+#ifdef GIT_SSH
+static void shutdown_ssh(void)
+{
+ libssh2_exit();
+}
+#endif
+
int git_transport_ssh_global_init(void)
{
#ifdef GIT_SSH
+ if (libssh2_init(0) < 0) {
+ giterr_set(GITERR_SSH, "unable to initialize libssh2");
+ return -1;
+ }
- libssh2_init(0);
+ git__on_shutdown(shutdown_ssh);
return 0;
#else
diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c
index 3a19389..a089ee4 100644
--- a/tests/attr/ignore.c
+++ b/tests/attr/ignore.c
@@ -291,3 +291,15 @@
assert_is_ignored(true, "symlink");
assert_is_ignored(true, "lala/../symlink");
}
+
+void test_attr_ignore__test(void)
+{
+ cl_git_rewritefile("attr/.gitignore",
+ "/*/\n"
+ "!/src\n");
+ assert_is_ignored(false, "src/foo.c");
+ assert_is_ignored(false, "src/foo/foo.c");
+ assert_is_ignored(false, "README.md");
+ assert_is_ignored(true, "dist/foo.o");
+ assert_is_ignored(true, "bin/foo");
+}
diff --git a/tests/clone/local.c b/tests/clone/local.c
index 91a0a1c..7f54d05 100644
--- a/tests/clone/local.c
+++ b/tests/clone/local.c
@@ -16,6 +16,7 @@
return git_buf_printf(buf, "file://%s/%s", host, path);
}
+#ifdef GIT_WIN32
static int git_style_unc_path(git_buf *buf, const char *host, const char *path)
{
git_buf_clear(buf);
@@ -49,6 +50,7 @@
return 0;
}
+#endif
void test_clone_local__should_clone_local(void)
{
diff --git a/tests/config/include.c b/tests/config/include.c
index 882b89b..0a07c9b 100644
--- a/tests/config/include.c
+++ b/tests/config/include.c
@@ -108,6 +108,26 @@
git_config_free(cfg);
}
+void test_config_include__missing_homedir(void)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
+ cl_git_mkfile("including", "[include]\npath = ~/.nonexistentfile\n[foo]\nbar = baz");
+
+ giterr_clear();
+ cl_git_pass(git_config_open_ondisk(&cfg, "including"));
+ cl_assert(giterr_last() == NULL);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ cl_assert_equal_s("baz", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+ git_config_free(cfg);
+
+ cl_sandbox_set_search_path_defaults();
+}
+
#define replicate10(s) s s s s s s s s s s
void test_config_include__depth2(void)
{
diff --git a/tests/core/features.c b/tests/core/features.c
index cf5e190..7b28cc0 100644
--- a/tests/core/features.c
+++ b/tests/core/features.c
@@ -17,7 +17,9 @@
cl_assert((caps & GIT_FEATURE_THREADS) == 0);
#endif
+#ifdef GIT_HTTPS
cl_assert((caps & GIT_FEATURE_HTTPS) != 0);
+#endif
#if defined(GIT_SSH)
cl_assert((caps & GIT_FEATURE_SSH) != 0);
diff --git a/tests/object/lookup.c b/tests/object/lookup.c
index cfa6d46..544f32b 100644
--- a/tests/object/lookup.c
+++ b/tests/object/lookup.c
@@ -6,13 +6,12 @@
void test_object_lookup__initialize(void)
{
- cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+ g_repo = cl_git_sandbox_init("testrepo.git");
}
void test_object_lookup__cleanup(void)
{
- git_repository_free(g_repo);
- g_repo = NULL;
+ cl_git_sandbox_cleanup();
}
void test_object_lookup__lookup_wrong_type_returns_enotfound(void)
@@ -63,3 +62,61 @@
GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_TAG));
}
+void test_object_lookup__lookup_corrupt_object_returns_error(void)
+{
+ const char *commit = "8e73b769e97678d684b809b163bebdae2911720f",
+ *file = "objects/8e/73b769e97678d684b809b163bebdae2911720f";
+ git_buf path = GIT_BUF_INIT, contents = GIT_BUF_INIT;
+ git_oid oid;
+ git_object *object;
+ size_t i;
+
+ cl_git_pass(git_oid_fromstr(&oid, commit));
+ cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), file));
+ cl_git_pass(git_futils_readbuffer(&contents, path.ptr));
+
+ /* Corrupt and try to read the object */
+ for (i = 0; i < contents.size; i++) {
+ contents.ptr[i] ^= 0x1;
+ cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+ cl_git_fail(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT));
+ contents.ptr[i] ^= 0x1;
+ }
+
+ /* Restore original content and assert we can read the object */
+ cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT));
+
+ git_object_free(object);
+ git_buf_free(&path);
+ git_buf_free(&contents);
+}
+
+void test_object_lookup__lookup_object_with_wrong_hash_returns_error(void)
+{
+ const char *oldloose = "objects/8e/73b769e97678d684b809b163bebdae2911720f",
+ *newloose = "objects/8e/73b769e97678d684b809b163bebdae2911720e",
+ *commit = "8e73b769e97678d684b809b163bebdae2911720e";
+ git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT;
+ git_object *object;
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, commit));
+
+ /* Copy object to another location with wrong hash */
+ cl_git_pass(git_buf_joinpath(&oldpath, git_repository_path(g_repo), oldloose));
+ cl_git_pass(git_buf_joinpath(&newpath, git_repository_path(g_repo), newloose));
+ cl_git_pass(git_futils_cp(oldpath.ptr, newpath.ptr, 0644));
+
+ /* Verify that lookup fails due to a hashsum mismatch */
+ cl_git_fail_with(GIT_EMISMATCH, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT));
+
+ /* Disable verification and try again */
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1));
+
+ git_object_free(object);
+ git_buf_free(&oldpath);
+ git_buf_free(&newpath);
+}
diff --git a/tests/odb/backend/nonrefreshing.c b/tests/odb/backend/nonrefreshing.c
index b435294..f12ac74 100644
--- a/tests/odb/backend/nonrefreshing.c
+++ b/tests/odb/backend/nonrefreshing.c
@@ -17,6 +17,9 @@
static fake_backend *_fake;
static git_oid _oid;
+#define HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+#define EMPTY_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+
static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid)
{
fake_backend *fake;
@@ -78,7 +81,6 @@
{
fake_backend *fake;
- GIT_UNUSED(out_oid);
GIT_UNUSED(buffer_p);
GIT_UNUSED(len_p);
GIT_UNUSED(type_p);
@@ -89,6 +91,7 @@
fake->read_prefix_calls++;
+ git_oid_cpy(out_oid, &_oid);
*len_p = 0;
*buffer_p = NULL;
*type_p = GIT_OBJ_BLOB;
@@ -130,7 +133,7 @@
return 0;
}
-static void setup_repository_and_backend(git_error_code error_code)
+static void setup_repository_and_backend(git_error_code error_code, const char *hash)
{
git_odb *odb = NULL;
git_odb_backend *backend = NULL;
@@ -144,7 +147,7 @@
_fake = (fake_backend *)backend;
- cl_git_pass(git_oid_fromstr(&_oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+ cl_git_pass(git_oid_fromstr(&_oid, hash));
}
void test_odb_backend_nonrefreshing__cleanup(void)
@@ -156,7 +159,7 @@
{
git_odb *odb;
- setup_repository_and_backend(GIT_ENOTFOUND);
+ setup_repository_and_backend(GIT_ENOTFOUND, HASH);
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
cl_assert_equal_b(false, git_odb_exists(odb, &_oid));
@@ -168,7 +171,7 @@
{
git_object *obj;
- setup_repository_and_backend(GIT_ENOTFOUND);
+ setup_repository_and_backend(GIT_ENOTFOUND, HASH);
cl_git_fail_with(
git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY),
@@ -181,7 +184,7 @@
{
git_object *obj;
- setup_repository_and_backend(GIT_ENOTFOUND);
+ setup_repository_and_backend(GIT_ENOTFOUND, HASH);
cl_git_fail_with(
git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY),
@@ -196,7 +199,7 @@
size_t len;
git_otype type;
- setup_repository_and_backend(GIT_ENOTFOUND);
+ setup_repository_and_backend(GIT_ENOTFOUND, HASH);
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
@@ -211,7 +214,7 @@
{
git_odb *odb;
- setup_repository_and_backend(GIT_OK);
+ setup_repository_and_backend(GIT_OK, HASH);
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
cl_assert_equal_b(true, git_odb_exists(odb, &_oid));
@@ -223,7 +226,7 @@
{
git_object *obj;
- setup_repository_and_backend(GIT_OK);
+ setup_repository_and_backend(GIT_OK, EMPTY_HASH);
cl_git_pass(git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY));
@@ -236,7 +239,7 @@
{
git_object *obj;
- setup_repository_and_backend(GIT_OK);
+ setup_repository_and_backend(GIT_OK, EMPTY_HASH);
cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY));
@@ -251,7 +254,7 @@
size_t len;
git_otype type;
- setup_repository_and_backend(GIT_OK);
+ setup_repository_and_backend(GIT_OK, HASH);
cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
@@ -264,7 +267,7 @@
{
git_object *obj;
- setup_repository_and_backend(GIT_ENOTFOUND);
+ setup_repository_and_backend(GIT_ENOTFOUND, HASH);
cl_git_fail_with(
git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
diff --git a/tests/online/badssl.c b/tests/online/badssl.c
index aa4c24d..6524fcd 100644
--- a/tests/online/badssl.c
+++ b/tests/online/badssl.c
@@ -4,7 +4,7 @@
static git_repository *g_repo;
-#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
+#ifdef GIT_HTTPS
static bool g_has_ssl = true;
#else
static bool g_has_ssl = false;
diff --git a/tests/refs/revparse.c b/tests/refs/revparse.c
index c22c304..459188c 100644
--- a/tests/refs/revparse.c
+++ b/tests/refs/revparse.c
@@ -122,6 +122,14 @@
test_id_inrepo(spec, expected_left, expected_right, expected_flags, g_repo);
}
+static void test_invalid_revspec(const char* invalid_spec)
+{
+ git_revspec revspec;
+
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC, git_revparse(&revspec, g_repo, invalid_spec));
+}
+
void test_refs_revparse__initialize(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
@@ -749,6 +757,33 @@
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+
+ test_id("HEAD~3..",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVPARSE_RANGE);
+
+ test_id("HEAD~3...",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+
+ test_id("..HEAD~3",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ GIT_REVPARSE_RANGE);
+
+ test_id("...HEAD~3",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+
+ test_id("...",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+
+ test_invalid_revspec("..");
}
void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void)
diff --git a/tests/repo/discover.c b/tests/repo/discover.c
index abb7bd1..eadd055 100644
--- a/tests/repo/discover.c
+++ b/tests/repo/discover.c
@@ -33,7 +33,7 @@
git_buf_attach(&resolved, p_realpath(expected_path, NULL), 0);
cl_assert(resolved.size > 0);
cl_git_pass(git_path_to_dir(&resolved));
- cl_git_pass(git_repository_discover(&found_path, start_path, 0, ceiling_dirs));
+ cl_git_pass(git_repository_discover(&found_path, start_path, 1, ceiling_dirs));
cl_assert_equal_s(found_path.ptr, resolved.ptr);
diff --git a/tests/repo/head.c b/tests/repo/head.c
index 31c2287..d021160 100644
--- a/tests/repo/head.c
+++ b/tests/repo/head.c
@@ -261,15 +261,19 @@
cl_git_pass(git_revparse_single(&tag, repo, "tags/test"));
cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag)));
cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
+ cl_git_pass(git_repository_set_head(repo, "refs/tags/test"));
+ cl_git_pass(git_repository_set_head(repo, "refs/remotes/test/master"));
- test_reflog(repo, 2, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from master to haacked");
- test_reflog(repo, 1, NULL, "tags/test^{commit}", "foo@example.com", "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d");
- test_reflog(repo, 0, "tags/test^{commit}", "refs/heads/haacked", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked");
+ test_reflog(repo, 4, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from master to haacked");
+ test_reflog(repo, 3, NULL, "tags/test^{commit}", "foo@example.com", "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d");
+ test_reflog(repo, 2, "tags/test^{commit}", "refs/heads/haacked", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked");
+ test_reflog(repo, 1, "refs/heads/haacked", "tags/test^{commit}", "foo@example.com", "checkout: moving from haacked to test");
+ test_reflog(repo, 0, "tags/test^{commit}", "refs/remotes/test/master", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to test/master");
cl_git_pass(git_annotated_commit_from_revspec(&annotated, repo, "haacked~0"));
cl_git_pass(git_repository_set_head_detached_from_annotated(repo, annotated));
- test_reflog(repo, 0, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from haacked to haacked~0");
+ test_reflog(repo, 0, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from be3563ae3f795b2b4353bcce3a527ad0a4f7f644 to haacked~0");
git_annotated_commit_free(annotated);
git_object_free(tag);
diff --git a/tests/repo/open.c b/tests/repo/open.c
index 6114ad2..3239b6f 100644
--- a/tests/repo/open.c
+++ b/tests/repo/open.c
@@ -398,7 +398,8 @@
cl_git_fail(git_repository_open_bare(&barerepo, "alternate/subdir/sub2"));
cl_git_pass(git_repository_open_ext(
- &barerepo, "alternate/subdir/sub2", GIT_REPOSITORY_OPEN_BARE, NULL));
+ &barerepo, "alternate/subdir/sub2",
+ GIT_REPOSITORY_OPEN_BARE|GIT_REPOSITORY_OPEN_CROSS_FS, NULL));
cl_assert(git_repository_is_bare(barerepo));
git_repository_free(barerepo);
}
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
index 251de39..23384fb 100644
--- a/tests/status/ignore.c
+++ b/tests/status/ignore.c
@@ -1077,3 +1077,81 @@
cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "code/projects/foo/bar/packages/repositories.config"));
cl_assert_equal_i(0, ignored);
}
+
+void test_status_ignore__ignore_all_toplevel_dirs_include_files(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/README.md",
+ "empty_standard_repo/src/main.c",
+ "empty_standard_repo/src/foo/foo.c",
+ "empty_standard_repo/dist/foo.o",
+ "empty_standard_repo/dist/main.o",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "/*/\n"
+ "!/src\n");
+
+ assert_is_ignored("dist/foo.o");
+ assert_is_ignored("dist/main.o");
+
+ refute_is_ignored("README.md");
+ refute_is_ignored("src/foo.c");
+ refute_is_ignored("src/foo/foo.c");
+}
+
+void test_status_ignore__subdir_ignore_all_toplevel_dirs_include_files(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/project/README.md",
+ "empty_standard_repo/project/src/main.c",
+ "empty_standard_repo/project/src/foo/foo.c",
+ "empty_standard_repo/project/dist/foo.o",
+ "empty_standard_repo/project/dist/main.o",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/project/.gitignore",
+ "/*/\n"
+ "!/src\n");
+
+ assert_is_ignored("project/dist/foo.o");
+ assert_is_ignored("project/dist/main.o");
+
+ refute_is_ignored("project/src/foo.c");
+ refute_is_ignored("project/src/foo/foo.c");
+ refute_is_ignored("project/README.md");
+}
+
+void test_status_ignore__subdir_ignore_everything_except_certain_files(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/project/README.md",
+ "empty_standard_repo/project/some_file",
+ "empty_standard_repo/project/src/main.c",
+ "empty_standard_repo/project/src/foo/foo.c",
+ "empty_standard_repo/project/dist/foo.o",
+ "empty_standard_repo/project/dist/main.o",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/project/.gitignore",
+ "/*\n"
+ "!/src\n"
+ "!README.md\n");
+
+ assert_is_ignored("project/some_file");
+ assert_is_ignored("project/dist/foo.o");
+ assert_is_ignored("project/dist/main.o");
+
+ refute_is_ignored("project/README.md");
+ refute_is_ignored("project/src/foo.c");
+ refute_is_ignored("project/src/foo/foo.c");
+}
diff --git a/tests/submodule/open.c b/tests/submodule/open.c
new file mode 100644
index 0000000..0ef01ec
--- /dev/null
+++ b/tests/submodule/open.c
@@ -0,0 +1,90 @@
+#include "clar_libgit2.h"
+#include "submodule_helpers.h"
+#include "path.h"
+
+static git_repository *g_parent;
+static git_repository *g_child;
+static git_submodule *g_module;
+
+void test_submodule_open__initialize(void)
+{
+ g_parent = setup_fixture_submod2();
+}
+
+void test_submodule_open__cleanup(void)
+{
+ git_submodule_free(g_module);
+ git_repository_free(g_child);
+ cl_git_sandbox_cleanup();
+ g_parent = NULL;
+ g_child = NULL;
+ g_module = NULL;
+}
+
+static void assert_sm_valid(git_repository *parent, git_repository *child, const char *sm_name)
+{
+ git_buf expected = GIT_BUF_INIT, actual = GIT_BUF_INIT;
+
+ /* assert working directory */
+ cl_git_pass(git_buf_joinpath(&expected, git_repository_workdir(parent), sm_name));
+ cl_git_pass(git_path_prettify_dir(&expected, expected.ptr, NULL));
+ cl_git_pass(git_buf_sets(&actual, git_repository_workdir(child)));
+ cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL));
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ git_buf_clear(&expected);
+ git_buf_clear(&actual);
+
+ /* assert common directory */
+ cl_git_pass(git_buf_joinpath(&expected, git_repository_commondir(parent), "modules"));
+ cl_git_pass(git_buf_joinpath(&expected, expected.ptr, sm_name));
+ cl_git_pass(git_path_prettify_dir(&expected, expected.ptr, NULL));
+ cl_git_pass(git_buf_sets(&actual, git_repository_commondir(child)));
+ cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL));
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ /* assert git directory */
+ cl_git_pass(git_buf_sets(&actual, git_repository_path(child)));
+ cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL));
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ git_buf_free(&expected);
+ git_buf_free(&actual);
+}
+
+void test_submodule_open__opening_via_lookup_succeeds(void)
+{
+ cl_git_pass(git_submodule_lookup(&g_module, g_parent, "sm_unchanged"));
+ cl_git_pass(git_submodule_open(&g_child, g_module));
+ assert_sm_valid(g_parent, g_child, "sm_unchanged");
+}
+
+void test_submodule_open__direct_open_succeeds(void)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged"));
+ cl_git_pass(git_repository_open(&g_child, path.ptr));
+ assert_sm_valid(g_parent, g_child, "sm_unchanged");
+
+ git_buf_free(&path);
+}
+
+void test_submodule_open__direct_open_succeeds_for_broken_sm_with_gitdir(void)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ /*
+ * This is actually not a valid submodule, but we
+ * encountered at least one occasion where the gitdir
+ * file existed inside of a submodule's gitdir. As we are
+ * now able to open these submodules correctly, we still
+ * add a test for this.
+ */
+ cl_git_mkfile("submod2/.git/modules/sm_unchanged/gitdir", ".git");
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged"));
+ cl_git_pass(git_repository_open(&g_child, path.ptr));
+ assert_sm_valid(g_parent, g_child, "sm_unchanged");
+
+ git_buf_free(&path);
+}
diff --git a/tests/threads/basic.c b/tests/threads/basic.c
index a9310bb..af60490 100644
--- a/tests/threads/basic.c
+++ b/tests/threads/basic.c
@@ -54,12 +54,6 @@
{
return param;
}
-
-static void *exit_abruptly(void *param)
-{
- git_thread_exit(param);
- return NULL;
-}
#endif
void test_threads_basic__exit(void)