summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorlmcglash <lmcglash@mathworks.com>2023-03-10 08:51:43 +0000
committerlmcglash <lmcglash@mathworks.com>2023-03-10 08:51:43 +0000
commit570ef74a07f80d8890a2bf0125d72ee42f83187e (patch)
treec3106ddd08967e3f071f1eec872a1d6db01dec37 /src
parenta9793ac643a0cd82b00970d0d6e0b67681ec3112 (diff)
parentd066d0d95c43e97df6624292f3f527f9372ca8fe (diff)
downloadlibgit2-570ef74a07f80d8890a2bf0125d72ee42f83187e.tar.gz
Merge commit 'd066d0d95c43e97df6624292f3f527f9372ca8fe'
Diffstat (limited to 'src')
-rw-r--r--src/cli/CMakeLists.txt3
-rw-r--r--src/cli/cmd_hash_object.c32
-rw-r--r--src/cli/opt.c2
-rw-r--r--src/libgit2/CMakeLists.txt16
-rw-r--r--src/libgit2/attrcache.c2
-rw-r--r--src/libgit2/blob.c6
-rw-r--r--src/libgit2/blob.h4
-rw-r--r--src/libgit2/clone.c34
-rw-r--r--src/libgit2/commit.c60
-rw-r--r--src/libgit2/commit.h32
-rw-r--r--src/libgit2/commit_graph.c39
-rw-r--r--src/libgit2/commit_graph.h3
-rw-r--r--src/libgit2/commit_list.c6
-rw-r--r--src/libgit2/config.c2
-rw-r--r--src/libgit2/config_file.c4
-rw-r--r--src/libgit2/describe.c2
-rw-r--r--src/libgit2/diff_file.c7
-rw-r--r--src/libgit2/experimental.h.in7
-rw-r--r--src/libgit2/fetch.c4
-rw-r--r--src/libgit2/index.c5
-rw-r--r--src/libgit2/indexer.c148
-rw-r--r--src/libgit2/libgit2.c19
-rw-r--r--src/libgit2/midx.c5
-rw-r--r--src/libgit2/mwindow.c7
-rw-r--r--src/libgit2/mwindow.h5
-rw-r--r--src/libgit2/object.c45
-rw-r--r--src/libgit2/object.h3
-rw-r--r--src/libgit2/odb.c36
-rw-r--r--src/libgit2/odb_loose.c34
-rw-r--r--src/libgit2/odb_pack.c138
-rw-r--r--src/libgit2/oid.h41
-rw-r--r--src/libgit2/pack-objects.c13
-rw-r--r--src/libgit2/pack.c156
-rw-r--r--src/libgit2/pack.h44
-rw-r--r--src/libgit2/push.c13
-rw-r--r--src/libgit2/refdb_fs.c109
-rw-r--r--src/libgit2/reflog.c14
-rw-r--r--src/libgit2/reflog.h3
-rw-r--r--src/libgit2/remote.c67
-rw-r--r--src/libgit2/remote.h40
-rw-r--r--src/libgit2/repository.c246
-rw-r--r--src/libgit2/repository.h9
-rw-r--r--src/libgit2/revparse.c30
-rw-r--r--src/libgit2/revwalk.c6
-rw-r--r--src/libgit2/stash.c227
-rw-r--r--src/libgit2/strarray.c1
-rw-r--r--src/libgit2/strarray.h25
-rw-r--r--src/libgit2/streams/openssl.c2
-rw-r--r--src/libgit2/streams/openssl_dynamic.c10
-rw-r--r--src/libgit2/streams/socket.c5
-rw-r--r--src/libgit2/submodule.c8
-rw-r--r--src/libgit2/sysdir.c301
-rw-r--r--src/libgit2/sysdir.h50
-rw-r--r--src/libgit2/tag.c34
-rw-r--r--src/libgit2/tag.h4
-rw-r--r--src/libgit2/transports/http.c15
-rw-r--r--src/libgit2/transports/httpclient.c9
-rw-r--r--src/libgit2/transports/httpclient.h10
-rw-r--r--src/libgit2/transports/local.c14
-rw-r--r--src/libgit2/transports/smart.c35
-rw-r--r--src/libgit2/transports/smart.h11
-rw-r--r--src/libgit2/transports/smart_pkt.c187
-rw-r--r--src/libgit2/transports/smart_protocol.c49
-rw-r--r--src/libgit2/transports/ssh.c434
-rw-r--r--src/libgit2/transports/winhttp.c13
-rw-r--r--src/libgit2/tree.c38
-rw-r--r--src/libgit2/tree.h4
-rw-r--r--src/libgit2/worktree.c5
-rw-r--r--src/util/CMakeLists.txt2
-rw-r--r--src/util/fs_path.c2
-rw-r--r--src/util/futils.c3
-rw-r--r--src/util/hash.h13
-rw-r--r--src/util/hash/openssl.c7
-rw-r--r--src/util/hash/rfc6234/sha.h112
-rw-r--r--src/util/net.c422
-rw-r--r--src/util/net.h7
-rw-r--r--src/util/posix.h2
-rw-r--r--src/util/rand.c4
-rw-r--r--src/util/regexp.c6
-rw-r--r--src/util/thread.h39
-rw-r--r--src/util/util.h1
-rw-r--r--src/util/win32/findfile.c286
-rw-r--r--src/util/win32/findfile.h22
83 files changed, 2752 insertions, 1168 deletions
diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
index 46f4d63b5..ac1659c17 100644
--- a/src/cli/CMakeLists.txt
+++ b/src/cli/CMakeLists.txt
@@ -4,7 +4,8 @@ set(CLI_INCLUDES
"${libgit2_BINARY_DIR}/include/git2"
"${libgit2_SOURCE_DIR}/src/util"
"${libgit2_SOURCE_DIR}/src/cli"
- "${libgit2_SOURCE_DIR}/include")
+ "${libgit2_SOURCE_DIR}/include"
+ "${LIBGIT2_DEPENDENCY_INCLUDES}")
if(WIN32 AND NOT CYGWIN)
file(GLOB CLI_SRC_OS win32/*.c)
diff --git a/src/cli/cmd_hash_object.c b/src/cli/cmd_hash_object.c
index a64db8823..93b980d66 100644
--- a/src/cli/cmd_hash_object.c
+++ b/src/cli/cmd_hash_object.c
@@ -49,26 +49,37 @@ static void print_help(void)
cli_opt_help_fprint(stdout, opts);
}
-static int hash_buf(git_odb *odb, git_str *buf, git_object_t type)
+static int hash_buf(
+ git_odb *odb,
+ git_str *buf,
+ git_object_t object_type,
+ git_oid_t oid_type)
{
git_oid oid;
if (!literally) {
int valid = 0;
- if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, type) < 0 || !valid)
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, object_type, oid_type) < 0 || !valid)
+ return cli_error_git();
+#else
+ GIT_UNUSED(oid_type);
+
+ if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, object_type) < 0 || !valid)
return cli_error_git();
+#endif
}
if (write_object) {
- if (git_odb_write(&oid, odb, buf->ptr, buf->size, type) < 0)
+ if (git_odb_write(&oid, odb, buf->ptr, buf->size, object_type) < 0)
return cli_error_git();
} else {
#ifdef GIT_EXPERIMENTAL_SHA256
- if (git_odb_hash(&oid, buf->ptr, buf->size, type, GIT_OID_SHA1) < 0)
+ if (git_odb_hash(&oid, buf->ptr, buf->size, object_type, GIT_OID_SHA1) < 0)
return cli_error_git();
#else
- if (git_odb_hash(&oid, buf->ptr, buf->size, type) < 0)
+ if (git_odb_hash(&oid, buf->ptr, buf->size, object_type) < 0)
return cli_error_git();
#endif
}
@@ -83,9 +94,10 @@ int cmd_hash_object(int argc, char **argv)
{
git_repository *repo = NULL;
git_odb *odb = NULL;
+ git_oid_t oid_type;
git_str buf = GIT_STR_INIT;
cli_opt invalid_opt;
- git_object_t type = GIT_OBJECT_BLOB;
+ git_object_t object_type = GIT_OBJECT_BLOB;
char **filename;
int ret = 0;
@@ -97,7 +109,7 @@ int cmd_hash_object(int argc, char **argv)
return 0;
}
- if (type_name && (type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID)
+ if (type_name && (object_type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID)
return cli_error_usage("invalid object type '%s'", type_name);
if (write_object &&
@@ -107,6 +119,8 @@ int cmd_hash_object(int argc, char **argv)
goto done;
}
+ oid_type = git_repository_oid_type(repo);
+
/*
* TODO: we're reading blobs, we shouldn't pull them all into main
* memory, we should just stream them into the odb instead.
@@ -118,7 +132,7 @@ int cmd_hash_object(int argc, char **argv)
goto done;
}
- if ((ret = hash_buf(odb, &buf, type)) != 0)
+ if ((ret = hash_buf(odb, &buf, object_type, oid_type)) != 0)
goto done;
} else {
for (filename = filenames; *filename; filename++) {
@@ -127,7 +141,7 @@ int cmd_hash_object(int argc, char **argv)
goto done;
}
- if ((ret = hash_buf(odb, &buf, type)) != 0)
+ if ((ret = hash_buf(odb, &buf, object_type, oid_type)) != 0)
goto done;
}
}
diff --git a/src/cli/opt.c b/src/cli/opt.c
index 72df5877f..62a3430d1 100644
--- a/src/cli/opt.c
+++ b/src/cli/opt.c
@@ -23,7 +23,7 @@
#include "opt.h"
#ifdef _WIN32
-# include <Windows.h>
+# include <windows.h>
#else
# include <fcntl.h>
# include <sys/ioctl.h>
diff --git a/src/libgit2/CMakeLists.txt b/src/libgit2/CMakeLists.txt
index 3462b795e..03b571212 100644
--- a/src/libgit2/CMakeLists.txt
+++ b/src/libgit2/CMakeLists.txt
@@ -15,16 +15,6 @@ set(LIBGIT2_INCLUDES
"${PROJECT_SOURCE_DIR}/src/util"
"${PROJECT_SOURCE_DIR}/include")
-if(WIN32 AND EMBED_SSH_PATH)
- file(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
- list(SORT SRC_SSH)
- target_sources(libgit2 PRIVATE ${SRC_SSH})
-
- list(APPEND LIBGIT2_SYSTEM_INCLUDES "${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\"")
- set(GIT_SSH 1)
-endif()
-
# Collect sourcefiles
file(GLOB SRC_H
"${PROJECT_SOURCE_DIR}/include/git2.h"
@@ -69,6 +59,7 @@ endif()
ide_split_sources(libgit2)
list(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:libgit2> ${LIBGIT2_DEPENDENCY_OBJECTS})
+list(APPEND LIBGIT2_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES})
target_include_directories(libgit2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_include_directories(libgit2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
@@ -86,6 +77,7 @@ set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
add_library(libgit2package ${SRC_RC} ${LIBGIT2_OBJECTS})
target_link_libraries(libgit2package ${LIBGIT2_SYSTEM_LIBS})
+target_include_directories(libgit2package SYSTEM PRIVATE ${LIBGIT2_INCLUDES})
set_target_properties(libgit2package PROPERTIES C_STANDARD 90)
set_target_properties(libgit2package PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
@@ -111,10 +103,10 @@ if(SONAME)
endif()
endif()
-pkg_build_config(NAME "${LIBGIT2_FILENAME}"
+pkg_build_config(NAME "lib${LIBGIT2_FILENAME}"
VERSION ${libgit2_VERSION}
DESCRIPTION "The git library, take 2"
- LIBS_SELF git2
+ LIBS_SELF ${LIBGIT2_FILENAME}
PRIVATE_LIBS ${LIBGIT2_PC_LIBS}
REQUIRES ${LIBGIT2_PC_REQUIRES})
diff --git a/src/libgit2/attrcache.c b/src/libgit2/attrcache.c
index b16d95c3c..405944ed1 100644
--- a/src/libgit2/attrcache.c
+++ b/src/libgit2/attrcache.c
@@ -300,7 +300,7 @@ static int attr_cache__lookup_path(
/* expand leading ~/ as needed */
if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') {
- if (! (error = git_sysdir_expand_global_file(&buf, &cfgval[2])))
+ if (! (error = git_sysdir_expand_homedir_file(&buf, &cfgval[2])))
*out = git_str_detach(&buf);
} else if (cfgval) {
*out = git__strdup(cfgval);
diff --git a/src/libgit2/blob.c b/src/libgit2/blob.c
index b1680d3a8..5cfd7474b 100644
--- a/src/libgit2/blob.c
+++ b/src/libgit2/blob.c
@@ -52,11 +52,12 @@ void git_blob__free(void *_blob)
git__free(blob);
}
-int git_blob__parse_raw(void *_blob, const char *data, size_t size)
+int git_blob__parse_raw(void *_blob, const char *data, size_t size, git_oid_t oid_type)
{
git_blob *blob = (git_blob *) _blob;
GIT_ASSERT_ARG(blob);
+ GIT_UNUSED(oid_type);
blob->raw = 1;
blob->data.raw.data = data;
@@ -64,11 +65,12 @@ int git_blob__parse_raw(void *_blob, const char *data, size_t size)
return 0;
}
-int git_blob__parse(void *_blob, git_odb_object *odb_obj)
+int git_blob__parse(void *_blob, git_odb_object *odb_obj, git_oid_t oid_type)
{
git_blob *blob = (git_blob *) _blob;
GIT_ASSERT_ARG(blob);
+ GIT_UNUSED(oid_type);
git_cached_obj_incref((git_cached_obj *)odb_obj);
blob->raw = 0;
diff --git a/src/libgit2/blob.h b/src/libgit2/blob.h
index 9a5dda225..d6c9dd99b 100644
--- a/src/libgit2/blob.h
+++ b/src/libgit2/blob.h
@@ -36,8 +36,8 @@ struct git_blob {
} while(0)
void git_blob__free(void *blob);
-int git_blob__parse(void *blob, git_odb_object *obj);
-int git_blob__parse_raw(void *blob, const char *data, size_t size);
+int git_blob__parse(void *blob, git_odb_object *obj, git_oid_t oid_type);
+int git_blob__parse_raw(void *blob, const char *data, size_t size, git_oid_t oid_type);
int git_blob__getbuf(git_str *buffer, git_blob *blob);
extern int git_blob__create_from_paths(
diff --git a/src/libgit2/clone.c b/src/libgit2/clone.c
index 6f34cb7ca..43341a493 100644
--- a/src/libgit2/clone.c
+++ b/src/libgit2/clone.c
@@ -282,7 +282,11 @@ static int update_head_to_branch(
reflog_message)) < 0)
goto cleanup;
- if ((retcode = git_remote__default_branch(&default_branch, remote)) < 0)
+ retcode = git_remote__default_branch(&default_branch, remote);
+
+ if (retcode == GIT_ENOTFOUND)
+ retcode = 0;
+ else if (retcode)
goto cleanup;
if (!git_remote__matching_refspec(remote, git_str_cstr(&default_branch)))
@@ -389,12 +393,19 @@ static int checkout_branch(git_repository *repo, git_remote *remote, const git_c
return error;
}
-static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch)
+static int clone_into(
+ git_repository *repo,
+ git_remote *_remote,
+ const git_fetch_options *opts,
+ const git_checkout_options *co_opts,
+ const char *branch)
{
int error;
git_str reflog_message = GIT_STR_INIT;
+ git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
git_fetch_options fetch_opts;
git_remote *remote;
+ git_oid_t oid_type;
GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(_remote);
@@ -412,9 +423,25 @@ static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch
if (opts->depth <= 0)
fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
-
+
+ if ((error = git_remote_connect_options__from_fetch_opts(&connect_opts, remote, &fetch_opts)) < 0)
+ goto cleanup;
+
git_str_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
+ /*
+ * Connect to the server so that we can identify the remote
+ * object format.
+ */
+
+ if ((error = git_remote_connect_ext(remote, GIT_DIRECTION_FETCH,
+ &connect_opts)) < 0)
+ goto cleanup;
+
+ if ((error = git_remote_oid_type(&oid_type, remote)) < 0 ||
+ (error = git_repository__set_objectformat(repo, oid_type)) < 0)
+ goto cleanup;
+
if ((error = git_remote_fetch(remote, NULL, &fetch_opts, git_str_cstr(&reflog_message))) != 0)
goto cleanup;
@@ -422,6 +449,7 @@ static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch
cleanup:
git_remote_free(remote);
+ git_remote_connect_options_dispose(&connect_opts);
git_str_dispose(&reflog_message);
return error;
diff --git a/src/libgit2/commit.c b/src/libgit2/commit.c
index 528d8beb7..017c60303 100644
--- a/src/libgit2/commit.c
+++ b/src/libgit2/commit.c
@@ -391,7 +391,11 @@ int git_commit_amend(
return error;
}
-static int commit_parse(git_commit *commit, const char *data, size_t size, unsigned int flags)
+static int commit_parse(
+ git_commit *commit,
+ const char *data,
+ size_t size,
+ git_commit__parse_options *opts)
{
const char *buffer_start = data, *buffer;
const char *buffer_end = buffer_start + size;
@@ -402,6 +406,7 @@ static int commit_parse(git_commit *commit, const char *data, size_t size, unsig
GIT_ASSERT_ARG(commit);
GIT_ASSERT_ARG(data);
+ GIT_ASSERT_ARG(opts);
buffer = buffer_start;
@@ -410,26 +415,29 @@ static int commit_parse(git_commit *commit, const char *data, size_t size, unsig
GIT_ERROR_CHECK_ARRAY(commit->parent_ids);
/* The tree is always the first field */
- if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
+ if (!(opts->flags & GIT_COMMIT_PARSE_QUICK)) {
if (git_object__parse_oid_header(&commit->tree_id,
&buffer, buffer_end, "tree ",
- GIT_OID_SHA1) < 0)
+ opts->oid_type) < 0)
goto bad_buffer;
} else {
- size_t tree_len = strlen("tree ") + GIT_OID_SHA1_HEXSIZE + 1;
+ size_t tree_len = strlen("tree ") + git_oid_hexsize(opts->oid_type) + 1;
+
if (buffer + tree_len > buffer_end)
goto bad_buffer;
buffer += tree_len;
}
- while (git_object__parse_oid_header(&parent_id, &buffer, buffer_end, "parent ", GIT_OID_SHA1) == 0) {
+ while (git_object__parse_oid_header(&parent_id,
+ &buffer, buffer_end, "parent ",
+ opts->oid_type) == 0) {
git_oid *new_id = git_array_alloc(commit->parent_ids);
GIT_ERROR_CHECK_ALLOC(new_id);
git_oid_cpy(new_id, &parent_id);
}
- if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
+ if (!opts || !(opts->flags & GIT_COMMIT_PARSE_QUICK)) {
commit->author = git__malloc(sizeof(git_signature));
GIT_ERROR_CHECK_ALLOC(commit->author);
@@ -453,7 +461,7 @@ static int commit_parse(git_commit *commit, const char *data, size_t size, unsig
if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n')) < 0)
return error;
- if (flags & GIT_COMMIT_PARSE_QUICK)
+ if (opts && opts->flags & GIT_COMMIT_PARSE_QUICK)
return 0;
/* Parse add'l header entries */
@@ -498,9 +506,15 @@ bad_buffer:
return GIT_EINVALID;
}
-int git_commit__parse_raw(void *commit, const char *data, size_t size)
+int git_commit__parse(
+ void *commit,
+ git_odb_object *odb_obj,
+ git_oid_t oid_type)
{
- return commit_parse(commit, data, size, 0);
+ git_commit__parse_options parse_options = {0};
+ parse_options.oid_type = oid_type;
+
+ return git_commit__parse_ext(commit, odb_obj, &parse_options);
}
static int assign_commit_parents_from_graft(git_commit *commit, git_commit_graft *graft) {
@@ -540,9 +554,28 @@ int git_commit__parse_ext(git_commit *commit, git_odb_object *odb_obj, unsigned
return assign_commit_parents_from_graft(commit, graft);
}
-int git_commit__parse(void *_commit, git_odb_object *odb_obj)
+int git_commit__parse_raw(
+ void *commit,
+ const char *data,
+ size_t size,
+ git_oid_t oid_type)
{
- return git_commit__parse_ext(_commit, odb_obj, 0);
+ git_commit__parse_options parse_options = {0};
+ parse_options.oid_type = oid_type;
+
+ return commit_parse(commit, data, size, &parse_options);
+}
+
+int git_commit__parse_ext(
+ git_commit *commit,
+ git_odb_object *odb_obj,
+ git_commit__parse_options *parse_opts)
+{
+ return commit_parse(
+ commit,
+ git_odb_object_data(odb_obj),
+ git_odb_object_size(odb_obj),
+ parse_opts);
}
#define GIT_COMMIT_GETTER(_rvalue, _name, _return, _invalid) \
@@ -1012,11 +1045,14 @@ int git_commit_create_with_signature(
git_str commit = GIT_STR_INIT;
git_commit *parsed;
git_array_oid_t parents = GIT_ARRAY_INIT;
+ git_commit__parse_options parse_opts = {0};
+
+ parse_opts.oid_type = repo->oid_type;
/* The first step is to verify that all the tree and parents exist */
parsed = git__calloc(1, sizeof(git_commit));
GIT_ERROR_CHECK_ALLOC(parsed);
- if (commit_parse(parsed, commit_content, strlen(commit_content), 0) < 0) {
+ if (commit_parse(parsed, commit_content, strlen(commit_content), &parse_opts) < 0) {
error = -1;
goto cleanup;
}
diff --git a/src/libgit2/commit.h b/src/libgit2/commit.h
index 7a2454e61..c25fee327 100644
--- a/src/libgit2/commit.h
+++ b/src/libgit2/commit.h
@@ -33,6 +33,16 @@ struct git_commit {
char *body;
};
+typedef struct {
+ git_oid_t oid_type;
+ unsigned int flags;
+} git_commit__parse_options;
+
+typedef enum {
+ /** Only parse parents and committer info */
+ GIT_COMMIT_PARSE_QUICK = (1 << 0)
+} git_commit__parse_flags;
+
int git_commit__header_field(
git_str *out,
const git_commit *commit,
@@ -56,14 +66,22 @@ int git_commit__create_buffer(
size_t parent_count,
const git_commit *parents[]);
-void git_commit__free(void *commit);
-int git_commit__parse(void *commit, git_odb_object *obj);
-int git_commit__parse_raw(void *commit, const char *data, size_t size);
+int git_commit__parse(
+ void *commit,
+ git_odb_object *obj,
+ git_oid_t oid_type);
-typedef enum {
- GIT_COMMIT_PARSE_QUICK = (1 << 0) /**< Only parse parents and committer info */
-} git_commit__parse_flags;
+int git_commit__parse_raw(
+ void *commit,
+ const char *data,
+ size_t size,
+ git_oid_t oid_type);
-int git_commit__parse_ext(git_commit *commit, git_odb_object *odb_obj, unsigned int flags);
+int git_commit__parse_ext(
+ git_commit *commit,
+ git_odb_object *odb_obj,
+ git_commit__parse_options *parse_opts);
+
+void git_commit__free(void *commit);
#endif
diff --git a/src/libgit2/commit_graph.c b/src/libgit2/commit_graph.c
index 322d24b19..bf557f7ad 100644
--- a/src/libgit2/commit_graph.c
+++ b/src/libgit2/commit_graph.c
@@ -200,8 +200,7 @@ int git_commit_graph_file_parse(
const unsigned char *chunk_hdr;
struct git_commit_graph_chunk *last_chunk;
uint32_t i;
- off64_t last_chunk_offset, chunk_offset, trailer_offset;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ uint64_t last_chunk_offset, chunk_offset, trailer_offset;
size_t checksum_size;
int error;
struct git_commit_graph_chunk chunk_oid_fanout = {0}, chunk_oid_lookup = {0},
@@ -234,16 +233,11 @@ int git_commit_graph_file_parse(
return commit_graph_error("wrong commit-graph size");
memcpy(file->checksum, (data + trailer_offset), checksum_size);
- if (git_hash_buf(checksum, data, (size_t)trailer_offset, GIT_HASH_ALGORITHM_SHA1) < 0)
- return commit_graph_error("could not calculate signature");
- if (memcmp(checksum, file->checksum, checksum_size) != 0)
- return commit_graph_error("index signature mismatch");
-
chunk_hdr = data + sizeof(struct git_commit_graph_header);
last_chunk = NULL;
for (i = 0; i < hdr->chunks; ++i, chunk_hdr += 12) {
- chunk_offset = ((off64_t)ntohl(*((uint32_t *)(chunk_hdr + 4)))) << 32
- | ((off64_t)ntohl(*((uint32_t *)(chunk_hdr + 8))));
+ chunk_offset = ((uint64_t)ntohl(*((uint32_t *)(chunk_hdr + 4)))) << 32
+ | ((uint64_t)ntohl(*((uint32_t *)(chunk_hdr + 8))));
if (chunk_offset < last_chunk_offset)
return commit_graph_error("chunks are non-monotonic");
if (chunk_offset >= trailer_offset)
@@ -331,9 +325,29 @@ error:
return error;
}
+int git_commit_graph_validate(git_commit_graph *cgraph) {
+ unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ size_t checksum_size = GIT_HASH_SHA1_SIZE;
+ size_t trailer_offset = cgraph->file->graph_map.len - checksum_size;
+
+ if (cgraph->file->graph_map.len < checksum_size)
+ return commit_graph_error("map length too small");
+
+ if (git_hash_buf(checksum, cgraph->file->graph_map.data, trailer_offset, GIT_HASH_ALGORITHM_SHA1) < 0)
+ return commit_graph_error("could not calculate signature");
+ if (memcmp(checksum, cgraph->file->checksum, checksum_size) != 0)
+ return commit_graph_error("index signature mismatch");
+
+ return 0;
+}
+
int git_commit_graph_open(git_commit_graph **cgraph_out, const char *objects_dir)
{
- return git_commit_graph_new(cgraph_out, objects_dir, true);
+ int error = git_commit_graph_new(cgraph_out, objects_dir, true);
+ if (!error) {
+ return git_commit_graph_validate(*cgraph_out);
+ }
+ return error;
}
int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *path)
@@ -524,7 +538,7 @@ int git_commit_graph_entry_find(
hi = ntohl(file->oid_fanout[(int)short_oid->id[0]]);
lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(file->oid_fanout[(int)short_oid->id[0] - 1]));
- pos = git_pack__lookup_sha1(file->oid_lookup, GIT_OID_SHA1_SIZE, lo, hi, short_oid->id);
+ pos = git_pack__lookup_id(file->oid_lookup, GIT_OID_SHA1_SIZE, lo, hi, short_oid->id, GIT_OID_SHA1);
if (pos >= 0) {
/* An object matching exactly the oid was found */
@@ -712,7 +726,8 @@ int git_commit_graph_writer_add_index_file(
if (error < 0)
goto cleanup;
- error = git_mwindow_get_pack(&p, idx_path);
+ /* TODO: SHA256 */
+ error = git_mwindow_get_pack(&p, idx_path, 0);
if (error < 0)
goto cleanup;
diff --git a/src/libgit2/commit_graph.h b/src/libgit2/commit_graph.h
index b78ab8177..517abb239 100644
--- a/src/libgit2/commit_graph.h
+++ b/src/libgit2/commit_graph.h
@@ -106,6 +106,9 @@ struct git_commit_graph {
/** Create a new commit-graph, optionally opening the underlying file. */
int git_commit_graph_new(git_commit_graph **cgraph_out, const char *objects_dir, bool open_file);
+/** Validate the checksum of a commit graph */
+int git_commit_graph_validate(git_commit_graph *cgraph);
+
/** Open and validate a commit-graph file. */
int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *path);
diff --git a/src/libgit2/commit_list.c b/src/libgit2/commit_list.c
index 511d7291f..12b329b25 100644
--- a/src/libgit2/commit_list.c
+++ b/src/libgit2/commit_list.c
@@ -124,13 +124,17 @@ static int commit_quick_parse(
{
git_oid *parent_oid;
git_commit *commit;
+ git_commit__parse_options parse_opts = {
+ GIT_OID_SHA1,
+ GIT_COMMIT_PARSE_QUICK
+ };
size_t i;
commit = git__calloc(1, sizeof(*commit));
GIT_ERROR_CHECK_ALLOC(commit);
commit->object.repo = walk->repo;
- if (git_commit__parse_ext(commit, obj, GIT_COMMIT_PARSE_QUICK) < 0) {
+ if (git_commit__parse_ext(commit, obj, &parse_opts) < 0) {
git__free(commit);
return -1;
}
diff --git a/src/libgit2/config.c b/src/libgit2/config.c
index 5c366e221..6d15a8db6 100644
--- a/src/libgit2/config.c
+++ b/src/libgit2/config.c
@@ -860,7 +860,7 @@ static int git_config__parse_path(git_str *out, const char *value)
return -1;
}
- return git_sysdir_expand_global_file(out, value[1] ? &value[2] : NULL);
+ return git_sysdir_expand_homedir_file(out, value[1] ? &value[2] : NULL);
}
return git_str_sets(out, value);
diff --git a/src/libgit2/config_file.c b/src/libgit2/config_file.c
index 66fcb8ae2..932ca7601 100644
--- a/src/libgit2/config_file.c
+++ b/src/libgit2/config_file.c
@@ -528,7 +528,7 @@ static int included_path(git_str *out, const char *dir, const char *path)
{
/* From the user's home */
if (path[0] == '~' && path[1] == '/')
- return git_sysdir_expand_global_file(out, &path[1]);
+ return git_sysdir_expand_homedir_file(out, &path[1]);
return git_fs_path_join_unrooted(out, path, dir, NULL);
}
@@ -616,7 +616,7 @@ static int do_match_gitdir(
git_fs_path_dirname_r(&pattern, cfg_file);
git_str_joinpath(&pattern, pattern.ptr, condition + 2);
} else if (condition[0] == '~' && git_fs_path_is_dirsep(condition[1]))
- git_sysdir_expand_global_file(&pattern, condition + 1);
+ git_sysdir_expand_homedir_file(&pattern, condition + 1);
else if (!git_fs_path_is_absolute(condition))
git_str_joinpath(&pattern, "**", condition);
else
diff --git a/src/libgit2/describe.c b/src/libgit2/describe.c
index 20a171aa5..3f73d87d6 100644
--- a/src/libgit2/describe.c
+++ b/src/libgit2/describe.c
@@ -8,7 +8,6 @@
#include "common.h"
#include "git2/describe.h"
-#include "git2/strarray.h"
#include "git2/diff.h"
#include "git2/status.h"
@@ -19,6 +18,7 @@
#include "refs.h"
#include "repository.h"
#include "revwalk.h"
+#include "strarray.h"
#include "tag.h"
#include "vector.h"
#include "wildmatch.h"
diff --git a/src/libgit2/diff_file.c b/src/libgit2/diff_file.c
index 5f4336658..c2d08675a 100644
--- a/src/libgit2/diff_file.c
+++ b/src/libgit2/diff_file.c
@@ -348,6 +348,13 @@ static int diff_file_content_load_workdir_file(
goto cleanup;
}
+ /* if file is empty, don't attempt to mmap or readbuffer */
+ if (fc->file->size == 0) {
+ fc->map.len = 0;
+ fc->map.data = git_str__initstr;
+ goto cleanup;
+ }
+
if ((diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0 &&
diff_file_content_binary_by_size(fc))
goto cleanup;
diff --git a/src/libgit2/experimental.h.in b/src/libgit2/experimental.h.in
index 3d6e931e2..25fb14b9d 100644
--- a/src/libgit2/experimental.h.in
+++ b/src/libgit2/experimental.h.in
@@ -1,3 +1,10 @@
+/*
+ * 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_experimental_h__
#define INCLUDE_experimental_h__
diff --git a/src/libgit2/fetch.c b/src/libgit2/fetch.c
index 2a419534e..3a574e857 100644
--- a/src/libgit2/fetch.c
+++ b/src/libgit2/fetch.c
@@ -98,7 +98,6 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts)
git_remote_head **heads;
git_refspec tagspec, head, *spec;
int error = 0;
- git_odb *odb;
size_t i, heads_len;
unsigned int remote_caps;
unsigned int oid_mask = GIT_REMOTE_CAPABILITY_TIP_OID |
@@ -129,9 +128,6 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts)
goto cleanup;
}
- if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
- goto cleanup;
-
if ((error = git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote)) < 0 ||
(error = git_remote_capabilities(&remote_caps, remote)) < 0)
goto cleanup;
diff --git a/src/libgit2/index.c b/src/libgit2/index.c
index 1821f6027..d4532c005 100644
--- a/src/libgit2/index.c
+++ b/src/libgit2/index.c
@@ -3397,7 +3397,6 @@ int git_index_add_all(
{
int error;
git_repository *repo;
- git_iterator *wditer = NULL;
git_pathspec ps;
bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0;
@@ -3423,7 +3422,6 @@ int git_index_add_all(
git_error_set_after_callback(error);
cleanup:
- git_iterator_free(wditer);
git_pathspec__clear(&ps);
return error;
@@ -3511,7 +3509,8 @@ static int index_apply_to_wd_diff(git_index *index, int action, const git_strarr
GIT_DIFF_RECURSE_UNTRACKED_DIRS;
if (flags == GIT_INDEX_ADD_FORCE)
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_RECURSE_IGNORED_DIRS;
}
if ((error = git_diff_index_to_workdir(&diff, repo, index, &opts)) < 0)
diff --git a/src/libgit2/indexer.c b/src/libgit2/indexer.c
index 62bb70393..fa55fb5ea 100644
--- a/src/libgit2/indexer.c
+++ b/src/libgit2/indexer.c
@@ -42,6 +42,7 @@ struct git_indexer {
have_delta :1,
do_fsync :1,
do_verify :1;
+ git_oid_t oid_type;
struct git_pack_header hdr;
struct git_pack_file *pack;
unsigned int mode;
@@ -55,8 +56,8 @@ struct git_indexer {
git_vector deltas;
unsigned int fanout[256];
git_hash_ctx hash_ctx;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
- char name[(GIT_HASH_SHA1_SIZE * 2) + 1];
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ char name[(GIT_HASH_MAX_SIZE * 2) + 1];
git_indexer_progress_cb progress_cb;
void *progress_payload;
char objbuf[8*1024];
@@ -68,7 +69,7 @@ struct git_indexer {
git_odb *odb;
/* Fields for calculating the packfile trailer (hash of everything before it) */
- char inbuf[GIT_OID_SHA1_SIZE];
+ char inbuf[GIT_HASH_MAX_SIZE];
size_t inbuf_len;
git_hash_ctx trailer;
};
@@ -136,17 +137,33 @@ int git_indexer_init_options(git_indexer_options *opts, unsigned int version)
}
#endif
-int git_indexer_new(
- git_indexer **out,
- const char *prefix,
- unsigned int mode,
- git_odb *odb,
- git_indexer_options *in_opts)
+GIT_INLINE(git_hash_algorithm_t) indexer_hash_algorithm(git_indexer *idx)
+{
+ switch (idx->oid_type) {
+ case GIT_OID_SHA1:
+ return GIT_HASH_ALGORITHM_SHA1;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ return GIT_HASH_ALGORITHM_SHA256;
+#endif
+ }
+
+ return GIT_HASH_ALGORITHM_NONE;
+}
+
+static int indexer_new(
+ git_indexer **out,
+ const char *prefix,
+ git_oid_t oid_type,
+ unsigned int mode,
+ git_odb *odb,
+ git_indexer_options *in_opts)
{
git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
git_indexer *idx;
git_str path = GIT_STR_INIT, tmp_path = GIT_STR_INIT;
static const char suff[] = "/pack";
+ git_hash_algorithm_t checksum_type;
int error, fd = -1;
if (in_opts)
@@ -154,14 +171,17 @@ int git_indexer_new(
idx = git__calloc(1, sizeof(git_indexer));
GIT_ERROR_CHECK_ALLOC(idx);
+ idx->oid_type = oid_type;
idx->odb = odb;
idx->progress_cb = opts.progress_cb;
idx->progress_payload = opts.progress_cb_payload;
idx->mode = mode ? mode : GIT_PACK_FILE_MODE;
git_str_init(&idx->entry_data, 0);
- if ((error = git_hash_ctx_init(&idx->hash_ctx, GIT_HASH_ALGORITHM_SHA1)) < 0 ||
- (error = git_hash_ctx_init(&idx->trailer, GIT_HASH_ALGORITHM_SHA1)) < 0 ||
+ checksum_type = indexer_hash_algorithm(idx);
+
+ if ((error = git_hash_ctx_init(&idx->hash_ctx, checksum_type)) < 0 ||
+ (error = git_hash_ctx_init(&idx->trailer, checksum_type)) < 0 ||
(error = git_oidmap_new(&idx->expected_oids)) < 0)
goto cleanup;
@@ -179,7 +199,7 @@ int git_indexer_new(
if (fd < 0)
goto cleanup;
- error = git_packfile_alloc(&idx->pack, git_str_cstr(&tmp_path));
+ error = git_packfile_alloc(&idx->pack, git_str_cstr(&tmp_path), oid_type);
git_str_dispose(&tmp_path);
if (error < 0)
@@ -208,6 +228,33 @@ cleanup:
return -1;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_indexer_new(
+ git_indexer **out,
+ const char *prefix,
+ git_oid_t oid_type,
+ git_indexer_options *opts)
+{
+ return indexer_new(
+ out,
+ prefix,
+ oid_type,
+ opts ? opts->mode : 0,
+ opts ? opts->odb : NULL,
+ opts);
+}
+#else
+int git_indexer_new(
+ git_indexer **out,
+ const char *prefix,
+ unsigned int mode,
+ git_odb *odb,
+ git_indexer_options *opts)
+{
+ return indexer_new(out, prefix, GIT_OID_SHA1, mode, odb, opts);
+}
+#endif
+
void git_indexer__set_fsync(git_indexer *idx, int do_fsync)
{
idx->do_fsync = !!do_fsync;
@@ -272,7 +319,7 @@ static int advance_delta_offset(git_indexer *idx, git_object_t type)
GIT_ASSERT_ARG(type == GIT_OBJECT_REF_DELTA || type == GIT_OBJECT_OFS_DELTA);
if (type == GIT_OBJECT_REF_DELTA) {
- idx->off += GIT_OID_SHA1_SIZE;
+ idx->off += git_oid_size(idx->oid_type);
} else {
off64_t base_off;
int error = get_delta_base(&base_off, idx->pack, &w, &idx->off, type, idx->entry_start);
@@ -356,7 +403,7 @@ static int check_object_connectivity(git_indexer *idx, const git_rawobj *obj)
obj->type != GIT_OBJECT_TAG)
return 0;
- if (git_object__from_raw(&object, obj->data, obj->len, obj->type) < 0) {
+ if (git_object__from_raw(&object, obj->data, obj->len, obj->type, idx->oid_type) < 0) {
/*
* parse_raw returns EINVALID on invalid data; downgrade
* that to a normal -1 error code.
@@ -446,7 +493,7 @@ static int store_object(git_indexer *idx)
}
#ifdef GIT_EXPERIMENTAL_SHA256
- oid.type = GIT_OID_SHA1;
+ oid.type = idx->oid_type;
#endif
entry_size = idx->off - entry_start;
@@ -468,16 +515,16 @@ static int store_object(git_indexer *idx)
goto on_error;
}
- git_oid_cpy(&pentry->sha1, &oid);
+ git_oid_cpy(&pentry->id, &oid);
pentry->offset = entry_start;
- if (git_oidmap_exists(idx->pack->idx_cache, &pentry->sha1)) {
- git_error_set(GIT_ERROR_INDEXER, "duplicate object %s found in pack", git_oid_tostr_s(&pentry->sha1));
+ if (git_oidmap_exists(idx->pack->idx_cache, &pentry->id)) {
+ git_error_set(GIT_ERROR_INDEXER, "duplicate object %s found in pack", git_oid_tostr_s(&pentry->id));
git__free(pentry);
goto on_error;
}
- if ((error = git_oidmap_set(idx->pack->idx_cache, &pentry->sha1, pentry)) < 0) {
+ if ((error = git_oidmap_set(idx->pack->idx_cache, &pentry->id, pentry)) < 0) {
git__free(pentry);
git_error_set_oom();
goto on_error;
@@ -522,8 +569,8 @@ static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_ent
pentry->offset = entry_start;
- if (git_oidmap_exists(idx->pack->idx_cache, &pentry->sha1) ||
- git_oidmap_set(idx->pack->idx_cache, &pentry->sha1, pentry) < 0) {
+ if (git_oidmap_exists(idx->pack->idx_cache, &pentry->id) ||
+ git_oidmap_set(idx->pack->idx_cache, &pentry->id, pentry) < 0) {
git_error_set(GIT_ERROR_INDEXER, "cannot insert object into pack");
return -1;
}
@@ -549,7 +596,7 @@ static int hash_and_save(git_indexer *idx, git_rawobj *obj, off64_t entry_start)
entry = git__calloc(1, sizeof(*entry));
GIT_ERROR_CHECK_ALLOC(entry);
- if (git_odb__hashobj(&oid, obj, GIT_OID_SHA1) < 0) {
+ if (git_odb__hashobj(&oid, obj, idx->oid_type) < 0) {
git_error_set(GIT_ERROR_INDEXER, "failed to hash object");
goto on_error;
}
@@ -557,7 +604,7 @@ static int hash_and_save(git_indexer *idx, git_rawobj *obj, off64_t entry_start)
pentry = git__calloc(1, sizeof(struct git_pack_entry));
GIT_ERROR_CHECK_ALLOC(pentry);
- git_oid_cpy(&pentry->sha1, &oid);
+ git_oid_cpy(&pentry->id, &oid);
git_oid_cpy(&entry->oid, &oid);
entry->crc = crc32(0L, Z_NULL, 0);
@@ -583,34 +630,38 @@ static int do_progress_callback(git_indexer *idx, git_indexer_progress *stats)
return 0;
}
-/* Hash everything but the last 20B of input */
+/* Hash everything but the checksum trailer */
static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
{
size_t to_expell, to_keep;
+ size_t oid_size = git_oid_size(idx->oid_type);
if (size == 0)
return;
- /* Easy case, dump the buffer and the data minus the last 20 bytes */
- if (size >= GIT_OID_SHA1_SIZE) {
+ /*
+ * Easy case, dump the buffer and the data minus the trailing
+ * checksum (SHA1 or SHA256).
+ */
+ if (size >= oid_size) {
git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
- git_hash_update(&idx->trailer, data, size - GIT_OID_SHA1_SIZE);
+ git_hash_update(&idx->trailer, data, size - oid_size);
- data += size - GIT_OID_SHA1_SIZE;
- memcpy(idx->inbuf, data, GIT_OID_SHA1_SIZE);
- idx->inbuf_len = GIT_OID_SHA1_SIZE;
+ data += size - oid_size;
+ memcpy(idx->inbuf, data, oid_size);
+ idx->inbuf_len = oid_size;
return;
}
/* We can just append */
- if (idx->inbuf_len + size <= GIT_OID_SHA1_SIZE) {
+ if (idx->inbuf_len + size <= oid_size) {
memcpy(idx->inbuf + idx->inbuf_len, data, size);
idx->inbuf_len += size;
return;
}
/* We need to partially drain the buffer and then append */
- to_keep = GIT_OID_SHA1_SIZE - size;
+ to_keep = oid_size - size;
to_expell = idx->inbuf_len - to_keep;
git_hash_update(&idx->trailer, idx->inbuf, to_expell);
@@ -729,12 +780,14 @@ static int read_stream_object(git_indexer *idx, git_indexer_progress *stats)
{
git_packfile_stream *stream = &idx->stream;
off64_t entry_start = idx->off;
- size_t entry_size;
+ size_t oid_size, entry_size;
git_object_t type;
git_mwindow *w = NULL;
int error;
- if (idx->pack->mwf.size <= idx->off + 20)
+ oid_size = git_oid_size(idx->oid_type);
+
+ if (idx->pack->mwf.size <= idx->off + (long long)oid_size)
return GIT_EBUFS;
if (!idx->have_stream) {
@@ -905,7 +958,7 @@ static int index_path(git_str *path, git_indexer *idx, const char *suffix)
slash--;
if (git_str_grow(path, slash + 1 + strlen(prefix) +
- GIT_OID_SHA1_HEXSIZE + strlen(suffix) + 1) < 0)
+ git_oid_hexsize(idx->oid_type) + strlen(suffix) + 1) < 0)
return -1;
git_str_truncate(path, slash);
@@ -922,7 +975,7 @@ static int index_path(git_str *path, git_indexer *idx, const char *suffix)
*/
static int seek_back_trailer(git_indexer *idx)
{
- idx->pack->mwf.size -= GIT_OID_SHA1_SIZE;
+ idx->pack->mwf.size -= git_oid_size(idx->oid_type);
return git_mwindow_free_all(&idx->pack->mwf);
}
@@ -931,15 +984,17 @@ static int inject_object(git_indexer *idx, git_oid *id)
git_odb_object *obj = NULL;
struct entry *entry = NULL;
struct git_pack_entry *pentry = NULL;
- unsigned char empty_checksum[GIT_HASH_SHA1_SIZE] = {0};
+ unsigned char empty_checksum[GIT_HASH_MAX_SIZE] = {0};
unsigned char hdr[64];
git_str buf = GIT_STR_INIT;
off64_t entry_start;
const void *data;
size_t len, hdr_len;
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
+ size_t checksum_size;
int error;
+ checksum_size = git_hash_size(indexer_hash_algorithm(idx));
+
if ((error = seek_back_trailer(idx)) < 0)
goto cleanup;
@@ -982,12 +1037,12 @@ static int inject_object(git_indexer *idx, git_oid *id)
if ((error = append_to_pack(idx, empty_checksum, checksum_size)) < 0)
goto cleanup;
- idx->pack->mwf.size += GIT_OID_SHA1_SIZE;
+ idx->pack->mwf.size += git_oid_size(idx->oid_type);
pentry = git__calloc(1, sizeof(struct git_pack_entry));
GIT_ERROR_CHECK_ALLOC(pentry);
- git_oid_cpy(&pentry->sha1, id);
+ git_oid_cpy(&pentry->id, id);
git_oid_cpy(&entry->oid, id);
idx->off = entry_start + hdr_len + len;
@@ -1045,13 +1100,13 @@ static int fix_thin_pack(git_indexer *idx, git_indexer_progress *stats)
}
/* curpos now points to the base information, which is an OID */
- base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, GIT_OID_SHA1_SIZE, &left);
+ base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, git_oid_size(idx->oid_type), &left);
if (base_info == NULL) {
git_error_set(GIT_ERROR_INDEXER, "failed to map delta information");
return -1;
}
- git_oid__fromraw(&base, base_info, GIT_OID_SHA1);
+ git_oid__fromraw(&base, base_info, idx->oid_type);
git_mwindow_close(&w);
if (has_entry(idx, &base))
@@ -1173,10 +1228,10 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
struct git_pack_idx_header hdr;
git_str filename = GIT_STR_INIT;
struct entry *entry;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
git_filebuf index_file = {0};
void *packfile_trailer;
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
+ size_t checksum_size;
bool mismatch;
if (!idx->parsed_header) {
@@ -1184,6 +1239,9 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
return -1;
}
+ checksum_size = git_hash_size(indexer_hash_algorithm(idx));
+ GIT_ASSERT(checksum_size);
+
/* Test for this before resolve_deltas(), as it plays with idx->off */
if (idx->off + (ssize_t)checksum_size < idx->pack->mwf.size) {
git_error_set(GIT_ERROR_INDEXER, "unexpected data at the end of the pack");
@@ -1274,7 +1332,7 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
/* Write out the object names (SHA-1 hashes) */
git_vector_foreach(&idx->objects, i, entry) {
- git_filebuf_write(&index_file, &entry->oid.id, GIT_OID_SHA1_SIZE);
+ git_filebuf_write(&index_file, &entry->oid.id, git_oid_size(idx->oid_type));
}
/* Write out the CRC32 values */
diff --git a/src/libgit2/libgit2.c b/src/libgit2/libgit2.c
index 2537730cf..65f19e3ca 100644
--- a/src/libgit2/libgit2.c
+++ b/src/libgit2/libgit2.c
@@ -415,6 +415,25 @@ int git_libgit2_opts(int key, ...)
git_repository__validate_ownership = (va_arg(ap, int) != 0);
break;
+ case GIT_OPT_GET_HOMEDIR:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+ const git_str *tmp;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_sysdir_get(&tmp, GIT_SYSDIR_HOME)) < 0 ||
+ (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_HOMEDIR:
+ error = git_sysdir_set(GIT_SYSDIR_HOME, va_arg(ap, const char *));
+ break;
+
case GIT_OPT_ENABLE_SHALLOW:
git_shallow__enabled = (va_arg(ap, int) != 0);
break;
diff --git a/src/libgit2/midx.c b/src/libgit2/midx.c
index 51b2d6cc7..b09055237 100644
--- a/src/libgit2/midx.c
+++ b/src/libgit2/midx.c
@@ -392,7 +392,7 @@ int git_midx_entry_find(
hi = ntohl(idx->oid_fanout[(int)short_oid->id[0]]);
lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(idx->oid_fanout[(int)short_oid->id[0] - 1]));
- pos = git_pack__lookup_sha1(idx->oid_lookup, GIT_OID_SHA1_SIZE, lo, hi, short_oid->id);
+ pos = git_pack__lookup_id(idx->oid_lookup, GIT_OID_SHA1_SIZE, lo, hi, short_oid->id, GIT_OID_SHA1);
if (pos >= 0) {
/* An object matching exactly the oid was found */
@@ -549,7 +549,8 @@ int git_midx_writer_add(
if (error < 0)
return error;
- error = git_mwindow_get_pack(&p, git_str_cstr(&idx_path_buf));
+ /* TODO: SHA256 */
+ error = git_mwindow_get_pack(&p, git_str_cstr(&idx_path_buf), 0);
git_str_dispose(&idx_path_buf);
if (error < 0)
return error;
diff --git a/src/libgit2/mwindow.c b/src/libgit2/mwindow.c
index ad649490a..b8295d9d1 100644
--- a/src/libgit2/mwindow.c
+++ b/src/libgit2/mwindow.c
@@ -61,7 +61,10 @@ int git_mwindow_global_init(void)
return git_runtime_shutdown_register(git_mwindow_global_shutdown);
}
-int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
+int git_mwindow_get_pack(
+ struct git_pack_file **out,
+ const char *path,
+ git_oid_t oid_type)
{
struct git_pack_file *pack;
char *packname;
@@ -86,7 +89,7 @@ int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
}
/* If we didn't find it, we need to create it */
- if ((error = git_packfile_alloc(&pack, path)) < 0) {
+ if ((error = git_packfile_alloc(&pack, path, oid_type)) < 0) {
git_mutex_unlock(&git__mwindow_mutex);
return error;
}
diff --git a/src/libgit2/mwindow.h b/src/libgit2/mwindow.h
index e32ab99d4..8e6df2613 100644
--- a/src/libgit2/mwindow.h
+++ b/src/libgit2/mwindow.h
@@ -48,7 +48,10 @@ void git_mwindow_close(git_mwindow **w_cursor);
extern int git_mwindow_global_init(void);
struct git_pack_file; /* just declaration to avoid cyclical includes */
-int git_mwindow_get_pack(struct git_pack_file **out, const char *path);
+int git_mwindow_get_pack(
+ struct git_pack_file **out,
+ const char *path,
+ git_oid_t oid_type);
int git_mwindow_put_pack(struct git_pack_file *pack);
#endif
diff --git a/src/libgit2/object.c b/src/libgit2/object.c
index 5ce4f1142..3d544cfff 100644
--- a/src/libgit2/object.c
+++ b/src/libgit2/object.c
@@ -27,8 +27,8 @@ typedef struct {
const char *str; /* type name string */
size_t size; /* size in bytes of the object structure */
- int (*parse)(void *self, git_odb_object *obj);
- int (*parse_raw)(void *self, const char *data, size_t size);
+ int (*parse)(void *self, git_odb_object *obj, git_oid_t oid_type);
+ int (*parse_raw)(void *self, const char *data, size_t size, git_oid_t oid_type);
void (*free)(void *self);
} git_object_def;
@@ -60,7 +60,8 @@ int git_object__from_raw(
git_object **object_out,
const char *data,
size_t size,
- git_object_t type)
+ git_object_t object_type,
+ git_oid_t oid_type)
{
git_object_def *def;
git_object *object;
@@ -71,12 +72,15 @@ int git_object__from_raw(
*object_out = NULL;
/* Validate type match */
- if (type != GIT_OBJECT_BLOB && type != GIT_OBJECT_TREE && type != GIT_OBJECT_COMMIT && type != GIT_OBJECT_TAG) {
+ if (object_type != GIT_OBJECT_BLOB &&
+ object_type != GIT_OBJECT_TREE &&
+ object_type != GIT_OBJECT_COMMIT &&
+ object_type != GIT_OBJECT_TAG) {
git_error_set(GIT_ERROR_INVALID, "the requested type is invalid");
return GIT_ENOTFOUND;
}
- if ((object_size = git_object__size(type)) == 0) {
+ if ((object_size = git_object__size(object_type)) == 0) {
git_error_set(GIT_ERROR_INVALID, "the requested type is invalid");
return GIT_ENOTFOUND;
}
@@ -85,15 +89,15 @@ int git_object__from_raw(
object = git__calloc(1, object_size);
GIT_ERROR_CHECK_ALLOC(object);
object->cached.flags = GIT_CACHE_STORE_PARSED;
- object->cached.type = type;
- if ((error = git_odb__hash(&object->cached.oid, data, size, type, GIT_OID_SHA1)) < 0)
+ object->cached.type = object_type;
+ if ((error = git_odb__hash(&object->cached.oid, data, size, object_type, oid_type)) < 0)
return error;
/* Parse raw object data */
- def = &git_objects_table[type];
+ def = &git_objects_table[object_type];
GIT_ASSERT(def->free && def->parse_raw);
- if ((error = def->parse_raw(object, data, size)) < 0) {
+ if ((error = def->parse_raw(object, data, size, oid_type)) < 0) {
def->free(object);
return error;
}
@@ -158,7 +162,7 @@ int git_object__from_odb_object(
def = &git_objects_table[odb_obj->cached.type];
GIT_ASSERT(def->free && def->parse);
- if ((error = def->parse(object, odb_obj)) < 0) {
+ if ((error = def->parse(object, odb_obj, repo->oid_type)) < 0) {
/*
* parse returns EINVALID on invalid data; downgrade
* that to a normal -1 error code.
@@ -372,12 +376,11 @@ static int dereference_object(git_object **dereferenced, git_object *obj)
static int peel_error(int error, const git_oid *oid, git_object_t type)
{
const char *type_name;
- char hex_oid[GIT_OID_SHA1_HEXSIZE + 1];
+ char hex_oid[GIT_OID_MAX_HEXSIZE + 1];
type_name = git_object_type2string(type);
- git_oid_fmt(hex_oid, oid);
- hex_oid[GIT_OID_SHA1_HEXSIZE] = '\0';
+ git_oid_nfmt(hex_oid, GIT_OID_MAX_HEXSIZE + 1, oid);
git_error_set(GIT_ERROR_OBJECT, "the git_object of id '%s' can not be "
"successfully peeled into a %s (git_object_t=%i).", hex_oid, type_name, type);
@@ -591,21 +594,29 @@ int git_object_rawcontent_is_valid(
int *valid,
const char *buf,
size_t len,
- git_object_t type)
+ git_object_t object_type
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ )
{
git_object *obj = NULL;
int error;
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_oid_t oid_type = GIT_OID_SHA1;
+#endif
+
GIT_ASSERT_ARG(valid);
GIT_ASSERT_ARG(buf);
/* Blobs are always valid; don't bother parsing. */
- if (type == GIT_OBJECT_BLOB) {
+ if (object_type == GIT_OBJECT_BLOB) {
*valid = 1;
return 0;
}
- error = git_object__from_raw(&obj, buf, len, type);
+ error = git_object__from_raw(&obj, buf, len, object_type, oid_type);
git_object_free(obj);
if (error == 0) {
@@ -626,7 +637,7 @@ int git_object__parse_oid_header(
const char *header,
git_oid_t oid_type)
{
- const size_t sha_len = GIT_OID_SHA1_HEXSIZE;
+ const size_t sha_len = git_oid_hexsize(oid_type);
const size_t header_len = strlen(header);
const char *buffer = *buffer_out;
diff --git a/src/libgit2/object.h b/src/libgit2/object.h
index 9099f8607..b6c604c81 100644
--- a/src/libgit2/object.h
+++ b/src/libgit2/object.h
@@ -33,7 +33,8 @@ int git_object__from_raw(
git_object **object_out,
const char *data,
size_t size,
- git_object_t type);
+ git_object_t object_type,
+ git_oid_t oid_type);
int git_object__init_from_odb_object(
git_object **object_out,
diff --git a/src/libgit2/odb.c b/src/libgit2/odb.c
index aa2dd3cb2..0fc48035a 100644
--- a/src/libgit2/odb.c
+++ b/src/libgit2/odb.c
@@ -684,6 +684,7 @@ int git_odb__add_default_backends(
ino_t inode;
git_odb_backend *loose, *packed;
git_odb_backend_loose_options loose_opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+ git_odb_backend_pack_options pack_opts = GIT_ODB_BACKEND_PACK_OPTIONS_INIT;
/* TODO: inodes are not really relevant on Win32, so we need to find
* a cross-platform workaround for this */
@@ -722,6 +723,7 @@ int git_odb__add_default_backends(
loose_opts.flags |= GIT_ODB_BACKEND_LOOSE_FSYNC;
loose_opts.oid_type = db->options.oid_type;
+ pack_opts.oid_type = db->options.oid_type;
/* add the loose object backend */
if (git_odb__backend_loose(&loose, objects_dir, &loose_opts) < 0 ||
@@ -729,8 +731,17 @@ int git_odb__add_default_backends(
return -1;
/* add the packed file backend */
- if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
- add_backend_internal(db, packed, git_odb__packed_priority, as_alternates, inode) < 0)
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (git_odb_backend_pack(&packed, objects_dir, &pack_opts) < 0)
+ return -1;
+#else
+ GIT_UNUSED(pack_opts);
+
+ if (git_odb_backend_pack(&packed, objects_dir) < 0)
+ return -1;
+#endif
+
+ if (add_backend_internal(db, packed, git_odb__packed_priority, as_alternates, inode) < 0)
return -1;
if (git_mutex_lock(&db->lock) < 0) {
@@ -845,6 +856,25 @@ int git_odb__open(
return 0;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+
+int git_odb_open(
+ git_odb **out,
+ const char *objects_dir,
+ const git_odb_options *opts)
+{
+ return git_odb__open(out, objects_dir, opts);
+}
+
+#else
+
+int git_odb_open(git_odb **out, const char *objects_dir)
+{
+ return git_odb__open(out, objects_dir, NULL);
+}
+
+#endif
+
int git_odb__set_caps(git_odb *odb, int caps)
{
if (caps == GIT_ODB_CAP_FROM_OWNER) {
@@ -1693,7 +1723,9 @@ int git_odb_open_wstream(
(error = hash_header(ctx, size, type)) < 0)
goto done;
+#ifdef GIT_EXPERIMENTAL_SHA256
(*stream)->oid_type = db->options.oid_type;
+#endif
(*stream)->hash_ctx = ctx;
(*stream)->declared_size = size;
(*stream)->received_bytes = 0;
diff --git a/src/libgit2/odb_loose.c b/src/libgit2/odb_loose.c
index d1abbe233..51195d357 100644
--- a/src/libgit2/odb_loose.c
+++ b/src/libgit2/odb_loose.c
@@ -1204,3 +1204,37 @@ int git_odb__backend_loose(
*backend_out = (git_odb_backend *)backend;
return 0;
}
+
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_backend_loose(
+ git_odb_backend **backend_out,
+ const char *objects_dir,
+ git_odb_backend_loose_options *opts)
+{
+ return git_odb__backend_loose(backend_out, objects_dir, opts);
+}
+#else
+int git_odb_backend_loose(
+ git_odb_backend **backend_out,
+ const char *objects_dir,
+ int compression_level,
+ int do_fsync,
+ unsigned int dir_mode,
+ unsigned int file_mode)
+{
+ git_odb_backend_loose_flag_t flags = 0;
+ git_odb_backend_loose_options opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+
+ if (do_fsync)
+ flags |= GIT_ODB_BACKEND_LOOSE_FSYNC;
+
+ opts.flags = flags;
+ opts.compression_level = compression_level;
+ opts.dir_mode = dir_mode;
+ opts.file_mode = file_mode;
+ opts.oid_type = GIT_OID_DEFAULT;
+
+ return git_odb__backend_loose(backend_out, objects_dir, &opts);
+}
+#endif
diff --git a/src/libgit2/odb_pack.c b/src/libgit2/odb_pack.c
index 49a655b44..1b1d122b0 100644
--- a/src/libgit2/odb_pack.c
+++ b/src/libgit2/odb_pack.c
@@ -26,6 +26,7 @@
struct pack_backend {
git_odb_backend parent;
+ git_odb_backend_pack_options opts;
git_midx_file *midx;
git_vector midx_packs;
git_vector packs;
@@ -95,24 +96,24 @@ struct pack_writepack {
* --------------------------------------------------
*
* # pack_backend__exists / pack_backend__exists_prefix
- * | Check if the given SHA1 oid (or a SHA1 oid prefix) exists in any of the
+ * | Check if the given oid (or an oid prefix) exists in any of the
* | packs that have been loaded for our ODB.
* |
* |-# pack_entry_find / pack_entry_find_prefix
- * | If there is a multi-pack-index present, search the SHA1 oid in that
+ * | If there is a multi-pack-index present, search the oid in that
* | index first. If it is not found there, iterate through all the unindexed
* | packs that have been preloaded (starting by the pack where the latest
* | object was found) to try to find the OID in one of them.
* |
* |-# git_midx_entry_find
- * | Search for the SHA1 oid in the multi-pack-index. See
+ * | Search for the oid in the multi-pack-index. See
* | <https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt>
* | for specifics on the multi-pack-index format and how do we find
* | entries in it.
* |
* |-# git_pack_entry_find
- * | Check the index of an individual unindexed pack to see if the SHA1
- * | OID can be found. If we can find the offset to that SHA1 inside of the
+ * | Check the index of an individual unindexed pack to see if the
+ * | OID can be found. If we can find the offset to that inside of the
* | index, that means the object is contained inside of the packfile and
* | we can stop searching. Before returning, we verify that the
* | packfile behind the index we are searching still exists on disk.
@@ -141,13 +142,13 @@ struct pack_writepack {
* --------------------------------------------------
*
* # pack_backend__read / pack_backend__read_prefix
- * | Check if the given SHA1 oid (or a SHA1 oid prefix) exists in any of the
+ * | Check if the given oid (or an oid prefix) exists in any of the
* | packs that have been loaded for our ODB. If it does, open the packfile and
* | read from it.
* |
* |-# git_packfile_unpack
* Armed with a packfile and the offset within it, we can finally unpack
- * the object pointed at by the SHA1 oid. This involves mmapping part of
+ * the object pointed at by the oid. This involves mmapping part of
* the `.pack` file, and uncompressing the object within it (if it is
* stored in the undelfitied representation), or finding a base object and
* applying some deltas to its uncompressed representation (if it is stored
@@ -177,7 +178,7 @@ static int pack_entry_find(struct git_pack_entry *e,
* a prefix of an identifier.
* Sets GIT_EAMBIGUOUS if short oid is ambiguous.
* This method assumes that len is between
- * GIT_OID_MINPREFIXLEN and GIT_OID_SHA1_HEXSIZE.
+ * GIT_OID_MINPREFIXLEN and the hexsize for the hash type.
*/
static int pack_entry_find_prefix(
struct git_pack_entry *e,
@@ -251,7 +252,7 @@ static int packfile_load__cb(void *data, git_str *path)
if (git_vector_search2(NULL, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0)
return 0;
- error = git_mwindow_get_pack(&pack, path->ptr);
+ error = git_mwindow_get_pack(&pack, path->ptr, backend->opts.oid_type);
/* ignore missing .pack file as git does */
if (error == GIT_ENOTFOUND) {
@@ -270,33 +271,34 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
{
struct git_pack_file *last_found = backend->last_found, *p;
git_midx_entry midx_entry;
+ size_t oid_hexsize = git_oid_hexsize(backend->opts.oid_type);
size_t i;
if (backend->midx &&
- git_midx_entry_find(&midx_entry, backend->midx, oid, GIT_OID_SHA1_HEXSIZE) == 0 &&
+ git_midx_entry_find(&midx_entry, backend->midx, oid, oid_hexsize) == 0 &&
midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
e->offset = midx_entry.offset;
- git_oid_cpy(&e->sha1, &midx_entry.sha1);
+ git_oid_cpy(&e->id, &midx_entry.sha1);
e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
return 0;
}
if (last_found &&
- git_pack_entry_find(e, last_found, oid, GIT_OID_SHA1_HEXSIZE) == 0)
+ git_pack_entry_find(e, last_found, oid, oid_hexsize) == 0)
return 0;
git_vector_foreach(&backend->packs, i, p) {
if (p == last_found)
continue;
- if (git_pack_entry_find(e, p, oid, GIT_OID_SHA1_HEXSIZE) == 0) {
+ if (git_pack_entry_find(e, p, oid, oid_hexsize) == 0) {
backend->last_found = p;
return 0;
}
}
return git_odb__error_notfound(
- "failed to find pack entry", oid, GIT_OID_SHA1_HEXSIZE);
+ "failed to find pack entry", oid, oid_hexsize);
}
static int pack_entry_find_prefix(
@@ -318,9 +320,9 @@ static int pack_entry_find_prefix(
return error;
if (!error && midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
e->offset = midx_entry.offset;
- git_oid_cpy(&e->sha1, &midx_entry.sha1);
+ git_oid_cpy(&e->id, &midx_entry.sha1);
e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
- git_oid_cpy(&found_full_oid, &e->sha1);
+ git_oid_cpy(&found_full_oid, &e->id);
found = true;
}
}
@@ -330,9 +332,9 @@ static int pack_entry_find_prefix(
if (error == GIT_EAMBIGUOUS)
return error;
if (!error) {
- if (found && git_oid_cmp(&e->sha1, &found_full_oid))
+ if (found && git_oid_cmp(&e->id, &found_full_oid))
return git_odb__error_ambiguous("found multiple pack entries");
- git_oid_cpy(&found_full_oid, &e->sha1);
+ git_oid_cpy(&found_full_oid, &e->id);
found = true;
}
}
@@ -345,9 +347,9 @@ static int pack_entry_find_prefix(
if (error == GIT_EAMBIGUOUS)
return error;
if (!error) {
- if (found && git_oid_cmp(&e->sha1, &found_full_oid))
+ if (found && git_oid_cmp(&e->id, &found_full_oid))
return git_odb__error_ambiguous("found multiple pack entries");
- git_oid_cpy(&found_full_oid, &e->sha1);
+ git_oid_cpy(&found_full_oid, &e->id);
found = true;
backend->last_found = p;
}
@@ -425,7 +427,10 @@ static int process_multi_pack_index_pack(
}
/* Pack was not found. Allocate a new one. */
- error = git_mwindow_get_pack(&pack, git_str_cstr(&pack_path));
+ error = git_mwindow_get_pack(
+ &pack,
+ git_str_cstr(&pack_path),
+ backend->opts.oid_type);
git_str_dispose(&pack_path);
if (error < 0)
return error;
@@ -596,32 +601,33 @@ static int pack_backend__read_prefix(
void **buffer_p,
size_t *len_p,
git_object_t *type_p,
- git_odb_backend *backend,
+ git_odb_backend *_backend,
const git_oid *short_oid,
size_t len)
{
+ struct pack_backend *backend = (struct pack_backend *)_backend;
int error = 0;
if (len < GIT_OID_MINPREFIXLEN)
error = git_odb__error_ambiguous("prefix length too short");
- else if (len >= GIT_OID_SHA1_HEXSIZE) {
+ else if (len >= git_oid_hexsize(backend->opts.oid_type)) {
/* We can fall back to regular read method */
- error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
+ error = pack_backend__read(buffer_p, len_p, type_p, _backend, short_oid);
if (!error)
git_oid_cpy(out_oid, short_oid);
} else {
struct git_pack_entry e;
git_rawobj raw = {NULL};
- if ((error = pack_entry_find_prefix(
- &e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
- (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
+ if ((error = pack_entry_find_prefix(&e,
+ backend, short_oid, len)) == 0 &&
+ (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
{
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
- git_oid_cpy(out_oid, &e.sha1);
+ git_oid_cpy(out_oid, &e.id);
}
}
@@ -642,7 +648,7 @@ static int pack_backend__exists_prefix(
struct git_pack_entry e = {0};
error = pack_entry_find_prefix(&e, pb, short_id, len);
- git_oid_cpy(out, &e.sha1);
+ git_oid_cpy(out, &e.id);
return error;
}
@@ -712,6 +718,7 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
struct pack_backend *backend;
struct pack_writepack *writepack;
+ int error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(_backend);
@@ -726,11 +733,20 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
writepack = git__calloc(1, sizeof(struct pack_writepack));
GIT_ERROR_CHECK_ALLOC(writepack);
- if (git_indexer_new(&writepack->indexer,
- backend->pack_folder, 0, odb, &opts) < 0) {
- git__free(writepack);
+#ifdef GIT_EXPERIMENTAL_SHA256
+ opts.odb = odb;
+
+ error = git_indexer_new(&writepack->indexer,
+ backend->pack_folder,
+ backend->opts.oid_type,
+ &opts);
+#else
+ error = git_indexer_new(&writepack->indexer,
+ backend->pack_folder, 0, odb, &opts);
+#endif
+
+ if (error < 0)
return -1;
- }
writepack->parent.backend = _backend;
writepack->parent.append = pack_backend__writepack_append;
@@ -840,7 +856,10 @@ static void pack_backend__free(git_odb_backend *_backend)
git__free(backend);
}
-static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
+static int pack_backend__alloc(
+ struct pack_backend **out,
+ size_t initial_size,
+ const git_odb_backend_pack_options *opts)
{
struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend));
GIT_ERROR_CHECK_ALLOC(backend);
@@ -849,12 +868,19 @@ static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
git__free(backend);
return -1;
}
+
if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) {
git_vector_free(&backend->midx_packs);
git__free(backend);
return -1;
}
+ if (opts)
+ memcpy(&backend->opts, opts, sizeof(git_odb_backend_pack_options));
+
+ if (!backend->opts.oid_type)
+ backend->opts.oid_type = GIT_OID_DEFAULT;
+
backend->parent.version = GIT_ODB_BACKEND_VERSION;
backend->parent.read = &pack_backend__read;
@@ -873,17 +899,31 @@ static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
return 0;
}
-int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_backend_one_pack(
+ git_odb_backend **backend_out,
+ const char *idx,
+ const git_odb_backend_pack_options *opts)
+#else
+int git_odb_backend_one_pack(
+ git_odb_backend **backend_out,
+ const char *idx)
+#endif
{
struct pack_backend *backend = NULL;
struct git_pack_file *packfile = NULL;
- if (pack_backend__alloc(&backend, 1) < 0)
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_odb_backend_pack_options *opts = NULL;
+#endif
+
+ git_oid_t oid_type = opts ? opts->oid_type : 0;
+
+ if (pack_backend__alloc(&backend, 1, opts) < 0)
return -1;
- if (git_mwindow_get_pack(&packfile, idx) < 0 ||
- git_vector_insert(&backend->packs, packfile) < 0)
- {
+ if (git_mwindow_get_pack(&packfile, idx, oid_type) < 0 ||
+ git_vector_insert(&backend->packs, packfile) < 0) {
pack_backend__free((git_odb_backend *)backend);
return -1;
}
@@ -892,18 +932,30 @@ int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
return 0;
}
-int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_backend_pack(
+ git_odb_backend **backend_out,
+ const char *objects_dir,
+ const git_odb_backend_pack_options *opts)
+#else
+int git_odb_backend_pack(
+ git_odb_backend **backend_out,
+ const char *objects_dir)
+#endif
{
int error = 0;
struct pack_backend *backend = NULL;
git_str path = GIT_STR_INIT;
- if (pack_backend__alloc(&backend, 8) < 0)
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_odb_backend_pack_options *opts = NULL;
+#endif
+
+ if (pack_backend__alloc(&backend, 8, opts) < 0)
return -1;
if (!(error = git_str_joinpath(&path, objects_dir, "pack")) &&
- git_fs_path_isdir(git_str_cstr(&path)))
- {
+ git_fs_path_isdir(git_str_cstr(&path))) {
backend->pack_folder = git_str_detach(&path);
error = pack_backend__refresh((git_odb_backend *)backend);
}
diff --git a/src/libgit2/oid.h b/src/libgit2/oid.h
index d775e180b..7b6b09d8b 100644
--- a/src/libgit2/oid.h
+++ b/src/libgit2/oid.h
@@ -66,6 +66,47 @@ GIT_INLINE(size_t) git_oid_hexsize(git_oid_t type)
return 0;
}
+GIT_INLINE(const char *) git_oid_type_name(git_oid_t type)
+{
+ switch (type) {
+ case GIT_OID_SHA1:
+ return "sha1";
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ return "sha256";
+#endif
+ }
+
+ return "unknown";
+}
+
+GIT_INLINE(git_oid_t) git_oid_type_fromstr(const char *name)
+{
+ if (strcmp(name, "sha1") == 0)
+ return GIT_OID_SHA1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (strcmp(name, "sha256") == 0)
+ return GIT_OID_SHA256;
+#endif
+
+ return 0;
+}
+
+GIT_INLINE(git_oid_t) git_oid_type_fromstrn(const char *name, size_t len)
+{
+ if (len == CONST_STRLEN("sha1") && strncmp(name, "sha1", len) == 0)
+ return GIT_OID_SHA1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (len == CONST_STRLEN("sha256") && strncmp(name, "sha256", len) == 0)
+ return GIT_OID_SHA256;
+#endif
+
+ return 0;
+}
+
GIT_INLINE(git_hash_algorithm_t) git_oid_algorithm(git_oid_t type)
{
switch (type) {
diff --git a/src/libgit2/pack-objects.c b/src/libgit2/pack-objects.c
index 068231649..20a5dfcbd 100644
--- a/src/libgit2/pack-objects.c
+++ b/src/libgit2/pack-objects.c
@@ -1407,7 +1407,18 @@ int git_packbuilder_write(
opts.progress_cb = progress_cb;
opts.progress_cb_payload = progress_cb_payload;
- if ((error = git_indexer_new(&indexer, path, mode, pb->odb, &opts)) < 0)
+ /* TODO: SHA256 */
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ opts.mode = mode;
+ opts.odb = pb->odb;
+
+ error = git_indexer_new(&indexer, path, GIT_OID_SHA1, &opts);
+#else
+ error = git_indexer_new(&indexer, path, mode, pb->odb, &opts);
+#endif
+
+ if (error < 0)
goto cleanup;
if (!git_repository__configmap_lookup(&t, pb->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t)
diff --git a/src/libgit2/pack.c b/src/libgit2/pack.c
index d428729ea..c30801844 100644
--- a/src/libgit2/pack.c
+++ b/src/libgit2/pack.c
@@ -32,7 +32,7 @@ static int packfile_unpack_compressed(
* Throws GIT_EAMBIGUOUSOIDPREFIX if short oid
* is ambiguous within the pack.
* This method assumes that len is between
- * GIT_OID_MINPREFIXLEN and GIT_OID_SHA1_HEXSIZE.
+ * GIT_OID_MINPREFIXLEN and the oid type's hexsize.
*/
static int pack_entry_find_offset(
off64_t *offset_out,
@@ -186,9 +186,9 @@ static int cache_add(
static void pack_index_free(struct git_pack_file *p)
{
- if (p->oids) {
- git__free(p->oids);
- p->oids = NULL;
+ if (p->ids) {
+ git__free(p->ids);
+ p->ids = NULL;
}
if (p->index_map.data) {
git_futils_mmap_free(&p->index_map);
@@ -205,6 +205,7 @@ static int pack_index_check_locked(const char *path, struct git_pack_file *p)
size_t idx_size;
struct stat st;
int error;
+
/* TODO: properly open the file without access time using O_NOATIME */
git_file fd = git_futils_open_ro(path);
if (fd < 0)
@@ -218,8 +219,7 @@ static int pack_index_check_locked(const char *path, struct git_pack_file *p)
if (!S_ISREG(st.st_mode) ||
!git__is_sizet(st.st_size) ||
- (idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20)
- {
+ (idx_size = (size_t)st.st_size) < (size_t)((4 * 256) + (p->oid_size * 2))) {
p_close(fd);
git_error_set(GIT_ERROR_ODB, "invalid pack index '%s'", path);
return -1;
@@ -242,8 +242,9 @@ static int pack_index_check_locked(const char *path, struct git_pack_file *p)
return packfile_error("unsupported index version");
}
- } else
+ } else {
version = 1;
+ }
nr = 0;
index = idx_map;
@@ -264,11 +265,11 @@ static int pack_index_check_locked(const char *path, struct git_pack_file *p)
/*
* Total size:
* - 256 index entries 4 bytes each
- * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
- * - 20-byte SHA1 of the packfile
- * - 20-byte SHA1 file checksum
+ * - 24/36-byte entries * nr (20/32 byte SHA + 4-byte offset)
+ * - 20/32-byte SHA of the packfile
+ * - 20/32-byte SHA file checksum
*/
- if (idx_size != 4*256 + nr * 24 + 20 + 20) {
+ if (idx_size != (4 * 256 + (nr * (p->oid_size + 4)) + (p->oid_size * 2))) {
git_futils_mmap_free(&p->index_map);
return packfile_error("index is corrupted");
}
@@ -277,16 +278,16 @@ static int pack_index_check_locked(const char *path, struct git_pack_file *p)
* Minimum size:
* - 8 bytes of header
* - 256 index entries 4 bytes each
- * - 20-byte sha1 entry * nr
+ * - 20/32-byte SHA entry * nr
* - 4-byte crc entry * nr
* - 4-byte offset entry * nr
- * - 20-byte SHA1 of the packfile
- * - 20-byte SHA1 file checksum
+ * - 20/32-byte SHA of the packfile
+ * - 20/32-byte SHA file checksum
* And after the 4-byte offset table might be a
* variable sized table containing 8-byte entries
* for offsets larger than 2^31.
*/
- unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
+ unsigned long min_size = 8 + (4 * 256) + (nr * (p->oid_size + 4 + 4)) + (p->oid_size * 2);
unsigned long max_size = min_size;
if (nr)
@@ -365,12 +366,12 @@ static unsigned char *pack_window_open(
* Don't allow a negative offset, as that means we've wrapped
* around.
*/
- if (offset > (p->mwf.size - 20))
+ if (offset > (p->mwf.size - p->oid_size))
goto cleanup;
if (offset < 0)
goto cleanup;
- pack_data = git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
+ pack_data = git_mwindow_open(&p->mwf, w_cursor, offset, p->oid_size, left);
cleanup:
git_mutex_unlock(&p->mwf.lock);
@@ -473,13 +474,13 @@ int git_packfile_unpack_header(
return error;
}
- /* pack_window_open() assures us we have [base, base + 20) available
- * as a range that we can look at at. (Its actually the hash
- * size that is assured.) With our object header encoding
- * the maximum deflated object size is 2^137, which is just
- * insane, so we know won't exceed what we have been given.
+ /* pack_window_open() assures us we have [base, base + oid_size)
+ * available as a range that we can look at at. (It's actually
+ * the hash size that is assured.) With our object header
+ * encoding the maximum deflated object size is 2^137, which is
+ * just insane, so we know won't exceed what we have been given.
*/
- base = git_mwindow_open(&p->mwf, w_curs, *curpos, 20, &left);
+ base = git_mwindow_open(&p->mwf, w_curs, *curpos, p->oid_size, &left);
git_mutex_unlock(&p->lock);
git_mutex_unlock(&p->mwf.lock);
if (base == NULL)
@@ -977,11 +978,12 @@ int get_delta_base(
/* Assumption: the only reason this would fail is because the file is too small */
if (base_info == NULL)
return GIT_EBUFS;
- /* pack_window_open() assured us we have [base_info, base_info + 20)
- * as a range that we can look at without walking off the
- * end of the mapped window. Its actually the hash size
- * that is assured. An OFS_DELTA longer than the hash size
- * is stupid, as then a REF_DELTA would be smaller to store.
+ /* pack_window_open() assured us we have
+ * [base_info, base_info + oid_size) as a range that we can look
+ * at without walking off the end of the mapped window. Its
+ * actually the hash size that is assured. An OFS_DELTA longer
+ * than the hash size is stupid, as then a REF_DELTA would be
+ * smaller to store.
*/
if (type == GIT_OBJECT_OFS_DELTA) {
unsigned used = 0;
@@ -1002,7 +1004,7 @@ int get_delta_base(
*curpos += used;
} else if (type == GIT_OBJECT_REF_DELTA) {
git_oid base_oid;
- git_oid__fromraw(&base_oid, base_info, GIT_OID_SHA1);
+ git_oid__fromraw(&base_oid, base_info, p->oid_type);
/* If we have the cooperative cache, search in it first */
if (p->has_cache) {
@@ -1012,7 +1014,7 @@ int get_delta_base(
if (entry->offset == 0)
return packfile_error("delta offset is zero");
- *curpos += 20;
+ *curpos += p->oid_size;
*delta_base_out = entry->offset;
return 0;
} else {
@@ -1025,9 +1027,9 @@ int get_delta_base(
}
/* The base entry _must_ be in the same pack */
- if (pack_entry_find_offset(&base_offset, &unused, p, &base_oid, GIT_OID_SHA1_HEXSIZE) < 0)
+ if (pack_entry_find_offset(&base_offset, &unused, p, &base_oid, p->oid_hexsize) < 0)
return packfile_error("base entry delta is not in the same pack");
- *curpos += 20;
+ *curpos += p->oid_size;
} else
return packfile_error("unknown object type");
@@ -1070,7 +1072,7 @@ void git_packfile_free(struct git_pack_file *p, bool unlink_packfile)
pack_index_free(p);
- git__free(p->bad_object_sha1);
+ git__free(p->bad_object_ids);
git_mutex_free(&p->bases.lock);
git_mutex_free(&p->mwf.lock);
@@ -1083,8 +1085,8 @@ static int packfile_open_locked(struct git_pack_file *p)
{
struct stat st;
struct git_pack_header hdr;
- unsigned char sha1[GIT_OID_SHA1_SIZE];
- unsigned char *idx_sha1;
+ unsigned char checksum[GIT_OID_MAX_SIZE];
+ unsigned char *idx_checksum;
if (pack_index_open_locked(p) < 0)
return git_odb__error_notfound("failed to open packfile", NULL, 0);
@@ -1131,12 +1133,13 @@ static int packfile_open_locked(struct git_pack_file *p)
/* Verify the pack matches its index. */
if (p->num_objects != ntohl(hdr.hdr_entries) ||
- p_pread(p->mwf.fd, sha1, GIT_OID_SHA1_SIZE, p->mwf.size - GIT_OID_SHA1_SIZE) < 0)
+ p_pread(p->mwf.fd, checksum, p->oid_size, p->mwf.size - p->oid_size) < 0)
goto cleanup;
- idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;
+ idx_checksum = ((unsigned char *)p->index_map.data) +
+ p->index_map.len - (p->oid_size * 2);
- if (git_oid_raw_cmp(sha1, idx_sha1, GIT_OID_SHA1_SIZE) != 0)
+ if (git_oid_raw_cmp(checksum, idx_checksum, p->oid_size) != 0)
goto cleanup;
if (git_mwindow_file_register(&p->mwf) < 0)
@@ -1171,7 +1174,10 @@ int git_packfile__name(char **out, const char *path)
return 0;
}
-int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
+int git_packfile_alloc(
+ struct git_pack_file **pack_out,
+ const char *path,
+ git_oid_t oid_type)
{
struct stat st;
struct git_pack_file *p;
@@ -1219,6 +1225,9 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
p->pack_local = 1;
p->mtime = (git_time_t)st.st_mtime;
p->index_version = -1;
+ p->oid_type = oid_type ? oid_type : GIT_OID_DEFAULT;
+ p->oid_size = (unsigned int)git_oid_size(p->oid_type);
+ p->oid_hexsize = (unsigned int)git_oid_hexsize(p->oid_type);
if (git_mutex_init(&p->lock) < 0) {
git_error_set(GIT_ERROR_OS, "failed to initialize packfile mutex");
@@ -1260,9 +1269,9 @@ static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t
end = index + p->index_map.len;
index += 4 * 256;
if (p->index_version == 1)
- return ntohl(*((uint32_t *)(index + 24 * n)));
+ return ntohl(*((uint32_t *)(index + (p->oid_size + 4) * n)));
- index += 8 + p->num_objects * (20 + 4);
+ index += 8 + p->num_objects * (p->oid_size + 4);
off32 = ntohl(*((uint32_t *)(index + 4 * n)));
if (!(off32 & 0x80000000))
return off32;
@@ -1273,7 +1282,7 @@ static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t
return -1;
return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
- ntohl(*((uint32_t *)(index + 4)));
+ ntohl(*((uint32_t *)(index + 4)));
}
static int git__memcmp4(const void *a, const void *b) {
@@ -1312,7 +1321,7 @@ int git_pack_foreach_entry(
index += 4 * 256;
- if (p->oids == NULL) {
+ if (p->ids == NULL) {
git_vector offsets, oids;
if ((error = git_vector_init(&oids, p->num_objects, NULL))) {
@@ -1326,22 +1335,25 @@ int git_pack_foreach_entry(
}
if (p->index_version > 1) {
- const unsigned char *off = index + 24 * p->num_objects;
+ const unsigned char *off = index +
+ (p->oid_size + 4) * p->num_objects;
+
for (i = 0; i < p->num_objects; i++)
git_vector_insert(&offsets, (void*)&off[4 * i]);
+
git_vector_sort(&offsets);
git_vector_foreach(&offsets, i, current)
git_vector_insert(&oids, (void*)&index[5 * (current - off)]);
} else {
for (i = 0; i < p->num_objects; i++)
- git_vector_insert(&offsets, (void*)&index[24 * i]);
+ git_vector_insert(&offsets, (void*)&index[(p->oid_size + 4) * i]);
git_vector_sort(&offsets);
git_vector_foreach(&offsets, i, current)
git_vector_insert(&oids, (void*)&current[4]);
}
git_vector_free(&offsets);
- p->oids = (unsigned char **)git_vector_detach(NULL, NULL, &oids);
+ p->ids = (unsigned char **)git_vector_detach(NULL, NULL, &oids);
}
/*
@@ -1362,7 +1374,7 @@ int git_pack_foreach_entry(
git_array_clear(oids);
GIT_ERROR_CHECK_ALLOC(oid);
}
- git_oid__fromraw(oid, p->oids[i], GIT_OID_SHA1);
+ git_oid__fromraw(oid, p->ids[i], p->oid_type);
}
git_mutex_unlock(&p->lock);
@@ -1412,10 +1424,13 @@ int git_pack_foreach_entry_offset(
/* all offsets should have been validated by pack_index_check_locked */
if (p->index_version > 1) {
- const unsigned char *offsets = index + 24 * p->num_objects;
+ const unsigned char *offsets = index +
+ (p->oid_size + 4) * p->num_objects;
const unsigned char *large_offset_ptr;
- const unsigned char *large_offsets = index + 28 * p->num_objects;
- const unsigned char *large_offsets_end = ((const unsigned char *)p->index_map.data) + p->index_map.len - 20;
+ const unsigned char *large_offsets = index +
+ (p->oid_size + 8) * p->num_objects;
+ const unsigned char *large_offsets_end = ((const unsigned char *)p->index_map.data) + p->index_map.len - p->oid_size;
+
for (i = 0; i < p->num_objects; i++) {
current_offset = ntohl(*(const uint32_t *)(offsets + 4 * i));
if (current_offset & 0x80000000) {
@@ -1428,7 +1443,7 @@ int git_pack_foreach_entry_offset(
ntohl(*((uint32_t *)(large_offset_ptr + 4)));
}
- git_oid__fromraw(&current_oid, (index + 20 * i), GIT_OID_SHA1);
+ git_oid__fromraw(&current_oid, (index + p->oid_size * i), p->oid_type);
if ((error = cb(&current_oid, current_offset, data)) != 0) {
error = git_error_set_after_callback(error);
goto cleanup;
@@ -1436,8 +1451,8 @@ int git_pack_foreach_entry_offset(
}
} else {
for (i = 0; i < p->num_objects; i++) {
- current_offset = ntohl(*(const uint32_t *)(index + 24 * i));
- git_oid__fromraw(&current_oid, (index + 24 * i + 4), GIT_OID_SHA1);
+ current_offset = ntohl(*(const uint32_t *)(index + (p->oid_size + 4) * i));
+ git_oid__fromraw(&current_oid, (index + (p->oid_size + 4) * i + 4), p->oid_type);
if ((error = cb(&current_oid, current_offset, data)) != 0) {
error = git_error_set_after_callback(error);
goto cleanup;
@@ -1450,14 +1465,20 @@ cleanup:
return error;
}
-int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned lo,
- unsigned hi, const unsigned char *oid_prefix)
+int git_pack__lookup_id(
+ const void *oid_lookup_table,
+ size_t stride,
+ unsigned lo,
+ unsigned hi,
+ const unsigned char *oid_prefix,
+ const git_oid_t oid_type)
{
const unsigned char *base = oid_lookup_table;
+ size_t oid_size = git_oid_size(oid_type);
while (lo < hi) {
unsigned mi = (lo + hi) / 2;
- int cmp = git_oid_raw_cmp(base + mi * stride, oid_prefix, GIT_OID_SHA1_SIZE);
+ int cmp = git_oid_raw_cmp(base + mi * stride, oid_prefix, oid_size);
if (!cmp)
return mi;
@@ -1512,9 +1533,9 @@ static int pack_entry_find_offset(
lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)short_oid->id[0] - 1]));
if (p->index_version > 1) {
- stride = 20;
+ stride = p->oid_size;
} else {
- stride = 24;
+ stride = p->oid_size + 4;
index += 4;
}
@@ -1523,7 +1544,8 @@ static int pack_entry_find_offset(
short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
#endif
- pos = git_pack__lookup_sha1(index, stride, lo, hi, short_oid->id);
+ pos = git_pack__lookup_id(index, stride, lo, hi,
+ short_oid->id, p->oid_type);
if (pos >= 0) {
/* An object matching exactly the oid was found */
@@ -1541,7 +1563,9 @@ static int pack_entry_find_offset(
}
}
- if (found && len != GIT_OID_SHA1_HEXSIZE && pos + 1 < (int)p->num_objects) {
+ if (found &&
+ len != p->oid_hexsize &&
+ pos + 1 < (int)p->num_objects) {
/* Check for ambiguousity */
const unsigned char *next = current + stride;
@@ -1566,13 +1590,13 @@ static int pack_entry_find_offset(
}
*offset_out = offset;
- git_oid__fromraw(found_oid, current, GIT_OID_SHA1);
+ git_oid__fromraw(found_oid, current, p->oid_type);
#ifdef INDEX_DEBUG_LOOKUP
{
- unsigned char hex_sha1[GIT_OID_SHA1_HEXSIZE + 1];
+ char hex_sha1[p->oid_hexsize + 1];
git_oid_fmt(hex_sha1, found_oid);
- hex_sha1[GIT_OID_SHA1_HEXSIZE] = '\0';
+ hex_sha1[p->oid_hexsize] = '\0';
printf("found lo=%d %s\n", lo, hex_sha1);
}
#endif
@@ -1594,10 +1618,10 @@ int git_pack_entry_find(
GIT_ASSERT_ARG(p);
- if (len == GIT_OID_SHA1_HEXSIZE && p->num_bad_objects) {
+ if (len == p->oid_hexsize && p->num_bad_objects) {
unsigned i;
for (i = 0; i < p->num_bad_objects; i++)
- if (git_oid__cmp(short_oid, &p->bad_object_sha1[i]) == 0)
+ if (git_oid__cmp(short_oid, &p->bad_object_ids[i]) == 0)
return packfile_error("bad object found in packfile");
}
@@ -1630,6 +1654,6 @@ int git_pack_entry_find(
e->offset = offset;
e->p = p;
- git_oid_cpy(&e->sha1, &found_oid);
+ git_oid_cpy(&e->id, &found_oid);
return 0;
}
diff --git a/src/libgit2/pack.h b/src/libgit2/pack.h
index d90588f79..1a9eb14b2 100644
--- a/src/libgit2/pack.h
+++ b/src/libgit2/pack.h
@@ -99,13 +99,19 @@ struct git_pack_file {
uint32_t num_objects;
uint32_t num_bad_objects;
- git_oid *bad_object_sha1; /* array of git_oid */
+ git_oid *bad_object_ids; /* array of git_oid */
+
+ git_oid_t oid_type;
+ unsigned oid_hexsize:7,
+ oid_size:6,
+ pack_local:1,
+ pack_keep:1,
+ has_cache:1;
int index_version;
git_time_t mtime;
- unsigned pack_local:1, pack_keep:1, has_cache:1;
git_oidmap *idx_cache;
- unsigned char **oids;
+ unsigned char **ids;
git_pack_cache bases; /* delta base cache */
@@ -116,21 +122,26 @@ struct git_pack_file {
};
/**
- * Return the position where an OID (or a prefix) would be inserted within the
- * OID Lookup Table of an .idx file. This performs binary search between the lo
- * and hi indices.
+ * Return the position where an OID (or a prefix) would be inserted within
+ * the OID Lookup Table of an .idx file. This performs binary search
+ * between the lo and hi indices.
*
- * The stride parameter is provided because .idx files version 1 store the OIDs
- * interleaved with the 4-byte file offsets of the objects within the .pack
- * file (stride = 24), whereas files with version 2 store them in a contiguous
- * flat array (stride = 20).
+ * The stride parameter is provided because .idx files version 1 store the
+ * OIDs interleaved with the 4-byte file offsets of the objects within the
+ * .pack file (stride = oid_size + 4), whereas files with version 2 store
+ * them in a contiguous flat array (stride = oid_size).
*/
-int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned lo,
- unsigned hi, const unsigned char *oid_prefix);
+int git_pack__lookup_id(
+ const void *id_lookup_table,
+ size_t stride,
+ unsigned lo,
+ unsigned hi,
+ const unsigned char *id_prefix,
+ const git_oid_t oid_type);
struct git_pack_entry {
off64_t offset;
- git_oid sha1;
+ git_oid id;
struct git_pack_file *p;
};
@@ -174,12 +185,15 @@ int get_delta_base(
off64_t delta_obj_offset);
void git_packfile_free(struct git_pack_file *p, bool unlink_packfile);
-int git_packfile_alloc(struct git_pack_file **pack_out, const char *path);
+int git_packfile_alloc(
+ struct git_pack_file **pack_out,
+ const char *path,
+ git_oid_t oid_type);
int git_pack_entry_find(
struct git_pack_entry *e,
struct git_pack_file *p,
- const git_oid *short_oid,
+ const git_oid *short_id,
size_t len);
int git_pack_foreach_entry(
struct git_pack_file *p,
diff --git a/src/libgit2/push.c b/src/libgit2/push.c
index d477b4f0d..e25681870 100644
--- a/src/libgit2/push.c
+++ b/src/libgit2/push.c
@@ -385,11 +385,18 @@ static int calculate_work(git_push *push)
git_vector_foreach(&push->specs, i, spec) {
if (spec->refspec.src && spec->refspec.src[0]!= '\0') {
/* This is a create or update. Local ref must exist. */
- if (git_reference_name_to_id(
- &spec->loid, push->repo, spec->refspec.src) < 0) {
- git_error_set(GIT_ERROR_REFERENCE, "no such reference '%s'", spec->refspec.src);
+
+ git_object *obj;
+ int error = git_revparse_single(&obj, push->repo, spec->refspec.src);
+
+ if (error < 0) {
+ git_object_free(obj);
+ git_error_set(GIT_ERROR_REFERENCE, "src refspec %s does not match any", spec->refspec.src);
return -1;
}
+
+ git_oid_cpy(&spec->loid, git_object_id(obj));
+ git_object_free(obj);
}
/* Remote ref may or may not (e.g. during create) already exist. */
diff --git a/src/libgit2/refdb_fs.c b/src/libgit2/refdb_fs.c
index 43283b3e4..9ce1a9608 100644
--- a/src/libgit2/refdb_fs.c
+++ b/src/libgit2/refdb_fs.c
@@ -60,15 +60,17 @@ typedef struct refdb_fs_backend {
/* path to common objects' directory */
char *commonpath;
- git_sortedcache *refcache;
+ git_oid_t oid_type;
+
+ int fsync : 1,
+ sorted : 1;
int peeling_mode;
git_iterator_flag_t iterator_flags;
uint32_t direach_flags;
- int fsync;
+ git_sortedcache *refcache;
git_map packed_refs_map;
git_mutex prlock; /* protect packed_refs_map */
git_futils_filestamp packed_refs_stamp;
- bool sorted;
} refdb_fs_backend;
static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name);
@@ -113,6 +115,7 @@ static int packed_reload(refdb_fs_backend *backend)
{
int error;
git_str packedrefs = GIT_STR_INIT;
+ size_t oid_hexsize = git_oid_hexsize(backend->oid_type);
char *scan, *eof, *eol;
if (!backend->gitpath)
@@ -158,9 +161,9 @@ static int packed_reload(refdb_fs_backend *backend)
/* parse "<OID> <refname>\n" */
- if (git_oid__fromstr(&oid, scan, GIT_OID_SHA1) < 0)
+ if (git_oid__fromstr(&oid, scan, backend->oid_type) < 0)
goto parse_failed;
- scan += GIT_OID_SHA1_HEXSIZE;
+ scan += oid_hexsize;
if (*scan++ != ' ')
goto parse_failed;
@@ -179,9 +182,9 @@ static int packed_reload(refdb_fs_backend *backend)
/* look for optional "^<OID>\n" */
if (*scan == '^') {
- if (git_oid__fromstr(&oid, scan + 1, GIT_OID_SHA1) < 0)
+ if (git_oid__fromstr(&oid, scan + 1, backend->oid_type) < 0)
goto parse_failed;
- scan += GIT_OID_SHA1_HEXSIZE + 1;
+ scan += oid_hexsize + 1;
if (scan < eof) {
if (!(eol = strchr(scan, '\n')))
@@ -214,19 +217,23 @@ parse_failed:
}
static int loose_parse_oid(
- git_oid *oid, const char *filename, git_str *file_content)
+ git_oid *oid,
+ const char *filename,
+ git_str *file_content,
+ git_oid_t oid_type)
{
const char *str = git_str_cstr(file_content);
+ size_t oid_hexsize = git_oid_hexsize(oid_type);
- if (git_str_len(file_content) < GIT_OID_SHA1_HEXSIZE)
+ if (git_str_len(file_content) < oid_hexsize)
goto corrupted;
/* we need to get 40 OID characters from the file */
- if (git_oid__fromstr(oid, str, GIT_OID_SHA1) < 0)
+ if (git_oid__fromstr(oid, str, oid_type) < 0)
goto corrupted;
/* If the file is longer than 40 chars, the 41st must be a space */
- str += GIT_OID_SHA1_HEXSIZE;
+ str += oid_hexsize;
if (*str == '\0' || git__isspace(*str))
return 0;
@@ -266,7 +273,7 @@ static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name)
goto done;
/* parse OID from file */
- if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0)
+ if ((error = loose_parse_oid(&oid, name, &ref_file, backend->oid_type)) < 0)
goto done;
if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
@@ -437,7 +444,7 @@ static int loose_lookup(
} else {
git_oid oid;
- if (!(error = loose_parse_oid(&oid, ref_name, &ref_file)) &&
+ if (!(error = loose_parse_oid(&oid, ref_name, &ref_file, backend->oid_type)) &&
out != NULL)
*out = git_reference__alloc(ref_name, &oid, NULL);
}
@@ -615,19 +622,24 @@ static const char *end_of_record(const char *p, const char *end)
return p;
}
-static int
-cmp_record_to_refname(const char *rec, size_t data_end, const char *ref_name)
+static int cmp_record_to_refname(
+ const char *rec,
+ size_t data_end,
+ const char *ref_name,
+ git_oid_t oid_type)
{
const size_t ref_len = strlen(ref_name);
int cmp_val;
const char *end;
+ size_t oid_hexsize = git_oid_hexsize(oid_type);
- rec += GIT_OID_SHA1_HEXSIZE + 1; /* <oid> + space */
- if (data_end < GIT_OID_SHA1_HEXSIZE + 3) {
- /* an incomplete (corrupt) record is treated as less than ref_name */
+ rec += oid_hexsize + 1; /* <oid> + space */
+
+ /* an incomplete (corrupt) record is treated as less than ref_name */
+ if (data_end < oid_hexsize + 3)
return -1;
- }
- data_end -= GIT_OID_SHA1_HEXSIZE + 1;
+
+ data_end -= oid_hexsize + 1;
end = memchr(rec, '\n', data_end);
if (end)
@@ -675,6 +687,7 @@ static int packed_lookup(
{
int error = 0;
const char *left, *right, *data_end;
+ size_t oid_hexsize = git_oid_hexsize(backend->oid_type);
if ((error = packed_map_check(backend)) < 0)
return error;
@@ -698,7 +711,7 @@ static int packed_lookup(
mid = left + (right - left) / 2;
rec = start_of_record(left, mid);
- compare = cmp_record_to_refname(rec, data_end - rec, ref_name);
+ compare = cmp_record_to_refname(rec, data_end - rec, ref_name, backend->oid_type);
if (compare < 0) {
left = end_of_record(mid, right);
@@ -708,11 +721,11 @@ static int packed_lookup(
const char *eol;
git_oid oid, peel, *peel_ptr = NULL;
- if (data_end - rec < GIT_OID_SHA1_HEXSIZE ||
- git_oid__fromstr(&oid, rec, GIT_OID_SHA1) < 0) {
+ if (data_end - rec < (long)oid_hexsize ||
+ git_oid__fromstr(&oid, rec, backend->oid_type) < 0) {
goto parse_failed;
}
- rec += GIT_OID_SHA1_HEXSIZE + 1;
+ rec += oid_hexsize + 1;
if (!(eol = memchr(rec, '\n', data_end - rec))) {
goto parse_failed;
}
@@ -724,8 +737,8 @@ static int packed_lookup(
if (*rec == '^') {
rec++;
- if (data_end - rec < GIT_OID_SHA1_HEXSIZE ||
- git_oid__fromstr(&peel, rec, GIT_OID_SHA1) < 0) {
+ if (data_end - rec < (long)oid_hexsize ||
+ git_oid__fromstr(&peel, rec, backend->oid_type) < 0) {
goto parse_failed;
}
peel_ptr = &peel;
@@ -1108,7 +1121,7 @@ static int loose_commit(git_filebuf *file, const git_reference *ref)
GIT_ASSERT_ARG(ref);
if (ref->type == GIT_REFERENCE_DIRECT) {
- char oid[GIT_OID_SHA1_HEXSIZE + 1];
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
git_filebuf_printf(file, "%s\n", oid);
@@ -1224,7 +1237,7 @@ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
*/
static int packed_write_ref(struct packref *ref, git_filebuf *file)
{
- char oid[GIT_OID_SHA1_HEXSIZE + 1];
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
git_oid_nfmt(oid, sizeof(oid), &ref->oid);
/*
@@ -1238,7 +1251,7 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
* The required peels have already been loaded into `ref->peel_target`.
*/
if (ref->flags & PACKREF_HAS_PEEL) {
- char peel[GIT_OID_SHA1_HEXSIZE + 1];
+ char peel[GIT_OID_MAX_HEXSIZE + 1];
git_oid_nfmt(peel, sizeof(peel), &ref->peel);
if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
@@ -1302,7 +1315,7 @@ static int packed_remove_loose(refdb_fs_backend *backend)
continue;
/* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */
- if (loose_parse_oid(&current_id, lock.path_original, &ref_content) < 0)
+ if (loose_parse_oid(&current_id, lock.path_original, &ref_content, backend->oid_type) < 0)
continue;
/* If the ref moved since we packed it, we must not delete it */
@@ -1891,7 +1904,10 @@ done:
return out;
}
-static int reflog_alloc(git_reflog **reflog, const char *name)
+static int reflog_alloc(
+ git_reflog **reflog,
+ const char *name,
+ git_oid_t oid_type)
{
git_reflog *log;
@@ -1903,6 +1919,8 @@ static int reflog_alloc(git_reflog **reflog, const char *name)
log->ref_name = git__strdup(name);
GIT_ERROR_CHECK_ALLOC(log->ref_name);
+ log->oid_type = oid_type;
+
if (git_vector_init(&log->entries, 0, NULL) < 0) {
git__free(log->ref_name);
git__free(log);
@@ -2032,7 +2050,10 @@ static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *nam
return has_reflog(backend->repo, name);
}
-static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
+static int refdb_reflog_fs__read(
+ git_reflog **out,
+ git_refdb_backend *_backend,
+ const char *name)
{
int error = -1;
git_str log_path = GIT_STR_INIT;
@@ -2048,7 +2069,7 @@ static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend,
backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
repo = backend->repo;
- if (reflog_alloc(&log, name) < 0)
+ if (reflog_alloc(&log, name, backend->oid_type) < 0)
return -1;
if (reflog_path(&log_path, repo, name) < 0)
@@ -2086,11 +2107,11 @@ static int serialize_reflog_entry(
const git_signature *committer,
const char *msg)
{
- char raw_old[GIT_OID_SHA1_HEXSIZE+1];
- char raw_new[GIT_OID_SHA1_HEXSIZE+1];
+ char raw_old[GIT_OID_MAX_HEXSIZE + 1];
+ char raw_new[GIT_OID_MAX_HEXSIZE + 1];
- git_oid_tostr(raw_old, GIT_OID_SHA1_HEXSIZE+1, oid_old);
- git_oid_tostr(raw_new, GIT_OID_SHA1_HEXSIZE+1, oid_new);
+ git_oid_tostr(raw_old, GIT_OID_MAX_HEXSIZE + 1, oid_old);
+ git_oid_tostr(raw_new, GIT_OID_MAX_HEXSIZE + 1, oid_new);
git_str_clear(buf);
@@ -2189,10 +2210,16 @@ success:
}
/* Append to the reflog, must be called under reference lock */
-static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *who, const char *message)
+static int reflog_append(
+ refdb_fs_backend *backend,
+ const git_reference *ref,
+ const git_oid *old,
+ const git_oid *new,
+ const git_signature *who,
+ const char *message)
{
int error, is_symbolic, open_flags;
- git_oid old_id = GIT_OID_SHA1_ZERO, new_id = GIT_OID_SHA1_ZERO;
+ git_oid old_id, new_id;
git_str buf = GIT_STR_INIT, path = GIT_STR_INIT;
git_repository *repo = backend->repo;
@@ -2206,6 +2233,9 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co
/* From here on is_symbolic also means that it's HEAD */
+ git_oid_clear(&old_id, backend->oid_type);
+ git_oid_clear(&new_id, backend->oid_type);
+
if (old) {
git_oid_cpy(&old_id, old);
} else {
@@ -2402,6 +2432,7 @@ int git_refdb_backend_fs(
goto fail;
backend->repo = repository;
+ backend->oid_type = repository->oid_type;
if (repository->gitdir) {
backend->gitpath = setup_namespace(repository, repository->gitdir);
diff --git a/src/libgit2/reflog.c b/src/libgit2/reflog.c
index eb0b7e48c..86d4355e3 100644
--- a/src/libgit2/reflog.c
+++ b/src/libgit2/reflog.c
@@ -71,7 +71,11 @@ int git_reflog_write(git_reflog *reflog)
return db->backend->reflog_write(db->backend, reflog);
}
-int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg)
+int git_reflog_append(
+ git_reflog *reflog,
+ const git_oid *new_oid,
+ const git_signature *committer,
+ const char *msg)
{
const git_reflog_entry *previous;
git_reflog_entry *entry;
@@ -104,7 +108,7 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_sign
previous = git_reflog_entry_byindex(reflog, 0);
if (previous == NULL)
- git_oid__fromstr(&entry->oid_old, GIT_OID_SHA1_HEXZERO, GIT_OID_SHA1);
+ git_oid_clear(&entry->oid_old, reflog->oid_type);
else
git_oid_cpy(&entry->oid_old, &previous->oid_cur);
@@ -218,12 +222,8 @@ int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
/* If the oldest entry has just been removed... */
if (idx == entrycount - 1) {
- git_oid zero = GIT_OID_SHA1_ZERO;
-
/* ...clear the oid_old member of the "new" oldest entry */
- if (git_oid_cpy(&entry->oid_old, &zero) < 0)
- return -1;
-
+ git_oid_clear(&entry->oid_old, reflog->oid_type);
return 0;
}
diff --git a/src/libgit2/reflog.h b/src/libgit2/reflog.h
index 50d1056ed..bc9878598 100644
--- a/src/libgit2/reflog.h
+++ b/src/libgit2/reflog.h
@@ -16,8 +16,6 @@
#define GIT_REFLOG_DIR_MODE 0777
#define GIT_REFLOG_FILE_MODE 0666
-#define GIT_REFLOG_SIZE_MIN (2*GIT_OID_SHA1_HEXSIZE+2+17)
-
struct git_reflog_entry {
git_oid oid_old;
git_oid oid_cur;
@@ -30,6 +28,7 @@ struct git_reflog_entry {
struct git_reflog {
git_refdb *db;
char *ref_name;
+ git_oid_t oid_type;
git_vector entries;
};
diff --git a/src/libgit2/remote.c b/src/libgit2/remote.c
index 63346e941..e1bc6c694 100644
--- a/src/libgit2/remote.c
+++ b/src/libgit2/remote.c
@@ -17,6 +17,7 @@
#include "fetchhead.h"
#include "push.h"
#include "proxy.h"
+#include "strarray.h"
#include "git2/config.h"
#include "git2/types.h"
@@ -1027,6 +1028,24 @@ int git_remote_capabilities(unsigned int *out, git_remote *remote)
return remote->transport->capabilities(out, remote->transport);
}
+int git_remote_oid_type(git_oid_t *out, git_remote *remote)
+{
+ GIT_ASSERT_ARG(remote);
+
+ if (!remote->transport) {
+ git_error_set(GIT_ERROR_NET, "this remote has never connected");
+ *out = 0;
+ return -1;
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ return remote->transport->oid_type(out, remote->transport);
+#else
+ *out = GIT_OID_SHA1;
+ return 0;
+#endif
+}
+
static int lookup_config(char **out, git_config *cfg, const char *name)
{
git_config_entry *ce = NULL;
@@ -1226,24 +1245,6 @@ static int ls_to_vector(git_vector *out, git_remote *remote)
return 0;
}
-#define copy_opts(out, in) \
- if (in) { \
- (out)->callbacks = (in)->callbacks; \
- (out)->proxy_opts = (in)->proxy_opts; \
- (out)->custom_headers = (in)->custom_headers; \
- (out)->follow_redirects = (in)->follow_redirects; \
- }
-
-GIT_INLINE(int) connect_opts_from_fetch_opts(
- git_remote_connect_options *out,
- git_remote *remote,
- const git_fetch_options *fetch_opts)
-{
- git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
- copy_opts(&tmp, fetch_opts);
- return git_remote_connect_options_normalize(out, remote->repo, &tmp);
-}
-
static int connect_or_reset_options(
git_remote *remote,
int direction,
@@ -1331,7 +1332,8 @@ int git_remote_download(
return -1;
}
- if (connect_opts_from_fetch_opts(&connect_opts, remote, opts) < 0)
+ if (git_remote_connect_options__from_fetch_opts(&connect_opts,
+ remote, opts) < 0)
return -1;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
@@ -1351,6 +1353,8 @@ int git_remote_fetch(
bool prune = false;
git_str reflog_msg_buf = GIT_STR_INIT;
git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ unsigned int capabilities;
+ git_oid_t oid_type;
GIT_ASSERT_ARG(remote);
@@ -1359,7 +1363,8 @@ int git_remote_fetch(
return -1;
}
- if (connect_opts_from_fetch_opts(&connect_opts, remote, opts) < 0)
+ if (git_remote_connect_options__from_fetch_opts(&connect_opts,
+ remote, opts) < 0)
return -1;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
@@ -1370,6 +1375,10 @@ int git_remote_fetch(
tagopt = opts->download_tags;
}
+ if ((error = git_remote_capabilities(&capabilities, remote)) < 0 ||
+ (error = git_remote_oid_type(&oid_type, remote)) < 0)
+ return error;
+
/* Connect and download everything */
error = git_remote__download(remote, refspecs, opts);
@@ -1725,7 +1734,7 @@ static int update_ref(
const git_remote_callbacks *callbacks)
{
git_reference *ref;
- git_oid old_id;
+ git_oid old_id = GIT_OID_SHA1_ZERO;
int error;
error = git_reference_name_to_id(&old_id, remote->repo, ref_name);
@@ -2902,16 +2911,6 @@ done:
return error;
}
-GIT_INLINE(int) connect_opts_from_push_opts(
- git_remote_connect_options *out,
- git_remote *remote,
- const git_push_options *push_opts)
-{
- git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
- copy_opts(&tmp, push_opts);
- return git_remote_connect_options_normalize(out, remote->repo, &tmp);
-}
-
int git_remote_upload(
git_remote *remote,
const git_strarray *refspecs,
@@ -2930,7 +2929,8 @@ int git_remote_upload(
return -1;
}
- if ((error = connect_opts_from_push_opts(&connect_opts, remote, opts)) < 0)
+ if ((error = git_remote_connect_options__from_push_opts(
+ &connect_opts, remote, opts)) < 0)
goto cleanup;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_PUSH, &connect_opts)) < 0)
@@ -2991,7 +2991,8 @@ int git_remote_push(
return -1;
}
- if (connect_opts_from_push_opts(&connect_opts, remote, opts) < 0)
+ if (git_remote_connect_options__from_push_opts(&connect_opts,
+ remote, opts) < 0)
return -1;
if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
diff --git a/src/libgit2/remote.h b/src/libgit2/remote.h
index df3aea29d..9e089be38 100644
--- a/src/libgit2/remote.h
+++ b/src/libgit2/remote.h
@@ -17,6 +17,7 @@
#include "refspec.h"
#include "vector.h"
#include "net.h"
+#include "proxy.h"
#define GIT_REMOTE_ORIGIN "origin"
@@ -57,5 +58,44 @@ int git_remote_connect_options_normalize(
const git_remote_connect_options *src);
int git_remote_capabilities(unsigned int *out, git_remote *remote);
+int git_remote_oid_type(git_oid_t *out, git_remote *remote);
+
+
+#define git_remote_connect_options__copy_opts(out, in) \
+ if (in) { \
+ (out)->callbacks = (in)->callbacks; \
+ (out)->proxy_opts = (in)->proxy_opts; \
+ (out)->custom_headers = (in)->custom_headers; \
+ (out)->follow_redirects = (in)->follow_redirects; \
+ }
+
+GIT_INLINE(int) git_remote_connect_options__from_fetch_opts(
+ git_remote_connect_options *out,
+ git_remote *remote,
+ const git_fetch_options *fetch_opts)
+{
+ git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_remote_connect_options__copy_opts(&tmp, fetch_opts);
+ return git_remote_connect_options_normalize(out, remote->repo, &tmp);
+}
+
+GIT_INLINE(int) git_remote_connect_options__from_push_opts(
+ git_remote_connect_options *out,
+ git_remote *remote,
+ const git_push_options *push_opts)
+{
+ git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_remote_connect_options__copy_opts(&tmp, push_opts);
+ return git_remote_connect_options_normalize(out, remote->repo, &tmp);
+}
+
+#undef git_remote_connect_options__copy_opts
+
+GIT_INLINE(void) git_remote_connect_options__dispose(
+ git_remote_connect_options *opts)
+{
+ git_proxy_options_dispose(&opts->proxy_opts);
+ git_strarray_dispose(&opts->custom_headers);
+}
#endif
diff --git a/src/libgit2/repository.c b/src/libgit2/repository.c
index 0eb16c223..bb6ae1b0f 100644
--- a/src/libgit2/repository.c
+++ b/src/libgit2/repository.c
@@ -68,6 +68,7 @@ static const struct {
static int check_repositoryformatversion(int *version, git_config *config);
static int check_extensions(git_config *config, int version);
static int load_global_config(git_config **config);
+static int load_objectformat(git_repository *repo, git_config *config);
#define GIT_COMMONDIR_FILE "commondir"
#define GIT_GITDIR_FILE "gitdir"
@@ -76,8 +77,8 @@ static int load_global_config(git_config **config);
#define GIT_BRANCH_DEFAULT "master"
-#define GIT_REPO_VERSION 0
-#define GIT_REPO_MAX_VERSION 1
+#define GIT_REPO_VERSION_DEFAULT 0
+#define GIT_REPO_VERSION_MAX 1
git_str git_repository__reserved_names_win32[] = {
{ DOT_GIT, 0, CONST_STRLEN(DOT_GIT) },
@@ -243,7 +244,7 @@ GIT_INLINE(int) validate_repo_path(git_str *path)
*/
static size_t suffix_len =
CONST_STRLEN("objects/pack/pack-.pack.lock") +
- GIT_OID_SHA1_HEXSIZE;
+ GIT_OID_MAX_HEXSIZE;
return git_fs_path_validate_str_length_with_suffix(
path, suffix_len);
@@ -498,12 +499,47 @@ static int validate_ownership_cb(const git_config_entry *entry, void *payload)
{
validate_ownership_data *data = payload;
- if (strcmp(entry->value, "") == 0)
+ if (strcmp(entry->value, "") == 0) {
*data->is_safe = false;
-
- if (git_fs_path_prettify_dir(&data->tmp, entry->value, NULL) == 0 &&
- strcmp(data->tmp.ptr, data->repo_path) == 0)
+ } else if (strcmp(entry->value, "*") == 0) {
*data->is_safe = true;
+ } else {
+ const char *test_path = entry->value;
+
+#ifdef GIT_WIN32
+ /*
+ * Git for Windows does some truly bizarre things with
+ * paths that start with a forward slash; and expects you
+ * to escape that with `%(prefix)`. This syntax generally
+ * means to add the prefix that Git was installed to -- eg
+ * `/usr/local` -- unless it's an absolute path, in which
+ * case the leading `%(prefix)/` is just removed. And Git
+ * for Windows expects you to use this syntax for absolute
+ * Unix-style paths (in "Git Bash" or Windows Subsystem for
+ * Linux).
+ *
+ * Worse, the behavior used to be that a leading `/` was
+ * not absolute. It would indicate that Git for Windows
+ * should add the prefix. So `//` is required for absolute
+ * Unix-style paths. Yes, this is truly horrifying.
+ *
+ * Emulate that behavior, I guess, but only for absolute
+ * paths. We won't deal with the Git install prefix. Also,
+ * give WSL users an escape hatch where they don't have to
+ * think about this and can use the literal path that the
+ * filesystem APIs provide (`//wsl.localhost/...`).
+ */
+ if (strncmp(test_path, "%(prefix)//", strlen("%(prefix)//")) == 0)
+ test_path += strlen("%(prefix)/");
+ else if (strncmp(test_path, "//", 2) == 0 &&
+ strncmp(test_path, "//wsl.localhost/", strlen("//wsl.localhost/")) != 0)
+ test_path++;
+#endif
+
+ if (git_fs_path_prettify_dir(&data->tmp, test_path, NULL) == 0 &&
+ strcmp(data->tmp.ptr, data->repo_path) == 0)
+ *data->is_safe = true;
+ }
return 0;
}
@@ -524,6 +560,9 @@ static int validate_ownership_config(bool *is_safe, const char *path)
validate_ownership_cb,
&ownership_data);
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+
git_config_free(config);
git_str_dispose(&ownership_data.tmp);
@@ -544,6 +583,9 @@ static int validate_ownership_path(bool *is_safe, const char *path)
if (error == GIT_ENOTFOUND) {
*is_safe = true;
error = 0;
+ } else if (error == GIT_EINVALID) {
+ *is_safe = false;
+ error = 0;
}
return error;
@@ -751,6 +793,43 @@ error:
return error;
}
+static int obtain_config_and_set_oid_type(
+ git_config **config_ptr,
+ git_repository *repo)
+{
+ int error;
+ git_config *config = NULL;
+ int version = 0;
+
+ /*
+ * We'd like to have the config, but git doesn't particularly
+ * care if it's not there, so we need to deal with that.
+ */
+
+ error = git_repository_config_snapshot(&config, repo);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto out;
+
+ if (config &&
+ (error = check_repositoryformatversion(&version, config)) < 0)
+ goto out;
+
+ if ((error = check_extensions(config, version)) < 0)
+ goto out;
+
+ if (version > 0) {
+ if ((error = load_objectformat(repo, config)) < 0)
+ goto out;
+ } else {
+ repo->oid_type = GIT_OID_SHA1;
+ }
+
+out:
+ *config_ptr = config;
+
+ return error;
+}
+
int git_repository_open_bare(
git_repository **repo_ptr,
const char *bare_path)
@@ -759,6 +838,7 @@ int git_repository_open_bare(
git_repository *repo = NULL;
bool is_valid;
int error;
+ git_config *config;
if ((error = git_fs_path_prettify_dir(&path, bare_path, NULL)) < 0 ||
(error = is_valid_repository_path(&is_valid, &path, &common_path)) < 0)
@@ -784,8 +864,15 @@ int git_repository_open_bare(
repo->is_worktree = 0;
repo->workdir = NULL;
+ if ((error = obtain_config_and_set_oid_type(&config, repo)) < 0)
+ goto cleanup;
+
*repo_ptr = repo;
- return 0;
+
+cleanup:
+ git_config_free(config);
+
+ return error;
}
static int _git_repository_open_ext_from_env(
@@ -992,7 +1079,6 @@ int git_repository_open_ext(
gitlink = GIT_STR_INIT, commondir = GIT_STR_INIT;
git_repository *repo = NULL;
git_config *config = NULL;
- int version = 0;
if (flags & GIT_REPOSITORY_OPEN_FROM_ENV)
return _git_repository_open_ext_from_env(repo_ptr, start_path);
@@ -1025,19 +1111,8 @@ int git_repository_open_ext(
goto cleanup;
repo->is_worktree = is_worktree;
- /*
- * We'd like to have the config, but git doesn't particularly
- * care if it's not there, so we need to deal with that.
- */
-
- error = git_repository_config_snapshot(&config, repo);
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- if (config && (error = check_repositoryformatversion(&version, config)) < 0)
- goto cleanup;
-
- if ((error = check_extensions(config, version)) < 0)
+ error = obtain_config_and_set_oid_type(&config, repo);
+ if (error < 0)
goto cleanup;
if (git_shallow__enabled && (error = load_grafts(repo)) < 0)
@@ -1291,11 +1366,14 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
*out = git_atomic_load(repo->_odb);
if (*out == NULL) {
git_str odb_path = GIT_STR_INIT;
+ git_odb_options odb_opts = GIT_ODB_OPTIONS_INIT;
git_odb *odb;
+ odb_opts.oid_type = repo->oid_type;
+
if ((error = git_repository__item_path(&odb_path, repo,
GIT_REPOSITORY_ITEM_OBJECTS)) < 0 ||
- (error = git_odb__new(&odb, NULL)) < 0)
+ (error = git_odb__new(&odb, &odb_opts)) < 0)
return error;
GIT_REFCOUNT_OWN(odb, repo);
@@ -1567,6 +1645,7 @@ static int check_repositoryformatversion(int *version, git_config *config)
int error;
error = git_config_get_int32(version, config, "core.repositoryformatversion");
+
/* git ignores this if the config variable isn't there */
if (error == GIT_ENOTFOUND)
return 0;
@@ -1574,10 +1653,15 @@ static int check_repositoryformatversion(int *version, git_config *config)
if (error < 0)
return -1;
- if (GIT_REPO_MAX_VERSION < *version) {
+ if (*version < 0) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "invalid repository version %d", *version);
+ }
+
+ if (GIT_REPO_VERSION_MAX < *version) {
git_error_set(GIT_ERROR_REPOSITORY,
"unsupported repository version %d; only versions up to %d are supported",
- *version, GIT_REPO_MAX_VERSION);
+ *version, GIT_REPO_VERSION_MAX);
return -1;
}
@@ -1585,7 +1669,8 @@ static int check_repositoryformatversion(int *version, git_config *config)
}
static const char *builtin_extensions[] = {
- "noop"
+ "noop",
+ "objectformat"
};
static git_vector user_extensions = GIT_VECTOR_INIT;
@@ -1649,6 +1734,79 @@ static int check_extensions(git_config *config, int version)
return git_config_foreach_match(config, "^extensions\\.", check_valid_extension, NULL);
}
+static int load_objectformat(git_repository *repo, git_config *config)
+{
+ git_config_entry *entry = NULL;
+ int error;
+
+ if ((error = git_config_get_entry(&entry, config, "extensions.objectformat")) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ repo->oid_type = GIT_OID_SHA1;
+ git_error_clear();
+ error = 0;
+ }
+
+ goto done;
+ }
+
+ if ((repo->oid_type = git_oid_type_fromstr(entry->value)) == 0) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "unknown object format '%s'", entry->value);
+ error = GIT_EINVALID;
+ }
+
+done:
+ git_config_entry_free(entry);
+ return error;
+}
+
+int git_repository__set_objectformat(
+ git_repository *repo,
+ git_oid_t oid_type)
+{
+ git_config *cfg;
+
+ /*
+ * Older clients do not necessarily understand the
+ * `objectformat` extension, even when it's set to an
+ * object format that they understand (SHA1). Do not set
+ * the objectformat extension unless we're not using the
+ * default object format.
+ */
+ if (oid_type == GIT_OID_DEFAULT)
+ return 0;
+
+ if (!git_repository_is_empty(repo) && repo->oid_type != oid_type) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "cannot change object id type of existing repository");
+ return -1;
+ }
+
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ return -1;
+
+ if (git_config_set_int32(cfg,
+ "core.repositoryformatversion", 1) < 0 ||
+ git_config_set_string(cfg, "extensions.objectformat",
+ git_oid_type_name(oid_type)) < 0)
+ return -1;
+
+ /*
+ * During repo init, we may create some backends with the
+ * default oid type. Clear them so that we create them with
+ * the proper oid type.
+ */
+ if (repo->oid_type != oid_type) {
+ set_index(repo, NULL);
+ set_odb(repo, NULL);
+ set_refdb(repo, NULL);
+
+ repo->oid_type = oid_type;
+ }
+
+ return 0;
+}
+
int git_repository__extensions(char ***out, size_t *out_len)
{
git_vector extensions;
@@ -1927,19 +2085,21 @@ static int repo_init_config(
const char *repo_dir,
const char *work_dir,
uint32_t flags,
- uint32_t mode)
+ uint32_t mode,
+ git_oid_t oid_type)
{
int error = 0;
git_str cfg_path = GIT_STR_INIT, worktree_path = GIT_STR_INIT;
git_config *config = NULL;
bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
- int version = 0;
+ int version = GIT_REPO_VERSION_DEFAULT;
if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
goto cleanup;
- if (is_reinit && (error = check_repositoryformatversion(&version, config)) < 0)
+ if (is_reinit &&
+ (error = check_repositoryformatversion(&version, config)) < 0)
goto cleanup;
if ((error = check_extensions(config, version)) < 0)
@@ -1950,7 +2110,7 @@ static int repo_init_config(
goto cleanup; } while (0)
SET_REPO_CONFIG(bool, "core.bare", is_bare);
- SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
+ SET_REPO_CONFIG(int32, "core.repositoryformatversion", version);
if ((error = repo_init_fs_configs(
config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
@@ -1983,6 +2143,11 @@ static int repo_init_config(
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
}
+ if (oid_type != GIT_OID_SHA1) {
+ SET_REPO_CONFIG(int32, "core.repositoryformatversion", 1);
+ SET_REPO_CONFIG(string, "extensions.objectformat", git_oid_type_name(oid_type));
+ }
+
cleanup:
git_str_dispose(&cfg_path);
git_str_dispose(&worktree_path);
@@ -2463,6 +2628,7 @@ int git_repository_init_ext(
common_path = GIT_STR_INIT;
const char *wd;
bool is_valid;
+ git_oid_t oid_type = GIT_OID_DEFAULT;
int error;
GIT_ASSERT_ARG(out);
@@ -2471,6 +2637,11 @@ int git_repository_init_ext(
GIT_ERROR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options");
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (opts->oid_type)
+ oid_type = opts->oid_type;
+#endif
+
if ((error = repo_init_directories(&repo_path, &wd_path, given_repo, opts)) < 0)
goto out;
@@ -2489,13 +2660,13 @@ int git_repository_init_ext(
opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
- if ((error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode)) < 0)
+ if ((error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode, oid_type)) < 0)
goto out;
/* TODO: reinitialize the templates */
} else {
if ((error = repo_init_structure(repo_path.ptr, wd, opts)) < 0 ||
- (error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode)) < 0 ||
+ (error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode, oid_type)) < 0 ||
(error = repo_init_head(repo_path.ptr, opts->initial_head)) < 0)
goto out;
}
@@ -2958,14 +3129,14 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head
{
git_filebuf file = GIT_FILEBUF_INIT;
git_str file_path = GIT_STR_INIT;
- char orig_head_str[GIT_OID_SHA1_HEXSIZE];
+ char orig_head_str[GIT_OID_MAX_HEXSIZE];
int error = 0;
git_oid_fmt(orig_head_str, orig_head);
if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_ORIG_HEAD_FILE)) == 0 &&
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) == 0 &&
- (error = git_filebuf_printf(&file, "%.*s\n", GIT_OID_SHA1_HEXSIZE, orig_head_str)) == 0)
+ (error = git_filebuf_printf(&file, "%.*s\n", (int)git_oid_hexsize(repo->oid_type), orig_head_str)) == 0)
error = git_filebuf_commit(&file);
if (error < 0)
@@ -3078,7 +3249,7 @@ int git_repository_hashfile(
goto cleanup;
}
- error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, GIT_OID_SHA1, fl);
+ error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, repo->oid_type, fl);
cleanup:
if (fd >= 0)
@@ -3478,3 +3649,8 @@ int git_repository_submodule_cache_clear(git_repository *repo)
repo->submodule_cache = NULL;
return error;
}
+
+git_oid_t git_repository_oid_type(git_repository *repo)
+{
+ return repo ? repo->oid_type : 0;
+}
diff --git a/src/libgit2/repository.h b/src/libgit2/repository.h
index c7966bcd2..16458e416 100644
--- a/src/libgit2/repository.h
+++ b/src/libgit2/repository.h
@@ -154,6 +154,7 @@ struct git_repository {
unsigned is_bare:1;
unsigned is_worktree:1;
+ git_oid_t oid_type;
unsigned int lru_counter;
@@ -265,4 +266,12 @@ int git_repository__extensions(char ***out, size_t *out_len);
int git_repository__set_extensions(const char **extensions, size_t len);
void git_repository__free_extensions(void);
+/*
+ * Set the object format (OID type) for a repository; this will set
+ * both the configuration and the internal value for the oid type.
+ */
+int git_repository__set_objectformat(
+ git_repository *repo,
+ git_oid_t oid_type);
+
#endif
diff --git a/src/libgit2/revparse.c b/src/libgit2/revparse.c
index 2ed282159..964afe378 100644
--- a/src/libgit2/revparse.c
+++ b/src/libgit2/revparse.c
@@ -15,21 +15,28 @@
#include "git2.h"
-static int maybe_sha_or_abbrev(git_object **out, git_repository *repo, const char *spec, size_t speclen)
+static int maybe_sha_or_abbrev(
+ git_object **out,
+ git_repository *repo,
+ const char *spec,
+ size_t speclen)
{
git_oid oid;
- if (git_oid__fromstrn(&oid, spec, speclen, GIT_OID_SHA1) < 0)
+ if (git_oid__fromstrn(&oid, spec, speclen, repo->oid_type) < 0)
return GIT_ENOTFOUND;
return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJECT_ANY);
}
-static int maybe_sha(git_object **out, git_repository *repo, const char *spec)
+static int maybe_sha(
+ git_object **out,
+ git_repository *repo,
+ const char *spec)
{
size_t speclen = strlen(spec);
- if (speclen != GIT_OID_SHA1_HEXSIZE)
+ if (speclen != git_oid_hexsize(repo->oid_type))
return GIT_ENOTFOUND;
return maybe_sha_or_abbrev(out, repo, spec, speclen);
@@ -110,8 +117,8 @@ static int revparse_lookup_object(
if (error != GIT_ENOTFOUND)
return error;
- if ((strlen(spec) < GIT_OID_SHA1_HEXSIZE) &&
- ((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND))
+ if ((strlen(spec) < git_oid_hexsize(repo->oid_type)) &&
+ ((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND))
return error;
if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND)
@@ -268,7 +275,16 @@ static int retrieve_revobject_from_reflog(git_object **out, git_reference **base
int error = -1;
if (*base_ref == NULL) {
- if ((error = git_reference_dwim(&ref, repo, identifier)) < 0)
+ /*
+ * When HEAD@{n} is specified, do not use dwim, which would resolve the
+ * reference (to the current branch that HEAD is pointing to).
+ */
+ if (position > 0 && strcmp(identifier, GIT_HEAD_FILE) == 0)
+ error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE);
+ else
+ error = git_reference_dwim(&ref, repo, identifier);
+
+ if (error < 0)
return error;
} else {
ref = *base_ref;
diff --git a/src/libgit2/revwalk.c b/src/libgit2/revwalk.c
index 553e0497a..3269d9279 100644
--- a/src/libgit2/revwalk.c
+++ b/src/libgit2/revwalk.c
@@ -121,8 +121,12 @@ int git_revwalk__push_ref(git_revwalk *walk, const char *refname, const git_revw
{
git_oid oid;
- if (git_reference_name_to_id(&oid, walk->repo, refname) < 0)
+ int error = git_reference_name_to_id(&oid, walk->repo, refname);
+ if (opts->from_glob && (error == GIT_ENOTFOUND || error == GIT_EINVALIDSPEC || error == GIT_EPEEL)) {
+ return 0;
+ } else if (error < 0) {
return -1;
+ }
return git_revwalk__push_commit(walk, &oid, opts);
}
diff --git a/src/libgit2/stash.c b/src/libgit2/stash.c
index 5fc01ac36..319ae3a3f 100644
--- a/src/libgit2/stash.c
+++ b/src/libgit2/stash.c
@@ -25,6 +25,7 @@
#include "merge.h"
#include "diff.h"
#include "diff_generate.h"
+#include "strarray.h"
static int create_error(int error, const char *msg)
{
@@ -193,6 +194,30 @@ static int stash_to_index(
return git_index_add(index, &entry);
}
+static int stash_update_index_from_paths(
+ git_repository *repo,
+ git_index *index,
+ const git_strarray *paths)
+{
+ unsigned int status_flags;
+ size_t i;
+ int error = 0;
+
+ for (i = 0; i < paths->count; i++) {
+ git_status_file(&status_flags, repo, paths->strings[i]);
+
+ if (status_flags & (GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_DELETED)) {
+ if ((error = git_index_remove(index, paths->strings[i], 0)) < 0)
+ return error;
+ } else {
+ if ((error = stash_to_index(repo, index, paths->strings[i])) < 0)
+ return error;
+ }
+ }
+
+ return error;
+}
+
static int stash_update_index_from_diff(
git_repository *repo,
git_index *index,
@@ -388,24 +413,79 @@ cleanup:
return error;
}
-static int commit_worktree(
+static int build_stash_commit_from_tree(
git_oid *w_commit_oid,
git_repository *repo,
const git_signature *stasher,
const char *message,
git_commit *i_commit,
git_commit *b_commit,
- git_commit *u_commit)
+ git_commit *u_commit,
+ const git_tree *tree)
{
const git_commit *parents[] = { NULL, NULL, NULL };
- git_index *i_index = NULL, *r_index = NULL;
- git_tree *w_tree = NULL;
- int error = 0, ignorecase;
parents[0] = b_commit;
parents[1] = i_commit;
parents[2] = u_commit;
+ return git_commit_create(
+ w_commit_oid,
+ repo,
+ NULL,
+ stasher,
+ stasher,
+ NULL,
+ message,
+ tree,
+ u_commit ? 3 : 2,
+ parents);
+}
+
+static int build_stash_commit_from_index(
+ git_oid *w_commit_oid,
+ git_repository *repo,
+ const git_signature *stasher,
+ const char *message,
+ git_commit *i_commit,
+ git_commit *b_commit,
+ git_commit *u_commit,
+ git_index *index)
+{
+ git_tree *tree;
+ int error;
+
+ if ((error = build_tree_from_index(&tree, repo, index)) < 0)
+ goto cleanup;
+
+ error = build_stash_commit_from_tree(
+ w_commit_oid,
+ repo,
+ stasher,
+ message,
+ i_commit,
+ b_commit,
+ u_commit,
+ tree);
+
+cleanup:
+ git_tree_free(tree);
+ return error;
+}
+
+static int commit_worktree(
+ git_oid *w_commit_oid,
+ git_repository *repo,
+ const git_signature *stasher,
+ const char *message,
+ git_commit *i_commit,
+ git_commit *b_commit,
+ git_commit *u_commit)
+{
+ git_index *i_index = NULL, *r_index = NULL;
+ git_tree *w_tree = NULL;
+ int error = 0, ignorecase;
+
if ((error = git_repository_index(&r_index, repo) < 0) ||
(error = git_index_new(&i_index)) < 0 ||
(error = git_index__fill(i_index, &r_index->entries) < 0) ||
@@ -417,17 +497,16 @@ static int commit_worktree(
if ((error = build_workdir_tree(&w_tree, repo, i_index, b_commit)) < 0)
goto cleanup;
- error = git_commit_create(
+ error = build_stash_commit_from_tree(
w_commit_oid,
repo,
- NULL,
stasher,
- stasher,
- NULL,
message,
- w_tree,
- u_commit ? 3 : 2,
- parents);
+ i_commit,
+ b_commit,
+ u_commit,
+ w_tree
+ );
cleanup:
git_tree_free(w_tree);
@@ -520,6 +599,54 @@ static int ensure_there_are_changes_to_stash(git_repository *repo, uint32_t flag
return error;
}
+static int has_changes_cb(
+ const char *path,
+ unsigned int status,
+ void *payload)
+{
+ GIT_UNUSED(path);
+ GIT_UNUSED(status);
+ GIT_UNUSED(payload);
+
+ if (status == GIT_STATUS_CURRENT)
+ return GIT_ENOTFOUND;
+
+ return 0;
+}
+
+static int ensure_there_are_changes_to_stash_paths(
+ git_repository *repo,
+ uint32_t flags,
+ const git_strarray *paths)
+{
+ int error;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
+
+ if (flags & GIT_STASH_INCLUDE_UNTRACKED)
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ if (flags & GIT_STASH_INCLUDE_IGNORED)
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+
+ git_strarray_copy(&opts.pathspec, paths);
+
+ error = git_status_foreach_ext(repo, &opts, has_changes_cb, NULL);
+
+ git_strarray_dispose(&opts.pathspec);
+
+ if (error == GIT_ENOTFOUND)
+ return create_error(GIT_ENOTFOUND, "one of the files does not have any changes to stash.");
+
+ return error;
+}
+
static int reset_index_and_workdir(git_repository *repo, git_commit *commit, uint32_t flags)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
@@ -540,14 +667,36 @@ int git_stash_save(
const char *message,
uint32_t flags)
{
- git_index *index = NULL;
+ git_stash_save_options opts = GIT_STASH_SAVE_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(stasher);
+
+ opts.stasher = stasher;
+ opts.message = message;
+ opts.flags = flags;
+
+ return git_stash_save_with_opts(out, repo, &opts);
+}
+
+int git_stash_save_with_opts(
+ git_oid *out,
+ git_repository *repo,
+ const git_stash_save_options *opts)
+{
+ git_index *index = NULL, *paths_index = NULL;
git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL;
git_str msg = GIT_STR_INIT;
+ git_tree *tree = NULL;
+ git_reference *head = NULL;
+ bool has_paths = false;
+
int error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(repo);
- GIT_ASSERT_ARG(stasher);
+ GIT_ASSERT_ARG(opts && opts->stasher);
+
+ has_paths = opts->paths.count > 0;
if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0)
return error;
@@ -555,44 +704,63 @@ int git_stash_save(
if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
goto cleanup;
- if ((error = ensure_there_are_changes_to_stash(repo, flags)) < 0)
+ if (!has_paths &&
+ (error = ensure_there_are_changes_to_stash(repo, opts->flags)) < 0)
+ goto cleanup;
+ else if (has_paths &&
+ (error = ensure_there_are_changes_to_stash_paths(
+ repo, opts->flags, &opts->paths)) < 0)
goto cleanup;
if ((error = git_repository_index(&index, repo)) < 0)
goto cleanup;
- if ((error = commit_index(&i_commit, repo, index, stasher,
+ if ((error = commit_index(&i_commit, repo, index, opts->stasher,
git_str_cstr(&msg), b_commit)) < 0)
goto cleanup;
- if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
- (error = commit_untracked(&u_commit, repo, stasher,
- git_str_cstr(&msg), i_commit, flags)) < 0)
+ if ((opts->flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
+ (error = commit_untracked(&u_commit, repo, opts->stasher,
+ git_str_cstr(&msg), i_commit, opts->flags)) < 0)
goto cleanup;
- if ((error = prepare_worktree_commit_message(&msg, message)) < 0)
+ if ((error = prepare_worktree_commit_message(&msg, opts->message)) < 0)
goto cleanup;
- if ((error = commit_worktree(out, repo, stasher, git_str_cstr(&msg),
- i_commit, b_commit, u_commit)) < 0)
- goto cleanup;
+ if (!has_paths) {
+ if ((error = commit_worktree(out, repo, opts->stasher, git_str_cstr(&msg),
+ i_commit, b_commit, u_commit)) < 0)
+ goto cleanup;
+ } else {
+ if ((error = git_index_new(&paths_index)) < 0 ||
+ (error = retrieve_head(&head, repo)) < 0 ||
+ (error = git_reference_peel((git_object**)&tree, head, GIT_OBJECT_TREE)) < 0 ||
+ (error = git_index_read_tree(paths_index, tree)) < 0 ||
+ (error = stash_update_index_from_paths(repo, paths_index, &opts->paths)) < 0 ||
+ (error = build_stash_commit_from_index(out, repo, opts->stasher, git_str_cstr(&msg),
+ i_commit, b_commit, u_commit, paths_index)) < 0)
+ goto cleanup;
+ }
git_str_rtrim(&msg);
if ((error = update_reflog(out, repo, git_str_cstr(&msg))) < 0)
goto cleanup;
- if ((error = reset_index_and_workdir(repo, (flags & GIT_STASH_KEEP_INDEX) ? i_commit : b_commit,
- flags)) < 0)
+ if (!(opts->flags & GIT_STASH_KEEP_ALL) &&
+ (error = reset_index_and_workdir(repo,
+ (opts->flags & GIT_STASH_KEEP_INDEX) ? i_commit : b_commit,opts->flags)) < 0)
goto cleanup;
cleanup:
-
git_str_dispose(&msg);
git_commit_free(i_commit);
git_commit_free(b_commit);
git_commit_free(u_commit);
+ git_tree_free(tree);
+ git_reference_free(head);
git_index_free(index);
+ git_index_free(paths_index);
return error;
}
@@ -777,6 +945,13 @@ int git_stash_apply_options_init(git_stash_apply_options *opts, unsigned int ver
return 0;
}
+int git_stash_save_options_init(git_stash_save_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_stash_save_options, GIT_STASH_SAVE_OPTIONS_INIT);
+ return 0;
+}
+
#ifndef GIT_DEPRECATE_HARD
int git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int version)
{
diff --git a/src/libgit2/strarray.c b/src/libgit2/strarray.c
index 2f9b77cc2..25e75f02a 100644
--- a/src/libgit2/strarray.c
+++ b/src/libgit2/strarray.c
@@ -8,6 +8,7 @@
#include "util.h"
#include "common.h"
+#include "strarray.h"
int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
{
diff --git a/src/libgit2/strarray.h b/src/libgit2/strarray.h
new file mode 100644
index 000000000..198480535
--- /dev/null
+++ b/src/libgit2/strarray.h
@@ -0,0 +1,25 @@
+/*
+ * 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_strarray_h__
+#define INCLUDE_strarray_h__
+
+#include "common.h"
+#include "git2/strarray.h"
+
+/**
+ * Copy a string array object from source to target.
+ *
+ * Note: target is overwritten and hence should be empty, otherwise its
+ * contents are leaked. Call git_strarray_free() if necessary.
+ *
+ * @param tgt target
+ * @param src source
+ * @return 0 on success, < 0 on allocation failure
+ */
+extern int git_strarray_copy(git_strarray *tgt, const git_strarray *src);
+
+#endif
diff --git a/src/libgit2/streams/openssl.c b/src/libgit2/streams/openssl.c
index 89c96780c..5e0e2c939 100644
--- a/src/libgit2/streams/openssl.c
+++ b/src/libgit2/streams/openssl.c
@@ -198,7 +198,7 @@ static int openssl_ensure_initialized(void)
if ((error = git_openssl_stream_dynamic_init()) == 0)
error = openssl_init();
- openssl_initialized = true;
+ openssl_initialized = !error;
}
error |= git_mutex_unlock(&openssl_mutex);
diff --git a/src/libgit2/streams/openssl_dynamic.c b/src/libgit2/streams/openssl_dynamic.c
index da16b6ed7..222c1099d 100644
--- a/src/libgit2/streams/openssl_dynamic.c
+++ b/src/libgit2/streams/openssl_dynamic.c
@@ -91,7 +91,7 @@ int (*sk_num)(const void *sk);
void *(*sk_value)(const void *sk, int i);
void (*sk_free)(void *sk);
-void *openssl_handle;
+static void *openssl_handle;
GIT_INLINE(void *) openssl_sym(int *err, const char *name, bool required)
{
@@ -125,7 +125,8 @@ int git_openssl_stream_dynamic_init(void)
(openssl_handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL &&
(openssl_handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL &&
(openssl_handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL &&
- (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL) {
+ (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.3", RTLD_NOW)) == NULL) {
git_error_set(GIT_ERROR_SSL, "could not load ssl libraries");
return -1;
}
@@ -175,7 +176,6 @@ int git_openssl_stream_dynamic_init(void)
SSL_connect = (int (*)(SSL *))openssl_sym(&err, "SSL_connect", true);
SSL_ctrl = (long (*)(SSL *, int, long, void *))openssl_sym(&err, "SSL_ctrl", true);
- SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get_peer_certificate", true);
SSL_library_init = (int (*)(void))openssl_sym(&err, "SSL_library_init", false);
SSL_free = (void (*)(SSL *))openssl_sym(&err, "SSL_free", true);
SSL_get_error = (int (*)(SSL *, int))openssl_sym(&err, "SSL_get_error", true);
@@ -187,6 +187,10 @@ int git_openssl_stream_dynamic_init(void)
SSL_shutdown = (int (*)(SSL *ssl))openssl_sym(&err, "SSL_shutdown", true);
SSL_write = (int (*)(SSL *, const void *, int))openssl_sym(&err, "SSL_write", true);
+ if (!(SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get_peer_certificate", false))) {
+ SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get1_peer_certificate", true);
+ }
+
SSL_CTX_ctrl = (long (*)(SSL_CTX *, int, long, void *))openssl_sym(&err, "SSL_CTX_ctrl", true);
SSL_CTX_free = (void (*)(SSL_CTX *))openssl_sym(&err, "SSL_CTX_free", true);
SSL_CTX_new = (SSL_CTX *(*)(const SSL_METHOD *))openssl_sym(&err, "SSL_CTX_new", true);
diff --git a/src/libgit2/streams/socket.c b/src/libgit2/streams/socket.c
index 9415fe892..908e8c02f 100644
--- a/src/libgit2/streams/socket.c
+++ b/src/libgit2/streams/socket.c
@@ -135,9 +135,12 @@ static ssize_t socket_write(git_stream *stream, const char *data, size_t len, in
git_socket_stream *st = (git_socket_stream *) stream;
ssize_t written;
+ GIT_ASSERT(flags == 0);
+ GIT_UNUSED(flags);
+
errno = 0;
- if ((written = p_send(st->s, data, len, flags)) < 0) {
+ if ((written = p_send(st->s, data, len, 0)) < 0) {
net_set_error("error sending data");
return -1;
}
diff --git a/src/libgit2/submodule.c b/src/libgit2/submodule.c
index 0f4f0726c..95ea84fc2 100644
--- a/src/libgit2/submodule.c
+++ b/src/libgit2/submodule.c
@@ -1338,7 +1338,11 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio
/* Get the status of the submodule to determine if it is already initialized */
if ((error = git_submodule_status(&submodule_status, sm->repo, sm->name, GIT_SUBMODULE_IGNORE_UNSPECIFIED)) < 0)
goto done;
-
+
+ /* If the submodule is configured but hasn't been added, skip it */
+ if (submodule_status == GIT_SUBMODULE_STATUS_IN_CONFIG)
+ goto done;
+
/*
* If submodule work dir is not already initialized, check to see
* what we need to do (initialize, clone, return error...)
@@ -1389,7 +1393,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio
*/
clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
- if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
+ if ((error = git_clone__submodule(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
(error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 ||
(error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0)
goto done;
diff --git a/src/libgit2/sysdir.c b/src/libgit2/sysdir.c
index 450cb509b..7838a6789 100644
--- a/src/libgit2/sysdir.c
+++ b/src/libgit2/sysdir.c
@@ -12,16 +12,262 @@
#include "fs_path.h"
#include <ctype.h>
#if GIT_WIN32
-#include "win32/findfile.h"
+# include "fs_path.h"
+# include "win32/path_w32.h"
+# include "win32/utf-conv.h"
#else
-#include <unistd.h>
-#include <pwd.h>
+# include <unistd.h>
+# include <pwd.h>
#endif
+#ifdef GIT_WIN32
+# define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
+# define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
+
+static int expand_win32_path(git_win32_path dest, const wchar_t *src)
+{
+ DWORD len = ExpandEnvironmentStringsW(src, dest, GIT_WIN_PATH_UTF16);
+
+ if (!len || len > GIT_WIN_PATH_UTF16)
+ return -1;
+
+ return 0;
+}
+
+static int win32_path_to_utf8(git_str *dest, const wchar_t *src)
+{
+ git_win32_utf8_path utf8_path;
+
+ if (git_win32_path_to_utf8(utf8_path, src) < 0) {
+ git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8");
+ return -1;
+ }
+
+ /* Convert backslashes to forward slashes */
+ git_fs_path_mkposix(utf8_path);
+
+ return git_str_sets(dest, utf8_path);
+}
+
+static git_win32_path mock_registry;
+static bool mock_registry_set;
+
+extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir)
+{
+ if (!mock_sysdir) {
+ mock_registry[0] = L'\0';
+ mock_registry_set = false;
+ } else {
+ size_t len = wcslen(mock_sysdir);
+
+ if (len > GIT_WIN_PATH_MAX) {
+ git_error_set(GIT_ERROR_INVALID, "mock path too long");
+ return -1;
+ }
+
+ wcscpy(mock_registry, mock_sysdir);
+ mock_registry_set = true;
+ }
+
+ return 0;
+}
+
+static int lookup_registry_key(
+ git_win32_path out,
+ const HKEY hive,
+ const wchar_t* key,
+ const wchar_t *value)
+{
+ HKEY hkey;
+ DWORD type, size;
+ int error = GIT_ENOTFOUND;
+
+ /*
+ * Registry data may not be NUL terminated, provide room to do
+ * it ourselves.
+ */
+ size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t));
+
+ if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0)
+ return GIT_ENOTFOUND;
+
+ if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 &&
+ type == REG_SZ &&
+ size > 0 &&
+ size < sizeof(git_win32_path)) {
+ size_t wsize = size / sizeof(wchar_t);
+ size_t len = wsize - 1;
+
+ if (out[wsize - 1] != L'\0') {
+ len = wsize;
+ out[wsize] = L'\0';
+ }
+
+ if (out[len - 1] == L'\\')
+ out[len - 1] = L'\0';
+
+ if (_waccess(out, F_OK) == 0)
+ error = 0;
+ }
+
+ RegCloseKey(hkey);
+ return error;
+}
+
+static int find_sysdir_in_registry(git_win32_path out)
+{
+ if (mock_registry_set) {
+ if (mock_registry[0] == L'\0')
+ return GIT_ENOTFOUND;
+
+ wcscpy(out, mock_registry);
+ return 0;
+ }
+
+ if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0)
+ return 0;
+
+ return GIT_ENOTFOUND;
+}
+
+static int find_sysdir_in_path(git_win32_path out)
+{
+ size_t out_len;
+
+ if (git_win32_path_find_executable(out, L"git.exe") < 0 &&
+ git_win32_path_find_executable(out, L"git.cmd") < 0)
+ return GIT_ENOTFOUND;
+
+ out_len = wcslen(out);
+
+ /* Trim the file name */
+ if (out_len <= CONST_STRLEN(L"git.exe"))
+ return GIT_ENOTFOUND;
+
+ out_len -= CONST_STRLEN(L"git.exe");
+
+ if (out_len && out[out_len - 1] == L'\\')
+ out_len--;
+
+ /*
+ * Git for Windows usually places the command in a 'bin' or
+ * 'cmd' directory, trim that.
+ */
+ if (out_len >= CONST_STRLEN(L"\\bin") &&
+ wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0)
+ out_len -= CONST_STRLEN(L"\\bin");
+ else if (out_len >= CONST_STRLEN(L"\\cmd") &&
+ wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0)
+ out_len -= CONST_STRLEN(L"\\cmd");
+
+ if (!out_len)
+ return GIT_ENOTFOUND;
+
+ out[out_len] = L'\0';
+ return 0;
+}
+
+static int find_win32_dirs(
+ git_str *out,
+ const wchar_t* tmpl[])
+{
+ git_win32_path path16;
+ git_str buf = GIT_STR_INIT;
+
+ git_str_clear(out);
+
+ for (; *tmpl != NULL; tmpl++) {
+ if (!expand_win32_path(path16, *tmpl) &&
+ path16[0] != L'%' &&
+ !_waccess(path16, F_OK)) {
+ win32_path_to_utf8(&buf, path16);
+
+ if (buf.size)
+ git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
+ }
+ }
+
+ git_str_dispose(&buf);
+
+ return (git_str_oom(out) ? -1 : 0);
+}
+
+static int append_subdir(git_str *out, git_str *path, const char *subdir)
+{
+ static const char* architecture_roots[] = {
+ "",
+ "mingw64",
+ "mingw32",
+ NULL
+ };
+ const char **root;
+ size_t orig_path_len = path->size;
+
+ for (root = architecture_roots; *root; root++) {
+ if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) ||
+ git_str_joinpath(path, path->ptr, subdir) < 0)
+ return -1;
+
+ if (git_fs_path_exists(path->ptr) &&
+ git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0)
+ return -1;
+
+ git_str_truncate(path, orig_path_len);
+ }
+
+ return 0;
+}
+
+int git_win32__find_system_dirs(git_str *out, const char *subdir)
+{
+ git_win32_path pathdir, regdir;
+ git_str path8 = GIT_STR_INIT;
+ bool has_pathdir, has_regdir;
+ int error;
+
+ has_pathdir = (find_sysdir_in_path(pathdir) == 0);
+ has_regdir = (find_sysdir_in_registry(regdir) == 0);
+
+ if (!has_pathdir && !has_regdir)
+ return 0;
+
+ /*
+ * Usually the git in the path is the same git in the registry,
+ * in this case there's no need to duplicate the paths.
+ */
+ if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0)
+ has_regdir = false;
+
+ if (has_pathdir) {
+ if ((error = win32_path_to_utf8(&path8, pathdir)) < 0 ||
+ (error = append_subdir(out, &path8, subdir)) < 0)
+ goto done;
+ }
+
+ if (has_regdir) {
+ if ((error = win32_path_to_utf8(&path8, regdir)) < 0 ||
+ (error = append_subdir(out, &path8, subdir)) < 0)
+ goto done;
+ }
+
+done:
+ git_str_dispose(&path8);
+ return error;
+}
+#endif /* WIN32 */
+
static int git_sysdir_guess_programdata_dirs(git_str *out)
{
#ifdef GIT_WIN32
- return git_win32__find_programdata_dirs(out);
+ static const wchar_t *programdata_tmpls[2] = {
+ L"%PROGRAMDATA%\\Git",
+ NULL,
+ };
+
+ return find_win32_dirs(out, programdata_tmpls);
#else
git_str_clear(out);
return 0;
@@ -75,10 +321,17 @@ out:
}
#endif
-static int git_sysdir_guess_global_dirs(git_str *out)
+static int git_sysdir_guess_home_dirs(git_str *out)
{
#ifdef GIT_WIN32
- return git_win32__find_global_dirs(out);
+ static const wchar_t *global_tmpls[4] = {
+ L"%HOME%\\",
+ L"%HOMEDRIVE%%HOMEPATH%\\",
+ L"%USERPROFILE%\\",
+ NULL,
+ };
+
+ return find_win32_dirs(out, global_tmpls);
#else
int error;
uid_t uid, euid;
@@ -114,10 +367,25 @@ static int git_sysdir_guess_global_dirs(git_str *out)
#endif
}
+static int git_sysdir_guess_global_dirs(git_str *out)
+{
+ return git_sysdir_guess_home_dirs(out);
+}
+
static int git_sysdir_guess_xdg_dirs(git_str *out)
{
#ifdef GIT_WIN32
- return git_win32__find_xdg_dirs(out);
+ static const wchar_t *global_tmpls[7] = {
+ L"%XDG_CONFIG_HOME%\\git",
+ L"%APPDATA%\\git",
+ L"%LOCALAPPDATA%\\git",
+ L"%HOME%\\.config\\git",
+ L"%HOMEDRIVE%%HOMEPATH%\\.config\\git",
+ L"%USERPROFILE%\\.config\\git",
+ NULL,
+ };
+
+ return find_win32_dirs(out, global_tmpls);
#else
git_str env = GIT_STR_INIT;
int error;
@@ -171,6 +439,7 @@ static struct git_sysdir__dir git_sysdir__dirs[] = {
{ GIT_STR_INIT, git_sysdir_guess_xdg_dirs },
{ GIT_STR_INIT, git_sysdir_guess_programdata_dirs },
{ GIT_STR_INIT, git_sysdir_guess_template_dirs },
+ { GIT_STR_INIT, git_sysdir_guess_home_dirs }
};
static void git_sysdir_global_shutdown(void)
@@ -350,6 +619,12 @@ int git_sysdir_find_template_dir(git_str *path)
path, NULL, GIT_SYSDIR_TEMPLATE, "template");
}
+int git_sysdir_find_homedir(git_str *path)
+{
+ return git_sysdir_find_in_dirlist(
+ path, NULL, GIT_SYSDIR_HOME, "home directory");
+}
+
int git_sysdir_expand_global_file(git_str *path, const char *filename)
{
int error;
@@ -361,3 +636,15 @@ int git_sysdir_expand_global_file(git_str *path, const char *filename)
return error;
}
+
+int git_sysdir_expand_homedir_file(git_str *path, const char *filename)
+{
+ int error;
+
+ if ((error = git_sysdir_find_homedir(path)) == 0) {
+ if (filename)
+ error = git_str_joinpath(path, path->ptr, filename);
+ }
+
+ return error;
+}
diff --git a/src/libgit2/sysdir.h b/src/libgit2/sysdir.h
index 568f27940..03f59e1de 100644
--- a/src/libgit2/sysdir.h
+++ b/src/libgit2/sysdir.h
@@ -57,10 +57,22 @@ extern int git_sysdir_find_programdata_file(git_str *path, const char *filename)
extern int git_sysdir_find_template_dir(git_str *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.
+ * Find the home directory. On Windows, this will look at the `HOME`,
+ * `HOMEPATH`, and `USERPROFILE` environment variables (in that order)
+ * and return the first path that is set and exists. On other systems,
+ * this will simply return the contents of the `HOME` environment variable.
+ *
+ * @param path buffer to write the full path into
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_sysdir_find_homedir(git_str *path);
+
+/**
+ * Expand the name of a "global" file -- by default inside the user's
+ * home directory, but can be overridden by the user configuration.
+ * 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
@@ -68,13 +80,25 @@ extern int git_sysdir_find_template_dir(git_str *path);
*/
extern int git_sysdir_expand_global_file(git_str *path, const char *filename);
+/**
+ * Expand the name of a file in the user's home directory. This
+ * function 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_homedir_file(git_str *path, const char *filename);
+
typedef enum {
- GIT_SYSDIR_SYSTEM = 0,
- GIT_SYSDIR_GLOBAL = 1,
- GIT_SYSDIR_XDG = 2,
+ GIT_SYSDIR_SYSTEM = 0,
+ GIT_SYSDIR_GLOBAL = 1,
+ GIT_SYSDIR_XDG = 2,
GIT_SYSDIR_PROGRAMDATA = 3,
- GIT_SYSDIR_TEMPLATE = 4,
- GIT_SYSDIR__MAX = 5
+ GIT_SYSDIR_TEMPLATE = 4,
+ GIT_SYSDIR_HOME = 5,
+ GIT_SYSDIR__MAX = 6
} git_sysdir_t;
/**
@@ -110,4 +134,12 @@ extern int git_sysdir_set(git_sysdir_t which, const char *paths);
*/
extern int git_sysdir_reset(void);
+#ifdef GIT_WIN32
+/** Sets the registry system dir to a mock; for testing. */
+extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir);
+
+/** Find the given system dir; for testing. */
+extern int git_win32__find_system_dirs(git_str *out, const char *subdir);
+#endif
+
#endif
diff --git a/src/libgit2/tag.c b/src/libgit2/tag.c
index 908adbebf..562ec13ea 100644
--- a/src/libgit2/tag.c
+++ b/src/libgit2/tag.c
@@ -65,7 +65,11 @@ static int tag_error(const char *str)
return GIT_EINVALID;
}
-static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
+static int tag_parse(
+ git_tag *tag,
+ const char *buffer,
+ const char *buffer_end,
+ git_oid_t oid_type)
{
static const char *tag_types[] = {
NULL, "commit\n", "tree\n", "blob\n", "tag\n"
@@ -76,7 +80,7 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
int error;
if (git_object__parse_oid_header(&tag->target,
- &buffer, buffer_end, "object ", GIT_OID_SHA1) < 0)
+ &buffer, buffer_end, "object ", oid_type) < 0)
return tag_error("object field invalid");
if (buffer + 5 >= buffer_end)
@@ -161,18 +165,25 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
return 0;
}
-int git_tag__parse_raw(void *_tag, const char *data, size_t size)
+int git_tag__parse_raw(
+ void *_tag,
+ const char *data,
+ size_t size,
+ git_oid_t oid_type)
{
- return tag_parse(_tag, data, data + size);
+ return tag_parse(_tag, data, data + size, oid_type);
}
-int git_tag__parse(void *_tag, git_odb_object *odb_obj)
+int git_tag__parse(
+ void *_tag,
+ git_odb_object *odb_obj,
+ git_oid_t oid_type)
{
git_tag *tag = _tag;
const char *buffer = git_odb_object_data(odb_obj);
const char *buffer_end = buffer + git_odb_object_size(odb_obj);
- return tag_parse(tag, buffer, buffer_end);
+ return tag_parse(tag, buffer, buffer_end, oid_type);
}
static int retrieve_tag_reference(
@@ -299,8 +310,10 @@ static int git_tag_create__internal(
}
if (create_tag_annotation) {
- if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
+ if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0) {
+ git_str_dispose(&ref_name);
return -1;
+ }
} else
git_oid_cpy(oid, git_object_id(target));
@@ -372,7 +385,7 @@ int git_tag_create_from_buffer(git_oid *oid, git_repository *repo, const char *b
return -1;
/* validate the buffer */
- if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
+ if (tag_parse(&tag, buffer, buffer + strlen(buffer), repo->oid_type) < 0)
return -1;
/* validate the target */
@@ -397,14 +410,17 @@ int git_tag_create_from_buffer(git_oid *oid, git_repository *repo, const char *b
/** Ensure the tag name doesn't conflict with an already existing
* reference unless overwriting has explicitly been requested **/
if (error == 0 && !allow_ref_overwrite) {
+ git_str_dispose(&ref_name);
git_error_set(GIT_ERROR_TAG, "tag already exists");
return GIT_EEXISTS;
}
/* write the buffer */
if ((error = git_odb_open_wstream(
- &stream, odb, strlen(buffer), GIT_OBJECT_TAG)) < 0)
+ &stream, odb, strlen(buffer), GIT_OBJECT_TAG)) < 0) {
+ git_str_dispose(&ref_name);
return error;
+ }
if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
error = git_odb_stream_finalize_write(oid, stream);
diff --git a/src/libgit2/tag.h b/src/libgit2/tag.h
index 76ae1508e..fdaaa463c 100644
--- a/src/libgit2/tag.h
+++ b/src/libgit2/tag.h
@@ -25,7 +25,7 @@ struct git_tag {
};
void git_tag__free(void *tag);
-int git_tag__parse(void *tag, git_odb_object *obj);
-int git_tag__parse_raw(void *tag, const char *data, size_t size);
+int git_tag__parse(void *tag, git_odb_object *obj, git_oid_t oid_type);
+int git_tag__parse_raw(void *tag, const char *data, size_t size, git_oid_t oid_type);
#endif
diff --git a/src/libgit2/transports/http.c b/src/libgit2/transports/http.c
index 7db5582ca..cda76ae61 100644
--- a/src/libgit2/transports/http.c
+++ b/src/libgit2/transports/http.c
@@ -655,6 +655,7 @@ static int http_action(
{
http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
+ git_http_client_options opts = {0};
http_stream *stream;
const http_service *service;
int error;
@@ -683,14 +684,14 @@ static int http_action(
stream = git__calloc(sizeof(http_stream), 1);
GIT_ERROR_CHECK_ALLOC(stream);
- if (!transport->http_client) {
- git_http_client_options opts = {0};
-
- opts.server_certificate_check_cb = connect_opts->callbacks.certificate_check;
- opts.server_certificate_check_payload = connect_opts->callbacks.payload;
- opts.proxy_certificate_check_cb = connect_opts->proxy_opts.certificate_check;
- opts.proxy_certificate_check_payload = connect_opts->proxy_opts.payload;
+ opts.server_certificate_check_cb = connect_opts->callbacks.certificate_check;
+ opts.server_certificate_check_payload = connect_opts->callbacks.payload;
+ opts.proxy_certificate_check_cb = connect_opts->proxy_opts.certificate_check;
+ opts.proxy_certificate_check_payload = connect_opts->proxy_opts.payload;
+ if (transport->http_client) {
+ git_http_client_set_options(transport->http_client, &opts);
+ } else {
if (git_http_client_new(&transport->http_client, &opts) < 0)
return -1;
}
diff --git a/src/libgit2/transports/httpclient.c b/src/libgit2/transports/httpclient.c
index f07923ef2..0ad6cd4dc 100644
--- a/src/libgit2/transports/httpclient.c
+++ b/src/libgit2/transports/httpclient.c
@@ -1541,6 +1541,15 @@ int git_http_client_new(
return 0;
}
+/* Update the options of an existing httpclient instance. */
+void git_http_client_set_options(
+ git_http_client *client,
+ git_http_client_options *opts)
+{
+ if (opts)
+ memcpy(&client->opts, opts, sizeof(git_http_client_options));
+}
+
GIT_INLINE(void) http_server_close(git_http_server *server)
{
if (server->stream) {
diff --git a/src/libgit2/transports/httpclient.h b/src/libgit2/transports/httpclient.h
index 6d0ef9edb..22c4dd093 100644
--- a/src/libgit2/transports/httpclient.h
+++ b/src/libgit2/transports/httpclient.h
@@ -88,6 +88,16 @@ extern int git_http_client_new(
git_http_client **out,
git_http_client_options *opts);
+/**
+ * Update the options of an existing httpclient instance.
+ *
+ * @param client the httpclient instance to modify
+ * @param opts new options or NULL to keep existing options
+ */
+extern void git_http_client_set_options(
+ git_http_client *client,
+ git_http_client_options *opts);
+
/*
* Sends a request to the host specified by the request URL. If the
* method is POST, either the content_length or the chunked flag must
diff --git a/src/libgit2/transports/local.c b/src/libgit2/transports/local.c
index 24f49cc65..f576682a7 100644
--- a/src/libgit2/transports/local.c
+++ b/src/libgit2/transports/local.c
@@ -266,6 +266,17 @@ static int local_capabilities(unsigned int *capabilities, git_transport *transpo
return 0;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+static int local_oid_type(git_oid_t *out, git_transport *transport)
+{
+ transport_local *t = (transport_local *)transport;
+
+ *out = t->repo->oid_type;
+
+ return 0;
+}
+#endif
+
static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_local *t = (transport_local *)transport;
@@ -730,6 +741,9 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
t->parent.connect = local_connect;
t->parent.set_connect_opts = local_set_connect_opts;
t->parent.capabilities = local_capabilities;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ t->parent.oid_type = local_oid_type;
+#endif
t->parent.negotiate_fetch = local_negotiate_fetch;
t->parent.download_pack = local_download_pack;
t->parent.push = local_push;
diff --git a/src/libgit2/transports/smart.c b/src/libgit2/transports/smart.c
index 04ee7e740..da6dca039 100644
--- a/src/libgit2/transports/smart.c
+++ b/src/libgit2/transports/smart.c
@@ -54,6 +54,12 @@ GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransp
return -1;
}
+ git__free(t->caps.object_format);
+ t->caps.object_format = NULL;
+
+ git__free(t->caps.agent);
+ t->caps.agent = NULL;
+
return 0;
}
@@ -242,6 +248,30 @@ static int git_smart__capabilities(unsigned int *capabilities, git_transport *tr
return 0;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+static int git_smart__oid_type(git_oid_t *out, git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ *out = 0;
+
+ if (t->caps.object_format == NULL) {
+ *out = GIT_OID_DEFAULT;
+ } else {
+ *out = git_oid_type_fromstr(t->caps.object_format);
+
+ if (!*out) {
+ git_error_set(GIT_ERROR_INVALID,
+ "unknown object format '%s'",
+ t->caps.object_format);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+#endif
+
static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
@@ -386,6 +416,8 @@ static void git_smart__free(git_transport *transport)
git_remote_connect_options_dispose(&t->connect_opts);
+ git__free(t->caps.object_format);
+ git__free(t->caps.agent);
git__free(t);
}
@@ -452,6 +484,9 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
t->parent.connect = git_smart__connect;
t->parent.set_connect_opts = git_smart__set_connect_opts;
t->parent.capabilities = git_smart__capabilities;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ t->parent.oid_type = git_smart__oid_type;
+#endif
t->parent.close = git_smart__close;
t->parent.free = git_smart__free;
t->parent.negotiate_fetch = git_smart__negotiate_fetch;
diff --git a/src/libgit2/transports/smart.h b/src/libgit2/transports/smart.h
index bc072d2fe..15b6c9a0a 100644
--- a/src/libgit2/transports/smart.h
+++ b/src/libgit2/transports/smart.h
@@ -34,6 +34,8 @@
#define GIT_CAP_WANT_TIP_SHA1 "allow-tip-sha1-in-want"
#define GIT_CAP_WANT_REACHABLE_SHA1 "allow-reachable-sha1-in-want"
#define GIT_CAP_SHALLOW "shallow"
+#define GIT_CAP_OBJECT_FORMAT "object-format="
+#define GIT_CAP_AGENT "agent="
extern bool git_smart__ofs_delta_enabled;
@@ -143,6 +145,8 @@ typedef struct transport_smart_caps {
want_tip_sha1:1,
want_reachable_sha1:1,
shallow:1;
+ char *object_format;
+ char *agent;
} transport_smart_caps;
typedef int (*packetsize_cb)(size_t received, void *payload);
@@ -191,7 +195,12 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream
int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
/* smart_pkt.c */
-int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen);
+typedef struct {
+ git_oid_t oid_type;
+ int seen_capabilities: 1;
+} git_pkt_parse_data;
+
+int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen, git_pkt_parse_data *data);
int git_pkt_buffer_flush(git_str *buf);
int git_pkt_send_flush(GIT_SOCKET s);
int git_pkt_buffer_done(git_str *buf);
diff --git a/src/libgit2/transports/smart_pkt.c b/src/libgit2/transports/smart_pkt.c
index 9e9bda020..ec9764a87 100644
--- a/src/libgit2/transports/smart_pkt.c
+++ b/src/libgit2/transports/smart_pkt.c
@@ -21,11 +21,14 @@
#include <ctype.h>
-#define PKT_LEN_SIZE 4
-static const char pkt_done_str[] = "0009done\n";
-static const char pkt_flush_str[] = "0000";
-static const char pkt_have_prefix[] = "0032have ";
-static const char pkt_want_prefix[] = "0032want ";
+#define PKT_DONE_STR "0009done\n"
+#define PKT_FLUSH_STR "0000"
+#define PKT_HAVE_PREFIX "have "
+#define PKT_WANT_PREFIX "want "
+
+#define PKT_LEN_SIZE 4
+#define PKT_MAX_SIZE 0xffff
+#define PKT_MAX_WANTLEN (PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) + GIT_OID_MAX_HEXSIZE + 1)
static int flush_pkt(git_pkt **out)
{
@@ -212,26 +215,87 @@ static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
return 0;
}
+static int set_data(
+ git_pkt_parse_data *data,
+ const char *line,
+ size_t len)
+{
+ const char *caps, *format_str = NULL, *eos;
+ size_t format_len;
+ git_oid_t remote_oid_type;
+
+ GIT_ASSERT_ARG(data);
+
+ if ((caps = memchr(line, '\0', len)) != NULL) {
+ caps++;
+
+ if (strncmp(caps, "object-format=", CONST_STRLEN("object-format=")) == 0)
+ format_str = caps + CONST_STRLEN("object-format=");
+ else if ((format_str = strstr(caps, " object-format=")) != NULL)
+ format_str += CONST_STRLEN(" object-format=");
+ }
+
+ if (format_str) {
+ if ((eos = strchr(format_str, ' ')) == NULL)
+ eos = strchr(format_str, '\0');
+
+ GIT_ASSERT(eos);
+
+ format_len = eos - format_str;
+
+ if ((remote_oid_type = git_oid_type_fromstrn(format_str, format_len)) == 0) {
+ git_error_set(GIT_ERROR_INVALID, "unknown remote object format '%.*s'", (int)format_len, format_str);
+ return -1;
+ }
+ } else {
+ remote_oid_type = GIT_OID_SHA1;
+ }
+
+ if (!data->oid_type) {
+ data->oid_type = remote_oid_type;
+ } else if (data->oid_type != remote_oid_type) {
+ git_error_set(GIT_ERROR_INVALID,
+ "the local object format '%s' does not match the remote object format '%s'",
+ git_oid_type_name(data->oid_type),
+ git_oid_type_name(remote_oid_type));
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* Parse an other-ref line.
*/
-static int ref_pkt(git_pkt **out, const char *line, size_t len)
+static int ref_pkt(
+ git_pkt **out,
+ const char *line,
+ size_t len,
+ git_pkt_parse_data *data)
{
git_pkt_ref *pkt;
- size_t alloclen;
+ size_t alloclen, oid_hexsize;
pkt = git__calloc(1, sizeof(git_pkt_ref));
GIT_ERROR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_REF;
- if (len < GIT_OID_SHA1_HEXSIZE ||
- git_oid__fromstr(&pkt->head.oid, line, GIT_OID_SHA1) < 0)
+ /* Determine OID type from capabilities */
+ if (!data->seen_capabilities && set_data(data, line, len) < 0)
+ return -1;
+
+ GIT_ASSERT(data->oid_type);
+ oid_hexsize = git_oid_hexsize(data->oid_type);
+
+ if (len < oid_hexsize ||
+ git_oid__fromstr(&pkt->head.oid, line, data->oid_type) < 0)
goto out_err;
- line += GIT_OID_SHA1_HEXSIZE;
- len -= GIT_OID_SHA1_HEXSIZE;
+ line += oid_hexsize;
+ len -= oid_hexsize;
if (git__prefixncmp(line, len, " "))
goto out_err;
+
line++;
len--;
@@ -248,8 +312,14 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
memcpy(pkt->head.name, line, len);
pkt->head.name[len] = '\0';
- if (strlen(pkt->head.name) < len)
- pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
+ if (strlen(pkt->head.name) < len) {
+ if (!data->seen_capabilities)
+ pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
+ else
+ goto out_err;
+ }
+
+ data->seen_capabilities = 1;
*out = (git_pkt *)pkt;
return 0;
@@ -462,7 +532,11 @@ static int parse_len(size_t *out, const char *line, size_t linelen)
*/
int git_pkt_parse_line(
- git_pkt **pkt, const char **endptr, const char *line, size_t linelen)
+ git_pkt **pkt,
+ const char **endptr,
+ const char *line,
+ size_t linelen,
+ git_pkt_parse_data *data)
{
int error;
size_t len;
@@ -541,7 +615,7 @@ int git_pkt_parse_line(
else if (!git__prefixcmp(line, "unshallow"))
error = unshallow_pkt(pkt, line, len);
else
- error = ref_pkt(pkt, line, len);
+ error = ref_pkt(pkt, line, len, data);
*endptr = line + len;
@@ -575,14 +649,21 @@ void git_pkt_free(git_pkt *pkt)
int git_pkt_buffer_flush(git_str *buf)
{
- return git_str_put(buf, pkt_flush_str, strlen(pkt_flush_str));
+ return git_str_put(buf, PKT_FLUSH_STR, CONST_STRLEN(PKT_FLUSH_STR));
}
-static int buffer_want_with_caps(const git_remote_head *head, transport_smart_caps *caps, git_str *buf)
+static int buffer_want_with_caps(
+ const git_remote_head *head,
+ transport_smart_caps *caps,
+ git_oid_t oid_type,
+ git_str *buf)
{
git_str str = GIT_STR_INIT;
- char oid[GIT_OID_SHA1_HEXSIZE +1] = {0};
- size_t len;
+ char oid[GIT_OID_MAX_HEXSIZE];
+ size_t oid_hexsize, len;
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+ git_oid_fmt(oid, &head->oid);
/* Prefer multi_ack_detailed */
if (caps->multi_ack_detailed)
@@ -611,19 +692,20 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
if (git_str_oom(&str))
return -1;
- len = strlen("XXXXwant ") + GIT_OID_SHA1_HEXSIZE + 1 /* NUL */ +
- git_str_len(&str) + 1 /* LF */;
-
- if (len > 0xffff) {
+ if (str.size > (PKT_MAX_SIZE - (PKT_MAX_WANTLEN + 1))) {
git_error_set(GIT_ERROR_NET,
- "tried to produce packet with invalid length %" PRIuZ, len);
+ "tried to produce packet with invalid caps length %" PRIuZ, str.size);
return -1;
}
+ len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
+ oid_hexsize + 1 /* NUL */ +
+ git_str_len(&str) + 1 /* LF */;
+
git_str_grow_by(buf, len);
- git_oid_fmt(oid, &head->oid);
git_str_printf(buf,
- "%04xwant %s %s\n", (unsigned int)len, oid, git_str_cstr(&str));
+ "%04x%s%.*s %s\n", (unsigned int)len, PKT_WANT_PREFIX,
+ (int)oid_hexsize, oid, git_str_cstr(&str));
git_str_dispose(&str);
GIT_ERROR_CHECK_ALLOC_STR(buf);
@@ -641,8 +723,21 @@ int git_pkt_buffer_wants(
transport_smart_caps *caps,
git_str *buf)
{
- size_t i = 0;
const git_remote_head *head;
+ char oid[GIT_OID_MAX_HEXSIZE];
+ git_oid_t oid_type;
+ size_t oid_hexsize, want_len, i = 0;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = wants->count > 0 ? wants->refs[0]->oid.type : GIT_OID_SHA1;
+#else
+ oid_type = GIT_OID_SHA1;
+#endif
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+
+ want_len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
+ oid_hexsize + 1 /* LF */;
if (caps->common) {
for (; i < wants->count; ++i) {
@@ -651,23 +746,24 @@ int git_pkt_buffer_wants(
break;
}
- if (buffer_want_with_caps(wants->refs[i], caps, buf) < 0)
+ if (buffer_want_with_caps(wants->refs[i], caps, oid_type, buf) < 0)
return -1;
i++;
}
for (; i < wants->count; ++i) {
- char oid[GIT_OID_SHA1_HEXSIZE];
-
head = wants->refs[i];
+
if (head->local)
continue;
git_oid_fmt(oid, &head->oid);
- git_str_put(buf, pkt_want_prefix, strlen(pkt_want_prefix));
- git_str_put(buf, oid, GIT_OID_SHA1_HEXSIZE);
- git_str_putc(buf, '\n');
+
+ git_str_printf(buf, "%04x%s%.*s\n",
+ (unsigned int)want_len, PKT_WANT_PREFIX,
+ (int)oid_hexsize, oid);
+
if (git_str_oom(buf))
return -1;
}
@@ -707,14 +803,27 @@ int git_pkt_buffer_wants(
int git_pkt_buffer_have(git_oid *oid, git_str *buf)
{
- char oidhex[GIT_OID_SHA1_HEXSIZE + 1];
-
- memset(oidhex, 0x0, sizeof(oidhex));
- git_oid_fmt(oidhex, oid);
- return git_str_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
+ char oid_str[GIT_OID_MAX_HEXSIZE];
+ git_oid_t oid_type;
+ size_t oid_hexsize, have_len;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = oid->type;
+#else
+ oid_type = GIT_OID_SHA1;
+#endif
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+ have_len = PKT_LEN_SIZE + CONST_STRLEN(PKT_HAVE_PREFIX) +
+ oid_hexsize + 1 /* LF */;
+
+ git_oid_fmt(oid_str, oid);
+ return git_str_printf(buf, "%04x%s%.*s\n",
+ (unsigned int)have_len, PKT_HAVE_PREFIX,
+ (int)oid_hexsize, oid_str);
}
int git_pkt_buffer_done(git_str *buf)
{
- return git_str_puts(buf, pkt_done_str);
+ return git_str_put(buf, PKT_DONE_STR, CONST_STRLEN(PKT_DONE_STR));
}
diff --git a/src/libgit2/transports/smart_protocol.c b/src/libgit2/transports/smart_protocol.c
index a44d0c853..2ec390101 100644
--- a/src/libgit2/transports/smart_protocol.c
+++ b/src/libgit2/transports/smart_protocol.c
@@ -32,6 +32,7 @@ int git_smart__store_refs(transport_smart *t, int flushes)
int error, flush = 0, recvd;
const char *line_end = NULL;
git_pkt *pkt = NULL;
+ git_pkt_parse_data pkt_parse_data = { 0 };
size_t i;
/* Clear existing refs in case git_remote_connect() is called again
@@ -45,7 +46,7 @@ int git_smart__store_refs(transport_smart *t, int flushes)
do {
if (buf->offset > 0)
- error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset);
+ error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset, &pkt_parse_data);
else
error = GIT_EBUFS;
@@ -133,9 +134,12 @@ on_invalid:
return -1;
}
-int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
+int git_smart__detect_caps(
+ git_pkt_ref *pkt,
+ transport_smart_caps *caps,
+ git_vector *symrefs)
{
- const char *ptr;
+ const char *ptr, *start;
/* No refs or capabilities, odd but not a problem */
if (pkt == NULL || pkt->capabilities == NULL)
@@ -207,15 +211,38 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec
if (!git__prefixcmp(ptr, GIT_CAP_WANT_TIP_SHA1)) {
caps->common = caps->want_tip_sha1 = 1;
- ptr += strlen(GIT_CAP_DELETE_REFS);
+ ptr += strlen(GIT_CAP_WANT_TIP_SHA1);
continue;
}
if (!git__prefixcmp(ptr, GIT_CAP_WANT_REACHABLE_SHA1)) {
caps->common = caps->want_reachable_sha1 = 1;
- ptr += strlen(GIT_CAP_DELETE_REFS);
+ ptr += strlen(GIT_CAP_WANT_REACHABLE_SHA1);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_OBJECT_FORMAT)) {
+ ptr += strlen(GIT_CAP_OBJECT_FORMAT);
+
+ start = ptr;
+ ptr = strchr(ptr, ' ');
+
+ if ((caps->object_format = git__strndup(start, (ptr - start))) == NULL)
+ return -1;
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_AGENT)) {
+ ptr += strlen(GIT_CAP_AGENT);
+
+ start = ptr;
+ ptr = strchr(ptr, ' ');
+
+ if ((caps->agent = git__strndup(start, (ptr - start))) == NULL)
+ return -1;
+ continue;
}
-
+
if (!git__prefixcmp(ptr, GIT_CAP_SHALLOW)) {
caps->common = caps->shallow = 1;
ptr += strlen(GIT_CAP_SHALLOW);
@@ -233,11 +260,12 @@ static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf
{
const char *ptr = buf->data, *line_end = ptr;
git_pkt *pkt = NULL;
+ git_pkt_parse_data pkt_parse_data = { 0 };
int error = 0, ret;
do {
if (buf->offset > 0)
- error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset);
+ error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset, &pkt_parse_data);
else
error = GIT_EBUFS;
@@ -779,6 +807,7 @@ static int add_push_report_pkt(git_push *push, git_pkt *pkt)
static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_str *data_pkt_buf)
{
git_pkt *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
const char *line, *line_end = NULL;
size_t line_len;
int error;
@@ -797,7 +826,7 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt,
}
while (line_len > 0) {
- error = git_pkt_parse_line(&pkt, &line_end, line, line_len);
+ error = git_pkt_parse_line(&pkt, &line_end, line, line_len, &pkt_parse_data);
if (error == GIT_EBUFS) {
/* Buffer the data when the inner packet is split
@@ -833,6 +862,7 @@ done:
static int parse_report(transport_smart *transport, git_push *push)
{
git_pkt *pkt = NULL;
+ git_pkt_parse_data pkt_parse_data = { 0 };
const char *line_end = NULL;
gitno_buffer *buf = &transport->buffer;
int error, recvd;
@@ -841,7 +871,8 @@ static int parse_report(transport_smart *transport, git_push *push)
for (;;) {
if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, &line_end,
- buf->data, buf->offset);
+ buf->data, buf->offset,
+ &pkt_parse_data);
else
error = GIT_EBUFS;
diff --git a/src/libgit2/transports/ssh.c b/src/libgit2/transports/ssh.c
index 89f085230..5500ea100 100644
--- a/src/libgit2/transports/ssh.c
+++ b/src/libgit2/transports/ssh.c
@@ -16,6 +16,7 @@
#include "netops.h"
#include "smart.h"
#include "streams/socket.h"
+#include "sysdir.h"
#include "git2/credential.h"
#include "git2/sys/credential.h"
@@ -245,8 +246,10 @@ static int ssh_agent_auth(LIBSSH2_SESSION *session, git_credential_ssh_key *c) {
rc = libssh2_agent_connect(agent);
- if (rc != LIBSSH2_ERROR_NONE)
+ if (rc != LIBSSH2_ERROR_NONE) {
+ rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
goto shutdown;
+ }
rc = libssh2_agent_list_identities(agent);
@@ -421,15 +424,116 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char *
return 0;
}
+#define SSH_DIR ".ssh"
+#define KNOWN_HOSTS_FILE "known_hosts"
+
+/*
+ * Load the known_hosts file.
+ *
+ * Returns success but leaves the output NULL if we couldn't find the file.
+ */
+static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session)
+{
+ git_str path = GIT_STR_INIT, sshdir = GIT_STR_INIT;
+ LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(hosts);
+
+ if ((error = git_sysdir_expand_homedir_file(&sshdir, SSH_DIR)) < 0 ||
+ (error = git_str_joinpath(&path, git_str_cstr(&sshdir), KNOWN_HOSTS_FILE)) < 0)
+ goto out;
+
+ if ((known_hosts = libssh2_knownhost_init(session)) == NULL) {
+ ssh_error(session, "error initializing known hosts");
+ error = -1;
+ goto out;
+ }
+
+ /*
+ * Try to read the file and consider not finding it as not trusting the
+ * host rather than an error.
+ */
+ error = libssh2_knownhost_readfile(known_hosts, git_str_cstr(&path), LIBSSH2_KNOWNHOST_FILE_OPENSSH);
+ if (error == LIBSSH2_ERROR_FILE)
+ error = 0;
+ if (error < 0)
+ ssh_error(session, "error reading known_hosts");
+
+out:
+ *hosts = known_hosts;
+
+ git_str_dispose(&sshdir);
+ git_str_dispose(&path);
+
+ return error;
+}
+
+static void add_hostkey_pref_if_avail(
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ const char *hostname,
+ int port,
+ git_str *prefs,
+ int type,
+ const char *type_name)
+{
+ struct libssh2_knownhost *host = NULL;
+ const char key = '\0';
+ int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | type;
+ int error;
+
+ error = libssh2_knownhost_checkp(known_hosts, hostname, port, &key, 1, mask, &host);
+ if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
+ if (git_str_len(prefs) > 0) {
+ git_str_putc(prefs, ',');
+ }
+ git_str_puts(prefs, type_name);
+ }
+}
+
+/*
+ * We figure out what kind of key we want to ask the remote for by trying to
+ * look it up with a nonsense key and using that mismatch to figure out what key
+ * we do have stored for the host.
+ *
+ * Populates prefs with the string to pass to libssh2_session_method_pref.
+ */
+static void find_hostkey_preference(
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ const char *hostname,
+ int port,
+ git_str *prefs)
+{
+ /*
+ * The order here is important as it indicates the priority of what will
+ * be preferred.
+ */
+#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ED25519, "ssh-ed25519");
+#endif
+#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_256, "ecdsa-sha2-nistp256");
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_384, "ecdsa-sha2-nistp384");
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_521, "ecdsa-sha2-nistp521");
+#endif
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_SSHRSA, "ssh-rsa");
+}
+
static int _git_ssh_session_create(
LIBSSH2_SESSION **session,
+ LIBSSH2_KNOWNHOSTS **hosts,
+ const char *hostname,
+ int port,
git_stream *io)
{
- int rc = 0;
- LIBSSH2_SESSION *s;
git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent);
+ LIBSSH2_SESSION *s;
+ LIBSSH2_KNOWNHOSTS *known_hosts;
+ git_str prefs = GIT_STR_INIT;
+ int rc = 0;
GIT_ASSERT_ARG(session);
+ GIT_ASSERT_ARG(hosts);
s = libssh2_session_init();
if (!s) {
@@ -437,150 +541,276 @@ static int _git_ssh_session_create(
return -1;
}
+ if ((rc = load_known_hosts(&known_hosts, s)) < 0) {
+ ssh_error(s, "error loading known_hosts");
+ libssh2_session_free(s);
+ return -1;
+ }
+
+ find_hostkey_preference(known_hosts, hostname, port, &prefs);
+ if (git_str_len(&prefs) > 0) {
+ do {
+ rc = libssh2_session_method_pref(s, LIBSSH2_METHOD_HOSTKEY, git_str_cstr(&prefs));
+ } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+ if (rc != LIBSSH2_ERROR_NONE) {
+ ssh_error(s, "failed to set hostkey preference");
+ goto on_error;
+ }
+ }
+ git_str_dispose(&prefs);
+
do {
rc = libssh2_session_handshake(s, socket->s);
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
if (rc != LIBSSH2_ERROR_NONE) {
ssh_error(s, "failed to start SSH session");
- libssh2_session_free(s);
- return -1;
+ goto on_error;
}
libssh2_session_set_blocking(s, 1);
*session = s;
+ *hosts = known_hosts;
return 0;
+
+on_error:
+ libssh2_knownhost_free(known_hosts);
+ libssh2_session_free(s);
+ return -1;
}
-#define SSH_DEFAULT_PORT "22"
-static int _git_ssh_setup_conn(
- ssh_subtransport *t,
- const char *url,
- const char *cmd,
- git_smart_subtransport_stream **stream)
+/*
+ * Returns the typemask argument to pass to libssh2_knownhost_check{,p} based on
+ * the type of key that libssh2_session_hostkey returns.
+ */
+static int fingerprint_type_mask(int keytype)
{
- int auth_methods, error = 0;
- ssh_stream *s;
- git_credential *cred = NULL;
- LIBSSH2_SESSION *session=NULL;
- LIBSSH2_CHANNEL *channel=NULL;
+ int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW;
+ return mask;
+
+ switch (keytype) {
+ case LIBSSH2_HOSTKEY_TYPE_RSA:
+ mask |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_DSS:
+ mask |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
+ break;
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
+ case LIBSSH2_HOSTKEY_TYPE_ED25519:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ED25519;
+ break;
+#endif
+ }
- t->current_stream = NULL;
+ return mask;
+}
- *stream = NULL;
- if (ssh_stream_alloc(t, cmd, stream) < 0)
+/*
+ * Check the host against the user's known_hosts file.
+ *
+ * Returns 1/0 for valid/''not-valid or <0 for an error
+ */
+static int check_against_known_hosts(
+ LIBSSH2_SESSION *session,
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ const char *hostname,
+ int port,
+ const char *key,
+ size_t key_len,
+ int key_type)
+{
+ int check, typemask, ret = 0;
+ struct libssh2_knownhost *host = NULL;
+
+ if (known_hosts == NULL)
+ return 0;
+
+ typemask = fingerprint_type_mask(key_type);
+ check = libssh2_knownhost_checkp(known_hosts, hostname, port, key, key_len, typemask, &host);
+ if (check == LIBSSH2_KNOWNHOST_CHECK_FAILURE) {
+ ssh_error(session, "error checking for known host");
return -1;
+ }
- s = (ssh_stream *)*stream;
- s->session = NULL;
- s->channel = NULL;
+ ret = check == LIBSSH2_KNOWNHOST_CHECK_MATCH ? 1 : 0;
- if (git_net_str_is_url(url))
- error = git_net_url_parse(&s->url, url);
- else
- error = git_net_url_parse_scp(&s->url, url);
+ return ret;
+}
- if (error < 0)
- goto done;
+/*
+ * Perform the check for the session's certificate against known hosts if
+ * possible and then ask the user if they have a callback.
+ *
+ * Returns 1/0 for valid/not-valid or <0 for an error
+ */
+static int check_certificate(
+ LIBSSH2_SESSION *session,
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ git_transport_certificate_check_cb check_cb,
+ void *check_cb_payload,
+ const char *host,
+ int port)
+{
+ git_cert_hostkey cert = {{ 0 }};
+ const char *key;
+ size_t cert_len;
+ int cert_type, cert_valid = 0, error = 0;
- if ((error = git_socket_stream_new(&s->io, s->url.host, s->url.port)) < 0 ||
- (error = git_stream_connect(s->io)) < 0)
- goto done;
+ if ((key = libssh2_session_hostkey(session, &cert_len, &cert_type)) == NULL) {
+ ssh_error(session, "failed to retrieve hostkey");
+ return -1;
+ }
- if ((error = _git_ssh_session_create(&session, s->io)) < 0)
- goto done;
+ if ((cert_valid = check_against_known_hosts(session, known_hosts, host, port, key, cert_len, cert_type)) < 0)
+ return -1;
- if (t->owner->connect_opts.callbacks.certificate_check != NULL) {
- git_cert_hostkey cert = {{ 0 }}, *cert_ptr;
- const char *key;
- size_t cert_len;
- int cert_type;
-
- cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
-
- key = libssh2_session_hostkey(session, &cert_len, &cert_type);
- if (key != NULL) {
- cert.type |= GIT_CERT_SSH_RAW;
- cert.hostkey = key;
- cert.hostkey_len = cert_len;
- switch (cert_type) {
- case LIBSSH2_HOSTKEY_TYPE_RSA:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_RSA;
- break;
- case LIBSSH2_HOSTKEY_TYPE_DSS:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_DSS;
- break;
+ cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_RAW;
+ cert.hostkey = key;
+ cert.hostkey_len = cert_len;
+ switch (cert_type) {
+ case LIBSSH2_HOSTKEY_TYPE_RSA:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_RSA;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_DSS:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_DSS;
+ break;
#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
- case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256;
- break;
- case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384;
- break;
- case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521;
- break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521;
+ break;
#endif
#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
- case LIBSSH2_HOSTKEY_TYPE_ED25519:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ED25519;
- break;
+ case LIBSSH2_HOSTKEY_TYPE_ED25519:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ED25519;
+ break;
#endif
- default:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_UNKNOWN;
- }
+ default:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_UNKNOWN;
}
+ }
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
- key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
- if (key != NULL) {
- cert.type |= GIT_CERT_SSH_SHA256;
- memcpy(&cert.hash_sha256, key, 32);
- }
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_SHA256;
+ memcpy(&cert.hash_sha256, key, 32);
+ }
#endif
- key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
- if (key != NULL) {
- cert.type |= GIT_CERT_SSH_SHA1;
- memcpy(&cert.hash_sha1, key, 20);
- }
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_SHA1;
+ memcpy(&cert.hash_sha1, key, 20);
+ }
- key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
- if (key != NULL) {
- cert.type |= GIT_CERT_SSH_MD5;
- memcpy(&cert.hash_md5, key, 16);
- }
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_MD5;
+ memcpy(&cert.hash_md5, key, 16);
+ }
- if (cert.type == 0) {
- git_error_set(GIT_ERROR_SSH, "unable to get the host key");
- error = -1;
- goto done;
+ if (cert.type == 0) {
+ git_error_set(GIT_ERROR_SSH, "unable to get the host key");
+ return -1;
+ }
+
+ git_error_clear();
+ error = 0;
+ if (!cert_valid) {
+ git_error_set(GIT_ERROR_SSH, "invalid or unknown remote ssh hostkey");
+ error = GIT_ECERTIFICATE;
+ }
+
+ if (check_cb != NULL) {
+ git_cert_hostkey *cert_ptr = &cert;
+ git_error_state previous_error = {0};
+
+ git_error_state_capture(&previous_error, error);
+ error = check_cb((git_cert *) cert_ptr, cert_valid, host, check_cb_payload);
+ if (error == GIT_PASSTHROUGH) {
+ error = git_error_state_restore(&previous_error);
+ } else if (error < 0 && !git_error_last()) {
+ git_error_set(GIT_ERROR_NET, "unknown remote host key");
}
- /* We don't currently trust any hostkeys */
- git_error_clear();
+ git_error_state_free(&previous_error);
+ }
- cert_ptr = &cert;
+ return error;
+}
- error = t->owner->connect_opts.callbacks.certificate_check(
- (git_cert *)cert_ptr,
- 0,
- s->url.host,
- t->owner->connect_opts.callbacks.payload);
+#define SSH_DEFAULT_PORT "22"
- if (error < 0 && error != GIT_PASSTHROUGH) {
- if (!git_error_last())
- git_error_set(GIT_ERROR_NET, "user cancelled hostkey check");
+static int _git_ssh_setup_conn(
+ ssh_subtransport *t,
+ const char *url,
+ const char *cmd,
+ git_smart_subtransport_stream **stream)
+{
+ int auth_methods, error = 0, port;
+ ssh_stream *s;
+ git_credential *cred = NULL;
+ LIBSSH2_SESSION *session=NULL;
+ LIBSSH2_CHANNEL *channel=NULL;
+ LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
- goto done;
- }
+ t->current_stream = NULL;
+
+ *stream = NULL;
+ if (ssh_stream_alloc(t, cmd, stream) < 0)
+ return -1;
+
+ s = (ssh_stream *)*stream;
+ s->session = NULL;
+ s->channel = NULL;
+
+ if ((error = git_net_url_parse_standard_or_scp(&s->url, url)) < 0 ||
+ (error = git_socket_stream_new(&s->io, s->url.host, s->url.port)) < 0 ||
+ (error = git_stream_connect(s->io)) < 0)
+ goto done;
+
+ /*
+ * Try to parse the port as a number, if we can't then fall back to
+ * default. It would be nice if we could get the port that was resolved
+ * as part of the stream connection, but that's not something that's
+ * exposed.
+ */
+ if (git__strntol32(&port, s->url.port, strlen(s->url.port), NULL, 10) < 0) {
+ git_error_set(GIT_ERROR_NET, "invalid port to ssh: %s", s->url.port);
+ error = -1;
+ goto done;
}
+ if ((error = _git_ssh_session_create(&session, &known_hosts, s->url.host, port, s->io)) < 0)
+ goto done;
+
+ if ((error = check_certificate(session, known_hosts, t->owner->connect_opts.callbacks.certificate_check, t->owner->connect_opts.callbacks.payload, s->url.host, port)) < 0)
+ goto done;
+
/* we need the username to ask for auth methods */
if (!s->url.username) {
if ((error = request_creds(&cred, t, NULL, GIT_CREDENTIAL_USERNAME)) < 0)
@@ -651,6 +881,8 @@ done:
if (error < 0) {
ssh_stream_free(*stream);
+ if (known_hosts)
+ libssh2_knownhost_free(known_hosts);
if (session)
libssh2_session_free(session);
}
@@ -774,7 +1006,7 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use
/* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
if (list == NULL && !libssh2_userauth_authenticated(session)) {
- ssh_error(session, "Failed to retrieve list of SSH authentication methods");
+ ssh_error(session, "remote rejected authentication");
return GIT_EAUTH;
}
diff --git a/src/libgit2/transports/winhttp.c b/src/libgit2/transports/winhttp.c
index 8ec5b37c5..098227607 100644
--- a/src/libgit2/transports/winhttp.c
+++ b/src/libgit2/transports/winhttp.c
@@ -562,18 +562,23 @@ static int winhttp_stream_connect(winhttp_stream *s)
for (i = 0; i < t->owner->connect_opts.custom_headers.count; i++) {
if (t->owner->connect_opts.custom_headers.strings[i]) {
+ wchar_t *custom_header_wide = NULL;
+
git_str_clear(&buf);
git_str_puts(&buf, t->owner->connect_opts.custom_headers.strings[i]);
- if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
- git_error_set(GIT_ERROR_OS, "failed to convert custom header to wide characters");
+
+ /* Convert header to wide characters */
+ if ((error = git__utf8_to_16_alloc(&custom_header_wide, git_str_cstr(&buf))) < 0)
goto on_error;
- }
- if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
+ if (!WinHttpAddRequestHeaders(s->request, custom_header_wide, (ULONG)-1L,
WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
git_error_set(GIT_ERROR_OS, "failed to add a header to the request");
+ git__free(custom_header_wide);
goto on_error;
}
+
+ git__free(custom_header_wide);
}
}
diff --git a/src/libgit2/tree.c b/src/libgit2/tree.c
index 9a43d585c..9293d9422 100644
--- a/src/libgit2/tree.c
+++ b/src/libgit2/tree.c
@@ -85,11 +85,17 @@ static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, co
char *filename_ptr;
size_t tree_len;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ size_t oid_size = git_oid_size(id->type);
+#else
+ size_t oid_size = GIT_OID_SHA1_SIZE;
+#endif
+
TREE_ENTRY_CHECK_NAMELEN(filename_len);
if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) ||
GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) ||
- GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, GIT_OID_SHA1_SIZE))
+ GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, oid_size))
return NULL;
entry = git__calloc(1, tree_len);
@@ -383,11 +389,12 @@ static int parse_mode(uint16_t *mode_out, const char *buffer, size_t buffer_len,
return 0;
}
-int git_tree__parse_raw(void *_tree, const char *data, size_t size)
+int git_tree__parse_raw(void *_tree, const char *data, size_t size, git_oid_t oid_type)
{
git_tree *tree = _tree;
const char *buffer;
const char *buffer_end;
+ const long oid_size = (long)git_oid_size(oid_type);
buffer = data;
buffer_end = buffer + size;
@@ -414,35 +421,33 @@ int git_tree__parse_raw(void *_tree, const char *data, size_t size)
if ((filename_len = nul - buffer) == 0 || filename_len > UINT16_MAX)
return tree_parse_error("failed to parse tree: can't parse filename", NULL);
- if ((buffer_end - (nul + 1)) < GIT_OID_SHA1_SIZE)
+ if ((buffer_end - (nul + 1)) < (long)oid_size)
return tree_parse_error("failed to parse tree: can't parse OID", NULL);
/* Allocate the entry */
- {
- entry = git_array_alloc(tree->entries);
- GIT_ERROR_CHECK_ALLOC(entry);
-
- entry->attr = attr;
- entry->filename_len = (uint16_t)filename_len;
- entry->filename = buffer;
- git_oid__fromraw(&entry->oid, ((unsigned char *) buffer + filename_len + 1), GIT_OID_SHA1);
- }
+ entry = git_array_alloc(tree->entries);
+ GIT_ERROR_CHECK_ALLOC(entry);
+ entry->attr = attr;
+ entry->filename_len = (uint16_t)filename_len;
+ entry->filename = buffer;
buffer += filename_len + 1;
- buffer += GIT_OID_SHA1_SIZE;
+
+ git_oid__fromraw(&entry->oid, (unsigned char *)buffer, oid_type);
+ buffer += oid_size;
}
return 0;
}
-int git_tree__parse(void *_tree, git_odb_object *odb_obj)
+int git_tree__parse(void *_tree, git_odb_object *odb_obj, git_oid_t oid_type)
{
git_tree *tree = _tree;
const char *data = git_odb_object_data(odb_obj);
size_t size = git_odb_object_size(odb_obj);
int error;
- if ((error = git_tree__parse_raw(tree, data, size)) < 0 ||
+ if ((error = git_tree__parse_raw(tree, data, size, oid_type)) < 0 ||
(error = git_odb_object_dup(&tree->odb_obj, odb_obj)) < 0)
return error;
@@ -506,6 +511,7 @@ static int git_treebuilder__write_with_buffer(
git_odb *odb;
git_tree_entry *entry;
git_vector entries = GIT_VECTOR_INIT;
+ size_t oid_size = git_oid_size(bld->repo->oid_type);
git_str_clear(buf);
@@ -529,7 +535,7 @@ static int git_treebuilder__write_with_buffer(
git_str_printf(buf, "%o ", entry->attr);
git_str_put(buf, entry->filename, entry->filename_len + 1);
- git_str_put(buf, (char *)entry->oid.id, GIT_OID_SHA1_SIZE);
+ git_str_put(buf, (char *)entry->oid.id, oid_size);
if (git_str_oom(buf)) {
error = -1;
diff --git a/src/libgit2/tree.h b/src/libgit2/tree.h
index 0dd963ff2..5088450ab 100644
--- a/src/libgit2/tree.h
+++ b/src/libgit2/tree.h
@@ -41,8 +41,8 @@ GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
}
void git_tree__free(void *tree);
-int git_tree__parse(void *tree, git_odb_object *obj);
-int git_tree__parse_raw(void *_tree, const char *data, size_t size);
+int git_tree__parse(void *tree, git_odb_object *obj, git_oid_t oid_type);
+int git_tree__parse_raw(void *_tree, const char *data, size_t size, git_oid_t oid_type);
/**
* Write a tree to the given repository
diff --git a/src/libgit2/worktree.c b/src/libgit2/worktree.c
index 2ac2274f1..82e1d2d7e 100644
--- a/src/libgit2/worktree.c
+++ b/src/libgit2/worktree.c
@@ -187,6 +187,11 @@ int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *na
if ((error = git_str_join3(&path, '/', repo->commondir, "worktrees", name)) < 0)
goto out;
+ if (!git_fs_path_isdir(path.ptr)) {
+ error = GIT_ENOTFOUND;
+ goto out;
+ }
+
if ((error = (open_worktree_dir(out, git_repository_workdir(repo), path.ptr, name))) < 0)
goto out;
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
index b12ce409b..2207041ef 100644
--- a/src/util/CMakeLists.txt
+++ b/src/util/CMakeLists.txt
@@ -38,6 +38,7 @@ if(USE_SHA1 STREQUAL "CollisionDetection")
target_compile_definitions(util PRIVATE SHA1DC_CUSTOM_INCLUDE_SHA1_C=\"git2_util.h\")
target_compile_definitions(util PRIVATE SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"git2_util.h\")
elseif(USE_SHA1 STREQUAL "OpenSSL" OR USE_SHA1 STREQUAL "OpenSSL-Dynamic")
+ add_definitions(-DOPENSSL_API_COMPAT=0x10100000L)
file(GLOB UTIL_SRC_SHA1 hash/openssl.*)
elseif(USE_SHA1 STREQUAL "CommonCrypto")
file(GLOB UTIL_SRC_SHA1 hash/common_crypto.*)
@@ -54,6 +55,7 @@ list(SORT UTIL_SRC_SHA1)
if(USE_SHA256 STREQUAL "Builtin")
file(GLOB UTIL_SRC_SHA256 hash/builtin.* hash/rfc6234/*)
elseif(USE_SHA256 STREQUAL "OpenSSL" OR USE_SHA256 STREQUAL "OpenSSL-Dynamic")
+ add_definitions(-DOPENSSL_API_COMPAT=0x10100000L)
file(GLOB UTIL_SRC_SHA256 hash/openssl.*)
elseif(USE_SHA256 STREQUAL "CommonCrypto")
file(GLOB UTIL_SRC_SHA256 hash/common_crypto.*)
diff --git a/src/util/fs_path.c b/src/util/fs_path.c
index 6c87bfd01..b52867e77 100644
--- a/src/util/fs_path.c
+++ b/src/util/fs_path.c
@@ -1855,7 +1855,7 @@ static int file_owner_sid(PSID *out, const char *path)
PSECURITY_DESCRIPTOR descriptor = NULL;
PSID owner_sid;
DWORD ret;
- int error = -1;
+ int error = GIT_EINVALID;
if (git_win32_path_from_utf8(path_w32, path) < 0)
return -1;
diff --git a/src/util/futils.c b/src/util/futils.c
index cb872de09..084f1cd28 100644
--- a/src/util/futils.c
+++ b/src/util/futils.c
@@ -13,9 +13,6 @@
#include "rand.h"
#include <ctype.h>
-#if GIT_WIN32
-#include "win32/findfile.h"
-#endif
#define GIT_FILEMODE_DEFAULT 0100666
diff --git a/src/util/hash.h b/src/util/hash.h
index 387c5a66f..21fcaf045 100644
--- a/src/util/hash.h
+++ b/src/util/hash.h
@@ -23,6 +23,8 @@ typedef enum {
GIT_HASH_ALGORITHM_SHA256
} git_hash_algorithm_t;
+#define GIT_HASH_MAX_SIZE GIT_HASH_SHA256_SIZE
+
typedef struct git_hash_ctx {
union {
git_hash_sha1_ctx sha1;
@@ -45,4 +47,15 @@ int git_hash_vec(unsigned char *out, git_str_vec *vec, size_t n, git_hash_algori
int git_hash_fmt(char *out, unsigned char *hash, size_t hash_len);
+GIT_INLINE(size_t) git_hash_size(git_hash_algorithm_t algorithm) {
+ switch (algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ return GIT_HASH_SHA1_SIZE;
+ case GIT_HASH_ALGORITHM_SHA256:
+ return GIT_HASH_SHA256_SIZE;
+ default:
+ return 0;
+ }
+}
+
#endif
diff --git a/src/util/hash/openssl.c b/src/util/hash/openssl.c
index 649358ca2..eaf91e74c 100644
--- a/src/util/hash/openssl.c
+++ b/src/util/hash/openssl.c
@@ -10,8 +10,8 @@
#ifdef GIT_OPENSSL_DYNAMIC
# include <dlfcn.h>
-int handle_count;
-void *openssl_handle;
+static int handle_count;
+static void *openssl_handle;
static int git_hash_openssl_global_shutdown(void)
{
@@ -30,7 +30,8 @@ static int git_hash_openssl_global_init(void)
(openssl_handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL &&
(openssl_handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL &&
(openssl_handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL &&
- (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL) {
+ (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.3", RTLD_NOW)) == NULL) {
git_error_set(GIT_ERROR_SSL, "could not load ssl libraries");
return -1;
}
diff --git a/src/util/hash/rfc6234/sha.h b/src/util/hash/rfc6234/sha.h
index e0c400ca1..0fbcc50d3 100644
--- a/src/util/hash/rfc6234/sha.h
+++ b/src/util/hash/rfc6234/sha.h
@@ -192,49 +192,6 @@ typedef struct SHA256Context SHA224Context;
typedef struct SHA512Context SHA384Context;
/*
- * This structure holds context information for all SHA
- * hashing operations.
- */
-typedef struct USHAContext {
- int whichSha; /* which SHA is being used */
- union {
- SHA1Context sha1Context;
- SHA224Context sha224Context; SHA256Context sha256Context;
- SHA384Context sha384Context; SHA512Context sha512Context;
- } ctx;
-} USHAContext;
-
-/*
- * This structure will hold context information for the HMAC
- * keyed-hashing operation.
- */
-typedef struct HMACContext {
- int whichSha; /* which SHA is being used */
- int hashSize; /* hash size of SHA being used */
- int blockSize; /* block size of SHA being used */
- USHAContext shaContext; /* SHA context */
- unsigned char k_opad[USHA_Max_Message_Block_Size];
- /* outer padding - key XORd with opad */
- int Computed; /* Is the MAC computed? */
- int Corrupted; /* Cumulative corruption code */
-
-} HMACContext;
-
-/*
- * This structure will hold context information for the HKDF
- * extract-and-expand Key Derivation Functions.
- */
-typedef struct HKDFContext {
- int whichSha; /* which SHA is being used */
- HMACContext hmacContext;
- int hashSize; /* hash size of SHA being used */
- unsigned char prk[USHAMaxHashSize];
- /* pseudo-random key - output of hkdfInput */
- int Computed; /* Is the key material computed? */
- int Corrupted; /* Cumulative corruption code */
-} HKDFContext;
-
-/*
* Function Prototypes
*/
@@ -283,73 +240,4 @@ extern int SHA512FinalBits(SHA512Context *, uint8_t bits,
extern int SHA512Result(SHA512Context *,
uint8_t Message_Digest[SHA512HashSize]);
-/* Unified SHA functions, chosen by whichSha */
-extern int USHAReset(USHAContext *context, SHAversion whichSha);
-extern int USHAInput(USHAContext *context,
- const uint8_t *bytes, unsigned int bytecount);
-extern int USHAFinalBits(USHAContext *context,
- uint8_t bits, unsigned int bit_count);
-extern int USHAResult(USHAContext *context,
- uint8_t Message_Digest[USHAMaxHashSize]);
-extern int USHABlockSize(enum SHAversion whichSha);
-extern int USHAHashSize(enum SHAversion whichSha);
-extern int USHAHashSizeBits(enum SHAversion whichSha);
-extern const char *USHAHashName(enum SHAversion whichSha);
-
-/*
- * HMAC Keyed-Hashing for Message Authentication, RFC 2104,
- * for all SHAs.
- * This interface allows a fixed-length text input to be used.
- */
-extern int hmac(SHAversion whichSha, /* which SHA algorithm to use */
- const unsigned char *text, /* pointer to data stream */
- int text_len, /* length of data stream */
- const unsigned char *key, /* pointer to authentication key */
- int key_len, /* length of authentication key */
- uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */
-
-/*
- * HMAC Keyed-Hashing for Message Authentication, RFC 2104,
- * for all SHAs.
- * This interface allows any length of text input to be used.
- */
-extern int hmacReset(HMACContext *context, enum SHAversion whichSha,
- const unsigned char *key, int key_len);
-extern int hmacInput(HMACContext *context, const unsigned char *text,
- int text_len);
-extern int hmacFinalBits(HMACContext *context, uint8_t bits,
- unsigned int bit_count);
-extern int hmacResult(HMACContext *context,
- uint8_t digest[USHAMaxHashSize]);
-
-/*
- * HKDF HMAC-based Extract-and-Expand Key Derivation Function,
- * RFC 5869, for all SHAs.
- */
-extern int hkdf(SHAversion whichSha, const unsigned char *salt,
- int salt_len, const unsigned char *ikm, int ikm_len,
- const unsigned char *info, int info_len,
- uint8_t okm[ ], int okm_len);
-extern int hkdfExtract(SHAversion whichSha, const unsigned char *salt,
- int salt_len, const unsigned char *ikm,
- int ikm_len, uint8_t prk[USHAMaxHashSize]);
-extern int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ],
- int prk_len, const unsigned char *info,
- int info_len, uint8_t okm[ ], int okm_len);
-
-/*
- * HKDF HMAC-based Extract-and-Expand Key Derivation Function,
- * RFC 5869, for all SHAs.
- * This interface allows any length of text input to be used.
- */
-extern int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
- const unsigned char *salt, int salt_len);
-extern int hkdfInput(HKDFContext *context, const unsigned char *ikm,
- int ikm_len);
-extern int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
- unsigned int ikm_bit_count);
-extern int hkdfResult(HKDFContext *context,
- uint8_t prk[USHAMaxHashSize],
- const unsigned char *info, int info_len,
- uint8_t okm[USHAMaxHashSize], int okm_len);
#endif /* _SHA_H_ */
diff --git a/src/util/net.c b/src/util/net.c
index b2236daf8..ac7befe07 100644
--- a/src/util/net.c
+++ b/src/util/net.c
@@ -93,121 +93,367 @@ int git_net_url_dup(git_net_url *out, git_net_url *in)
return 0;
}
-int git_net_url_parse(git_net_url *url, const char *given)
+static int url_invalid(const char *message)
{
- struct http_parser_url u = {0};
- bool has_scheme, has_host, has_port, has_path, has_query, has_userinfo;
- git_str scheme = GIT_STR_INIT,
- host = GIT_STR_INIT,
- port = GIT_STR_INIT,
- path = GIT_STR_INIT,
- username = GIT_STR_INIT,
- password = GIT_STR_INIT,
- query = GIT_STR_INIT;
- int error = GIT_EINVALIDSPEC;
-
- if (http_parser_parse_url(given, strlen(given), false, &u)) {
- git_error_set(GIT_ERROR_NET, "malformed URL '%s'", given);
- goto done;
- }
+ git_error_set(GIT_ERROR_NET, "invalid url: %s", message);
+ return GIT_EINVALIDSPEC;
+}
- has_scheme = !!(u.field_set & (1 << UF_SCHEMA));
- has_host = !!(u.field_set & (1 << UF_HOST));
- has_port = !!(u.field_set & (1 << UF_PORT));
- has_path = !!(u.field_set & (1 << UF_PATH));
- has_query = !!(u.field_set & (1 << UF_QUERY));
- has_userinfo = !!(u.field_set & (1 << UF_USERINFO));
+static int url_parse_authority(
+ const char **user_start, size_t *user_len,
+ const char **password_start, size_t *password_len,
+ const char **host_start, size_t *host_len,
+ const char **port_start, size_t *port_len,
+ const char *authority_start, size_t len,
+ const char *scheme_start, size_t scheme_len)
+{
+ const char *c, *hostport_end, *host_end = NULL,
+ *userpass_end, *user_end = NULL;
- if (has_scheme) {
- const char *url_scheme = given + u.field_data[UF_SCHEMA].off;
- size_t url_scheme_len = u.field_data[UF_SCHEMA].len;
- git_str_put(&scheme, url_scheme, url_scheme_len);
- git__strntolower(scheme.ptr, scheme.size);
- } else {
- git_error_set(GIT_ERROR_NET, "malformed URL '%s'", given);
- goto done;
- }
+ enum {
+ HOSTPORT, HOST, IPV6, HOST_END, USERPASS, USER
+ } state = HOSTPORT;
- if (has_host) {
- const char *url_host = given + u.field_data[UF_HOST].off;
- size_t url_host_len = u.field_data[UF_HOST].len;
- git_str_decode_percent(&host, url_host, url_host_len);
- }
+ if (len == 0)
+ return 0;
- if (has_port) {
- const char *url_port = given + u.field_data[UF_PORT].off;
- size_t url_port_len = u.field_data[UF_PORT].len;
- git_str_put(&port, url_port, url_port_len);
- } else {
- const char *default_port = default_port_for_scheme(scheme.ptr);
+ /*
+ * walk the authority backwards so that we can parse google code's
+ * ssh urls that are not rfc compliant and allow @ in the username
+ */
+ for (hostport_end = authority_start + len, c = hostport_end - 1;
+ c >= authority_start && !user_end;
+ c--) {
+ switch (state) {
+ case HOSTPORT:
+ if (*c == ':') {
+ *port_start = c + 1;
+ *port_len = hostport_end - *port_start;
+ host_end = c;
+ state = HOST;
+ break;
+ }
- if (default_port == NULL) {
- git_error_set(GIT_ERROR_NET, "unknown scheme for URL '%s'", given);
- goto done;
- }
+ /*
+ * if we've only seen digits then we don't know
+ * if we're parsing just a host or a host and port.
+ * if we see a non-digit, then we're in a host,
+ * otherwise, fall through to possibly match the
+ * "@" (user/host separator).
+ */
+
+ if (*c < '0' || *c > '9') {
+ host_end = hostport_end;
+ state = HOST;
+ }
- git_str_puts(&port, default_port);
- }
+ /* fall through */
- if (has_path) {
- const char *url_path = given + u.field_data[UF_PATH].off;
- size_t url_path_len = u.field_data[UF_PATH].len;
- git_str_put(&path, url_path, url_path_len);
- } else {
- git_str_puts(&path, "/");
+ case HOST:
+ if (*c == ']' && host_end == c + 1) {
+ host_end = c;
+ state = IPV6;
+ }
+
+ else if (*c == '@') {
+ *host_start = c + 1;
+ *host_len = host_end ? host_end - *host_start :
+ hostport_end - *host_start;
+ userpass_end = c;
+ state = USERPASS;
+ }
+
+ else if (*c == '[' || *c == ']' || *c == ':') {
+ return url_invalid("malformed hostname");
+ }
+
+ break;
+
+ case IPV6:
+ if (*c == '[') {
+ *host_start = c + 1;
+ *host_len = host_end - *host_start;
+ state = HOST_END;
+ }
+
+ else if ((*c < '0' || *c > '9') &&
+ (*c < 'a' || *c > 'f') &&
+ (*c < 'A' || *c > 'F') &&
+ (*c != ':')) {
+ return url_invalid("malformed hostname");
+ }
+
+ break;
+
+ case HOST_END:
+ if (*c == '@') {
+ userpass_end = c;
+ state = USERPASS;
+ break;
+ }
+
+ return url_invalid("malformed hostname");
+
+ case USERPASS:
+ if (*c == '@' &&
+ strncasecmp(scheme_start, "ssh", scheme_len))
+ return url_invalid("malformed hostname");
+
+ if (*c == ':') {
+ *password_start = c + 1;
+ *password_len = userpass_end - *password_start;
+ user_end = c;
+ state = USER;
+ break;
+ }
+
+ break;
+
+ default:
+ GIT_ASSERT(!"unhandled state");
+ }
}
- if (has_query) {
- const char *url_query = given + u.field_data[UF_QUERY].off;
- size_t url_query_len = u.field_data[UF_QUERY].len;
- git_str_decode_percent(&query, url_query, url_query_len);
+ switch (state) {
+ case HOSTPORT:
+ *host_start = authority_start;
+ *host_len = (hostport_end - *host_start);
+ break;
+ case HOST:
+ *host_start = authority_start;
+ *host_len = (host_end - *host_start);
+ break;
+ case IPV6:
+ return url_invalid("malformed hostname");
+ case HOST_END:
+ break;
+ case USERPASS:
+ *user_start = authority_start;
+ *user_len = (userpass_end - *user_start);
+ break;
+ case USER:
+ *user_start = authority_start;
+ *user_len = (user_end - *user_start);
+ break;
+ default:
+ GIT_ASSERT(!"unhandled state");
}
- if (has_userinfo) {
- const char *url_userinfo = given + u.field_data[UF_USERINFO].off;
- size_t url_userinfo_len = u.field_data[UF_USERINFO].len;
- const char *colon = memchr(url_userinfo, ':', url_userinfo_len);
+ return 0;
+}
+
+int git_net_url_parse(git_net_url *url, const char *given)
+{
+ const char *c, *scheme_start, *authority_start, *user_start,
+ *password_start, *host_start, *port_start, *path_start,
+ *query_start, *fragment_start, *default_port;
+ git_str scheme = GIT_STR_INIT, user = GIT_STR_INIT,
+ password = GIT_STR_INIT, host = GIT_STR_INIT,
+ port = GIT_STR_INIT, path = GIT_STR_INIT,
+ query = GIT_STR_INIT, fragment = GIT_STR_INIT;
+ size_t scheme_len = 0, user_len = 0, password_len = 0, host_len = 0,
+ port_len = 0, path_len = 0, query_len = 0, fragment_len = 0;
+ bool hierarchical = false;
+ int error = 0;
+
+ enum {
+ SCHEME,
+ AUTHORITY_START, AUTHORITY,
+ PATH_START, PATH,
+ QUERY,
+ FRAGMENT
+ } state = SCHEME;
+
+ memset(url, 0, sizeof(git_net_url));
+
+ for (c = scheme_start = given; *c; c++) {
+ switch (state) {
+ case SCHEME:
+ if (*c == ':') {
+ scheme_len = (c - scheme_start);
+
+ if (*(c+1) == '/' && *(c+2) == '/') {
+ c += 2;
+ hierarchical = true;
+ state = AUTHORITY_START;
+ } else {
+ state = PATH_START;
+ }
+ } else if ((*c < 'A' || *c > 'Z') &&
+ (*c < 'a' || *c > 'z') &&
+ (*c < '0' || *c > '9') &&
+ (*c != '+' && *c != '-' && *c != '.')) {
+ /*
+ * an illegal scheme character means that we
+ * were just given a relative path
+ */
+ path_start = given;
+ state = PATH;
+ break;
+ }
+ break;
+
+ case AUTHORITY_START:
+ authority_start = c;
+ state = AUTHORITY;
+
+ /* fall through */
+
+ case AUTHORITY:
+ if (*c != '/')
+ break;
+
+ /*
+ * authority is sufficiently complex that we parse
+ * it separately
+ */
+ if ((error = url_parse_authority(
+ &user_start, &user_len,
+ &password_start,&password_len,
+ &host_start, &host_len,
+ &port_start, &port_len,
+ authority_start, (c - authority_start),
+ scheme_start, scheme_len)) < 0)
+ goto done;
+
+ /* fall through */
+
+ case PATH_START:
+ path_start = c;
+ state = PATH;
+ /* fall through */
+
+ case PATH:
+ switch (*c) {
+ case '?':
+ path_len = (c - path_start);
+ query_start = c + 1;
+ state = QUERY;
+ break;
+ case '#':
+ path_len = (c - path_start);
+ fragment_start = c + 1;
+ state = FRAGMENT;
+ break;
+ }
+ break;
+
+ case QUERY:
+ if (*c == '#') {
+ query_len = (c - query_start);
+ fragment_start = c + 1;
+ state = FRAGMENT;
+ }
+ break;
- if (colon) {
- const char *url_username = url_userinfo;
- size_t url_username_len = colon - url_userinfo;
- const char *url_password = colon + 1;
- size_t url_password_len = url_userinfo_len - (url_username_len + 1);
+ case FRAGMENT:
+ break;
- git_str_decode_percent(&username, url_username, url_username_len);
- git_str_decode_percent(&password, url_password, url_password_len);
- } else {
- git_str_decode_percent(&username, url_userinfo, url_userinfo_len);
+ default:
+ GIT_ASSERT(!"unhandled state");
}
}
- if (git_str_oom(&scheme) ||
- git_str_oom(&host) ||
- git_str_oom(&port) ||
- git_str_oom(&path) ||
- git_str_oom(&query) ||
- git_str_oom(&username) ||
- git_str_oom(&password))
- return -1;
+ switch (state) {
+ case SCHEME:
+ /*
+ * if we never saw a ':' then we were given a relative
+ * path, not a bare scheme
+ */
+ path_start = given;
+ path_len = (c - scheme_start);
+ break;
+ case AUTHORITY_START:
+ break;
+ case AUTHORITY:
+ if ((error = url_parse_authority(
+ &user_start, &user_len,
+ &password_start,&password_len,
+ &host_start, &host_len,
+ &port_start, &port_len,
+ authority_start, (c - authority_start),
+ scheme_start, scheme_len)) < 0)
+ goto done;
+ break;
+ case PATH_START:
+ break;
+ case PATH:
+ path_len = (c - path_start);
+ break;
+ case QUERY:
+ query_len = (c - query_start);
+ break;
+ case FRAGMENT:
+ fragment_len = (c - fragment_start);
+ break;
+ default:
+ GIT_ASSERT(!"unhandled state");
+ }
+
+ if (scheme_len) {
+ if ((error = git_str_put(&scheme, scheme_start, scheme_len)) < 0)
+ goto done;
+
+ git__strntolower(scheme.ptr, scheme.size);
+ }
+
+ if (user_len &&
+ (error = git_str_decode_percent(&user, user_start, user_len)) < 0)
+ goto done;
+
+ if (password_len &&
+ (error = git_str_decode_percent(&password, password_start, password_len)) < 0)
+ goto done;
+
+ if (host_len &&
+ (error = git_str_decode_percent(&host, host_start, host_len)) < 0)
+ goto done;
+
+ if (port_len)
+ error = git_str_put(&port, port_start, port_len);
+ else if (scheme_len && (default_port = default_port_for_scheme(scheme.ptr)) != NULL)
+ error = git_str_puts(&port, default_port);
+
+ if (error < 0)
+ goto done;
+
+ if (path_len)
+ error = git_str_put(&path, path_start, path_len);
+ else if (hierarchical)
+ error = git_str_puts(&path, "/");
+
+ if (error < 0)
+ goto done;
+
+ if (query_len &&
+ (error = git_str_decode_percent(&query, query_start, query_len)) < 0)
+ goto done;
+
+ if (fragment_len &&
+ (error = git_str_decode_percent(&fragment, fragment_start, fragment_len)) < 0)
+ goto done;
url->scheme = git_str_detach(&scheme);
url->host = git_str_detach(&host);
url->port = git_str_detach(&port);
url->path = git_str_detach(&path);
url->query = git_str_detach(&query);
- url->username = git_str_detach(&username);
+ url->fragment = git_str_detach(&fragment);
+ url->username = git_str_detach(&user);
url->password = git_str_detach(&password);
error = 0;
done:
git_str_dispose(&scheme);
+ git_str_dispose(&user);
+ git_str_dispose(&password);
git_str_dispose(&host);
git_str_dispose(&port);
git_str_dispose(&path);
git_str_dispose(&query);
- git_str_dispose(&username);
- git_str_dispose(&password);
+ git_str_dispose(&fragment);
+
return error;
}
@@ -374,7 +620,7 @@ int git_net_url_parse_scp(git_net_url *url, const char *given)
break;
default:
- GIT_ASSERT("unhandled state");
+ GIT_ASSERT(!"unhandled state");
}
}
@@ -400,6 +646,13 @@ int git_net_url_parse_scp(git_net_url *url, const char *given)
return 0;
}
+int git_net_url_parse_standard_or_scp(git_net_url *url, const char *given)
+{
+ return git_net_str_is_url(given) ?
+ git_net_url_parse(url, given) :
+ git_net_url_parse_scp(url, given);
+}
+
int git_net_url_joinpath(
git_net_url *out,
git_net_url *one,
@@ -588,7 +841,7 @@ bool git_net_url_is_default_port(git_net_url *url)
{
const char *default_port;
- if ((default_port = default_port_for_scheme(url->scheme)) != NULL)
+ if (url->scheme && (default_port = default_port_for_scheme(url->scheme)) != NULL)
return (strcmp(url->port, default_port) == 0);
else
return false;
@@ -744,6 +997,7 @@ void git_net_url_dispose(git_net_url *url)
git__free(url->port); url->port = NULL;
git__free(url->path); url->path = NULL;
git__free(url->query); url->query = NULL;
+ git__free(url->fragment); url->fragment = NULL;
git__free(url->username); url->username = NULL;
git__free(url->password); url->password = NULL;
}
diff --git a/src/util/net.h b/src/util/net.h
index 88030a952..17f0bc4f0 100644
--- a/src/util/net.h
+++ b/src/util/net.h
@@ -15,6 +15,7 @@ typedef struct git_net_url {
char *port;
char *path;
char *query;
+ char *fragment;
char *username;
char *password;
} git_net_url;
@@ -33,6 +34,12 @@ extern int git_net_url_parse(git_net_url *url, const char *str);
/** Parses a string containing an SCP style path into a URL structure. */
extern int git_net_url_parse_scp(git_net_url *url, const char *str);
+/**
+ * Parses a string containing a standard URL or an SCP style path into
+ * a URL structure.
+ */
+extern int git_net_url_parse_standard_or_scp(git_net_url *url, const char *str);
+
/** Appends a path and/or query string to the given URL */
extern int git_net_url_joinpath(
git_net_url *out,
diff --git a/src/util/posix.h b/src/util/posix.h
index c8f8cd9d2..607aa9dce 100644
--- a/src/util/posix.h
+++ b/src/util/posix.h
@@ -104,6 +104,8 @@ typedef __int64 off64_t;
typedef __haiku_std_int64 off64_t;
#elif defined(__APPLE__)
typedef __int64_t off64_t;
+#elif defined(_AIX)
+typedef long long off64_t;
#else
typedef int64_t off64_t;
#endif
diff --git a/src/util/rand.c b/src/util/rand.c
index d28e4aa97..940faf947 100644
--- a/src/util/rand.c
+++ b/src/util/rand.c
@@ -14,6 +14,10 @@ See <http://creativecommons.org/publicdomain/zero/1.0/>. */
# include <sys/random.h>
#endif
+#if defined(GIT_WIN32)
+# include <wincrypt.h>
+#endif
+
static uint64_t state[4];
static git_mutex state_lock;
diff --git a/src/util/regexp.c b/src/util/regexp.c
index 2569dea0a..08700882b 100644
--- a/src/util/regexp.c
+++ b/src/util/regexp.c
@@ -108,11 +108,11 @@ int git_regexp_match(const git_regexp *r, const char *string)
data = pcre2_match_data_create(1, NULL);
GIT_ERROR_CHECK_ALLOC(data);
- if ((error = pcre2_match(*r, (const unsigned char *) string, strlen(string),
- 0, 0, data, NULL)) < 0)
+ error = pcre2_match(*r, (const unsigned char *) string, strlen(string), 0, 0, data, NULL);
+ pcre2_match_data_free(data);
+ if (error < 0)
return (error == PCRE2_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC;
- pcre2_match_data_free(data);
return 0;
}
diff --git a/src/util/thread.h b/src/util/thread.h
index 4bbac9fd8..c32554bfd 100644
--- a/src/util/thread.h
+++ b/src/util/thread.h
@@ -260,36 +260,37 @@ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
#else
-#define git_threads_global_init git__noop
+#define git_threads_global_init git__noop
#define git_thread unsigned int
-#define git_thread_create(thread, start_routine, arg) git__noop()
-#define git_thread_join(id, status) git__noop()
+#define git_thread_create(t, s, a) git__noop(t, s, a)
+#define git_thread_join(i, s) git__noop_args(i, s)
/* Pthreads Mutex */
#define git_mutex unsigned int
-#define git_mutex_init(a) git__noop()
-#define git_mutex_init(a) git__noop()
-#define git_mutex_lock(a) git__noop()
-#define git_mutex_unlock(a) git__noop()
-#define git_mutex_free(a) git__noop()
+#define git_mutex_init(a) git__noop_args(a)
+#define git_mutex_init(a) git__noop_args(a)
+#define git_mutex_lock(a) git__noop_args(a)
+#define git_mutex_unlock(a) git__noop_args(a)
+#define git_mutex_free(a) git__noop_args(a)
/* Pthreads condition vars */
#define git_cond unsigned int
-#define git_cond_init(c) git__noop()
-#define git_cond_free(c) git__noop()
-#define git_cond_wait(c, l) git__noop()
-#define git_cond_signal(c) git__noop()
-#define git_cond_broadcast(c) git__noop()
+#define git_cond_init(c) git__noop_args(c)
+#define git_cond_free(c) git__noop_args(c)
+#define git_cond_wait(c, l) git__noop_args(c, l)
+#define git_cond_signal(c) git__noop_args(c)
+#define git_cond_broadcast(c) git__noop_args(c)
/* Pthreads rwlock */
#define git_rwlock unsigned int
-#define git_rwlock_init(a) git__noop()
-#define git_rwlock_rdlock(a) git__noop()
-#define git_rwlock_rdunlock(a) git__noop()
-#define git_rwlock_wrlock(a) git__noop()
-#define git_rwlock_wrunlock(a) git__noop()
-#define git_rwlock_free(a) git__noop()
+#define git_rwlock_init(a) git__noop_args(a)
+#define git_rwlock_rdlock(a) git__noop_args(a)
+#define git_rwlock_rdunlock(a) git__noop_args(a)
+#define git_rwlock_wrlock(a) git__noop_args(a)
+#define git_rwlock_wrunlock(a) git__noop_args(a)
+#define git_rwlock_free(a) git__noop_args(a)
+
#define GIT_RWLOCK_STATIC_INIT 0
diff --git a/src/util/util.h b/src/util/util.h
index 8d6d1d6b6..63d6080f7 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -381,6 +381,7 @@ extern int git__getenv(git_str *out, const char *name);
extern int git__online_cpus(void);
GIT_INLINE(int) git__noop(void) { return 0; }
+GIT_INLINE(int) git__noop_args(void *a, ...) { GIT_UNUSED(a); return 0; }
#include "alloc.h"
diff --git a/src/util/win32/findfile.c b/src/util/win32/findfile.c
deleted file mode 100644
index 725a90167..000000000
--- a/src/util/win32/findfile.c
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * 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.
- */
-
-#include "findfile.h"
-
-#include "path_w32.h"
-#include "utf-conv.h"
-#include "fs_path.h"
-
-#define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
-#define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
-
-static int git_win32__expand_path(git_win32_path dest, const wchar_t *src)
-{
- DWORD len = ExpandEnvironmentStringsW(src, dest, GIT_WIN_PATH_UTF16);
-
- if (!len || len > GIT_WIN_PATH_UTF16)
- return -1;
-
- return 0;
-}
-
-static int win32_path_to_8(git_str *dest, const wchar_t *src)
-{
- git_win32_utf8_path utf8_path;
-
- if (git_win32_path_to_utf8(utf8_path, src) < 0) {
- git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8");
- return -1;
- }
-
- /* Convert backslashes to forward slashes */
- git_fs_path_mkposix(utf8_path);
-
- return git_str_sets(dest, utf8_path);
-}
-
-static git_win32_path mock_registry;
-static bool mock_registry_set;
-
-extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir)
-{
- if (!mock_sysdir) {
- mock_registry[0] = L'\0';
- mock_registry_set = false;
- } else {
- size_t len = wcslen(mock_sysdir);
-
- if (len > GIT_WIN_PATH_MAX) {
- git_error_set(GIT_ERROR_INVALID, "mock path too long");
- return -1;
- }
-
- wcscpy(mock_registry, mock_sysdir);
- mock_registry_set = true;
- }
-
- return 0;
-}
-
-static int lookup_registry_key(
- git_win32_path out,
- const HKEY hive,
- const wchar_t* key,
- const wchar_t *value)
-{
- HKEY hkey;
- DWORD type, size;
- int error = GIT_ENOTFOUND;
-
- /*
- * Registry data may not be NUL terminated, provide room to do
- * it ourselves.
- */
- size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t));
-
- if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0)
- return GIT_ENOTFOUND;
-
- if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 &&
- type == REG_SZ &&
- size > 0 &&
- size < sizeof(git_win32_path)) {
- size_t wsize = size / sizeof(wchar_t);
- size_t len = wsize - 1;
-
- if (out[wsize - 1] != L'\0') {
- len = wsize;
- out[wsize] = L'\0';
- }
-
- if (out[len - 1] == L'\\')
- out[len - 1] = L'\0';
-
- if (_waccess(out, F_OK) == 0)
- error = 0;
- }
-
- RegCloseKey(hkey);
- return error;
-}
-
-static int find_sysdir_in_registry(git_win32_path out)
-{
- if (mock_registry_set) {
- if (mock_registry[0] == L'\0')
- return GIT_ENOTFOUND;
-
- wcscpy(out, mock_registry);
- return 0;
- }
-
- if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
- lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 ||
- lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
- lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0)
- return 0;
-
- return GIT_ENOTFOUND;
-}
-
-static int find_sysdir_in_path(git_win32_path out)
-{
- size_t out_len;
-
- if (git_win32_path_find_executable(out, L"git.exe") < 0 &&
- git_win32_path_find_executable(out, L"git.cmd") < 0)
- return GIT_ENOTFOUND;
-
- out_len = wcslen(out);
-
- /* Trim the file name */
- if (out_len <= CONST_STRLEN(L"git.exe"))
- return GIT_ENOTFOUND;
-
- out_len -= CONST_STRLEN(L"git.exe");
-
- if (out_len && out[out_len - 1] == L'\\')
- out_len--;
-
- /*
- * Git for Windows usually places the command in a 'bin' or
- * 'cmd' directory, trim that.
- */
- if (out_len >= CONST_STRLEN(L"\\bin") &&
- wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0)
- out_len -= CONST_STRLEN(L"\\bin");
- else if (out_len >= CONST_STRLEN(L"\\cmd") &&
- wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0)
- out_len -= CONST_STRLEN(L"\\cmd");
-
- if (!out_len)
- return GIT_ENOTFOUND;
-
- out[out_len] = L'\0';
- return 0;
-}
-
-static int win32_find_existing_dirs(
- git_str* out,
- const wchar_t* tmpl[])
-{
- git_win32_path path16;
- git_str buf = GIT_STR_INIT;
-
- git_str_clear(out);
-
- for (; *tmpl != NULL; tmpl++) {
- if (!git_win32__expand_path(path16, *tmpl) &&
- path16[0] != L'%' &&
- !_waccess(path16, F_OK)) {
- win32_path_to_8(&buf, path16);
-
- if (buf.size)
- git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
- }
- }
-
- git_str_dispose(&buf);
-
- return (git_str_oom(out) ? -1 : 0);
-}
-
-static int append_subdir(git_str *out, git_str *path, const char *subdir)
-{
- static const char* architecture_roots[] = {
- "",
- "mingw64",
- "mingw32",
- NULL
- };
- const char **root;
- size_t orig_path_len = path->size;
-
- for (root = architecture_roots; *root; root++) {
- if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) ||
- git_str_joinpath(path, path->ptr, subdir) < 0)
- return -1;
-
- if (git_fs_path_exists(path->ptr) &&
- git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0)
- return -1;
-
- git_str_truncate(path, orig_path_len);
- }
-
- return 0;
-}
-
-int git_win32__find_system_dirs(git_str *out, const char *subdir)
-{
- git_win32_path pathdir, regdir;
- git_str path8 = GIT_STR_INIT;
- bool has_pathdir, has_regdir;
- int error;
-
- has_pathdir = (find_sysdir_in_path(pathdir) == 0);
- has_regdir = (find_sysdir_in_registry(regdir) == 0);
-
- if (!has_pathdir && !has_regdir)
- return 0;
-
- /*
- * Usually the git in the path is the same git in the registry,
- * in this case there's no need to duplicate the paths.
- */
- if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0)
- has_regdir = false;
-
- if (has_pathdir) {
- if ((error = win32_path_to_8(&path8, pathdir)) < 0 ||
- (error = append_subdir(out, &path8, subdir)) < 0)
- goto done;
- }
-
- if (has_regdir) {
- if ((error = win32_path_to_8(&path8, regdir)) < 0 ||
- (error = append_subdir(out, &path8, subdir)) < 0)
- goto done;
- }
-
-done:
- git_str_dispose(&path8);
- return error;
-}
-
-int git_win32__find_global_dirs(git_str *out)
-{
- static const wchar_t *global_tmpls[4] = {
- L"%HOME%\\",
- L"%HOMEDRIVE%%HOMEPATH%\\",
- L"%USERPROFILE%\\",
- NULL,
- };
-
- return win32_find_existing_dirs(out, global_tmpls);
-}
-
-int git_win32__find_xdg_dirs(git_str *out)
-{
- static const wchar_t *global_tmpls[7] = {
- L"%XDG_CONFIG_HOME%\\git",
- L"%APPDATA%\\git",
- L"%LOCALAPPDATA%\\git",
- L"%HOME%\\.config\\git",
- L"%HOMEDRIVE%%HOMEPATH%\\.config\\git",
- L"%USERPROFILE%\\.config\\git",
- NULL,
- };
-
- return win32_find_existing_dirs(out, global_tmpls);
-}
-
-int git_win32__find_programdata_dirs(git_str *out)
-{
- static const wchar_t *programdata_tmpls[2] = {
- L"%PROGRAMDATA%\\Git",
- NULL,
- };
-
- return win32_find_existing_dirs(out, programdata_tmpls);
-}
diff --git a/src/util/win32/findfile.h b/src/util/win32/findfile.h
deleted file mode 100644
index 7b191d1fe..000000000
--- a/src/util/win32/findfile.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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_win32_findfile_h__
-#define INCLUDE_win32_findfile_h__
-
-#include "git2_util.h"
-
-/** Sets the mock registry root for Git for Windows for testing. */
-extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir);
-
-extern int git_win32__find_system_dirs(git_str *out, const char *subpath);
-extern int git_win32__find_global_dirs(git_str *out);
-extern int git_win32__find_xdg_dirs(git_str *out);
-extern int git_win32__find_programdata_dirs(git_str *out);
-
-#endif
-