summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--appveyor.yml7
-rw-r--r--include/git2/common.h9
-rw-r--r--include/git2/odb.h48
-rwxr-xr-xscript/appveyor-mingw.sh4
-rwxr-xr-xscript/coverity.sh18
-rw-r--r--script/toolchain-mingw32.cmake13
-rw-r--r--src/blame.c11
-rw-r--r--src/blame_git.c3
-rw-r--r--src/commit.c2
-rw-r--r--src/config_cache.c3
-rw-r--r--src/config_file.c33
-rw-r--r--src/describe.c3
-rw-r--r--src/diff_tform.c8
-rw-r--r--src/filebuf.c7
-rw-r--r--src/global.c2
-rw-r--r--src/global.h1
-rw-r--r--src/index.c12
-rw-r--r--src/indexer.c8
-rw-r--r--src/mwindow.c14
-rw-r--r--src/object.c9
-rw-r--r--src/odb.c258
-rw-r--r--src/odb.h3
-rw-r--r--src/odb_loose.c20
-rw-r--r--src/odb_pack.c8
-rw-r--r--src/oid.h9
-rw-r--r--src/openssl_stream.c13
-rw-r--r--src/pack-objects.c4
-rw-r--r--src/pack.c10
-rw-r--r--src/posix.c7
-rw-r--r--src/posix.h1
-rw-r--r--src/refdb_fs.c1
-rw-r--r--src/settings.c24
-rw-r--r--src/submodule.c3
-rw-r--r--src/unix/map.c5
-rw-r--r--src/win32/map.c29
-rw-r--r--tests/commit/parse.c24
-rw-r--r--tests/odb/mixed.c155
-rw-r--r--tests/online/badssl.c8
-rw-r--r--tests/rebase/abort.c31
-rw-r--r--tests/rebase/merge.c57
-rw-r--r--tests/rebase/setup.c205
42 files changed, 939 insertions, 154 deletions
diff --git a/.travis.yml b/.travis.yml
index 2f3ffe355..bfc0fac48 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -37,9 +37,6 @@ matrix:
- os: osx
compiler: gcc
include:
- - compiler: i586-mingw32msvc-gcc
- env: OPTIONS="-DCMAKE_TOOLCHAIN_FILE=../script/toolchain-mingw32.cmake" SKIP_TESTS=1
- os: linux
- compiler: gcc
env: COVERITY=1
os: linux
diff --git a/appveyor.yml b/appveyor.yml
index 3ed3c49a1..5ba8aaabd 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -13,15 +13,9 @@ environment:
- GENERATOR: "Visual Studio 11 Win64"
ARCH: 64
- GENERATOR: "MSYS Makefiles"
- ARCH: 32
- - GENERATOR: "MSYS Makefiles"
ARCH: i686 # this is for 32-bit MinGW-w64
- GENERATOR: "MSYS Makefiles"
ARCH: 64
-matrix:
- allow_failures:
- - GENERATOR: "MSYS Makefiles"
- ARCH: 32
cache:
- i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z
- x86_64-4.9.2-release-win32-seh-rt_v3-rev1.7z
@@ -37,6 +31,7 @@ build_script:
if "%GENERATOR%"=="MSYS Makefiles" (C:\MinGW\msys\1.0\bin\sh --login /c/projects/libgit2/script/appveyor-mingw.sh)
test_script:
- ps: |
+ $ErrorActionPreference="Stop"
ctest -V -R libgit2_clar
$env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
$env:GITTEST_REMOTE_USER="libgit2test"
diff --git a/include/git2/common.h b/include/git2/common.h
index 4f43185f8..0629abb7f 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -24,7 +24,8 @@
GIT_BEGIN_DECL
# include "inttypes.h"
GIT_END_DECL
-#else
+/** This check is needed for importing this file in an iOS/OS X framework throws an error in Xcode otherwise.*/
+#elif !defined(__CLANG_INTTYPES_H)
# include <inttypes.h>
#endif
@@ -148,6 +149,7 @@ typedef enum {
GIT_OPT_SET_SSL_CERT_LOCATIONS,
GIT_OPT_SET_USER_AGENT,
GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
+ GIT_OPT_SET_SSL_CIPHERS,
} git_libgit2_opt_t;
/**
@@ -259,6 +261,11 @@ typedef enum {
* > example, when this is enabled, the parent(s) and tree inputs
* > will be validated when creating a new commit. This defaults
* > to disabled.
+ * * opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers)
+ *
+ * > Set the SSL ciphers use for HTTPS connections.
+ * >
+ * > - `ciphers` is the list of ciphers that are eanbled.
*
* @param option Option key
* @param ... value to set the option
diff --git a/include/git2/odb.h b/include/git2/odb.h
index 4f1e18bc1..b3ed2706c 100644
--- a/include/git2/odb.h
+++ b/include/git2/odb.h
@@ -10,6 +10,7 @@
#include "common.h"
#include "types.h"
#include "oid.h"
+#include "oidarray.h"
/**
* @file git2/odb.h
@@ -159,7 +160,8 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_od
GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
/**
- * Determine if objects can be found in the object database from a short OID.
+ * Determine if an object can be found in the object database by an
+ * abbreviated object ID.
*
* @param out The full OID of the found object if just one is found.
* @param db The database to be searched for the given object.
@@ -172,6 +174,50 @@ GIT_EXTERN(int) git_odb_exists_prefix(
git_oid *out, git_odb *db, const git_oid *short_id, size_t len);
/**
+ * The information about object IDs to query in `git_odb_expand_ids`,
+ * which will be populated upon return.
+ */
+typedef struct git_odb_expand_id {
+ /** The object ID to expand */
+ git_oid id;
+
+ /**
+ * The length of the object ID (in nibbles, or packets of 4 bits; the
+ * number of hex characters)
+ * */
+ unsigned short length;
+
+ /**
+ * The (optional) type of the object to search for; leave as `0` or set
+ * to `GIT_OBJ_ANY` to query for any object matching the ID.
+ */
+ git_otype type;
+} git_odb_expand_id;
+
+/**
+ * Determine if one or more objects can be found in the object database
+ * by their abbreviated object ID and type. The given array will be
+ * updated in place: for each abbreviated ID that is unique in the
+ * database, and of the given type (if specified), the full object ID,
+ * object ID length (`GIT_OID_HEXSZ`) and type will be written back to
+ * the array. For IDs that are not found (or are ambiguous), the
+ * array entry will be zeroed.
+ *
+ * Note that since this function operates on multiple objects, the
+ * underlying database will not be asked to be reloaded if an object is
+ * not found (which is unlike other object database operations.)
+ *
+ * @param db The database to be searched for the given objects.
+ * @param ids An array of short object IDs to search for
+ * @param count The length of the `ids` array
+ * @return 0 on success or an error code on failure
+ */
+GIT_EXTERN(int) git_odb_expand_ids(
+ git_odb *db,
+ git_odb_expand_id *ids,
+ size_t count);
+
+/**
* Refresh the object database to load newly added files.
*
* If the object databases have changed on disk while the library
diff --git a/script/appveyor-mingw.sh b/script/appveyor-mingw.sh
index 48e0bad0a..198801875 100755
--- a/script/appveyor-mingw.sh
+++ b/script/appveyor-mingw.sh
@@ -1,9 +1,7 @@
#!/bin/sh
set -e
cd `dirname "$0"`/..
-if [ "$ARCH" = "32" ]; then
- echo 'C:\MinGW\ /MinGW' > /etc/fstab
-elif [ "$ARCH" = "i686" ]; then
+if [ "$ARCH" = "i686" ]; then
f=i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z
if ! [ -e $f ]; then
curl -LsSO http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-win32/sjlj/$f
diff --git a/script/coverity.sh b/script/coverity.sh
index 8c826892f..7fe9eb4c7 100755
--- a/script/coverity.sh
+++ b/script/coverity.sh
@@ -49,10 +49,24 @@ COVERITY_UNSUPPORTED=1 \
# Upload results
tar czf libgit2.tgz cov-int
SHA=$(git rev-parse --short HEAD)
-curl \
+
+HTML="$(curl \
+ --silent \
+ --write-out "\n%{http_code}" \
--form token="$COVERITY_TOKEN" \
--form email=bs@github.com \
--form file=@libgit2.tgz \
--form version="$SHA" \
--form description="Travis build" \
- https://scan.coverity.com/builds?project=libgit2
+ https://scan.coverity.com/builds?project=libgit2)"
+# Body is everything up to the last line
+BODY="$(echo "$HTML" | head -n-1)"
+# Status code is the last line
+STATUS_CODE="$(echo "$HTML" | tail -n1)"
+
+echo "${BODY}"
+
+if [ "${STATUS_CODE}" != "201" ]; then
+ echo "Received error code ${STATUS_CODE} from Coverity"
+ exit 1
+fi
diff --git a/script/toolchain-mingw32.cmake b/script/toolchain-mingw32.cmake
deleted file mode 100644
index 2536b01c3..000000000
--- a/script/toolchain-mingw32.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-# CMake toolchain file for Win32 cross-compile
-SET(CMAKE_SYSTEM_NAME Windows)
-
-SET(CMAKE_C_COMPILER i586-mingw32msvc-gcc)
-SET(CMAKE_RC_COMPILER i586-mingw32msvc-windres)
-
-SET(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc)
-
-SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-
-SET(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_FIND_ROOT_PATH}/lib/pkgconfig)
diff --git a/src/blame.c b/src/blame.c
index 2daf91591..2c8584ba5 100644
--- a/src/blame.c
+++ b/src/blame.c
@@ -178,7 +178,7 @@ const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno)
return NULL;
}
-static void normalize_options(
+static int normalize_options(
git_blame_options *out,
const git_blame_options *in,
git_repository *repo)
@@ -190,7 +190,9 @@ static void normalize_options(
/* No newest_commit => HEAD */
if (git_oid_iszero(&out->newest_commit)) {
- git_reference_name_to_id(&out->newest_commit, repo, "HEAD");
+ if (git_reference_name_to_id(&out->newest_commit, repo, "HEAD") < 0) {
+ return -1;
+ }
}
/* min_line 0 really means 1 */
@@ -204,6 +206,8 @@ static void normalize_options(
out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES)
out->flags |= GIT_BLAME_TRACK_COPIES_SAME_FILE;
+
+ return 0;
}
static git_blame_hunk *split_hunk_in_vector(
@@ -362,7 +366,8 @@ int git_blame_file(
git_blame *blame = NULL;
assert(out && repo && path);
- normalize_options(&normOptions, options, repo);
+ if ((error = normalize_options(&normOptions, options, repo)) < 0)
+ goto on_error;
blame = git_blame__alloc(repo, normOptions, path);
GITERR_CHECK_ALLOC(blame);
diff --git a/src/blame_git.c b/src/blame_git.c
index b8b568285..700207edb 100644
--- a/src/blame_git.c
+++ b/src/blame_git.c
@@ -525,7 +525,8 @@ static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
if (sg_origin[i])
continue;
- git_commit_parent(&p, origin->commit, i);
+ if ((error = git_commit_parent(&p, origin->commit, i)) < 0)
+ goto finish;
porigin = find_origin(blame, p, origin);
if (!porigin)
diff --git a/src/commit.c b/src/commit.c
index 9d675ac97..905407aac 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -726,7 +726,7 @@ int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_r
buf = git_odb_object_data(obj);
- while ((h = strchr(buf, '\n')) && h[1] != '\0' && h[1] != '\n') {
+ while ((h = strchr(buf, '\n')) && h[1] != '\0') {
h++;
if (git__prefixcmp(buf, field)) {
if (git_buf_put(signed_data, buf, h - buf) < 0)
diff --git a/src/config_cache.c b/src/config_cache.c
index c859ec148..dbea871b9 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -86,7 +86,8 @@ int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar)
struct map_data *data = &_cvar_maps[(int)cvar];
git_config_entry *entry;
- git_config__lookup_entry(&entry, config, data->cvar_name, false);
+ if ((error = git_config__lookup_entry(&entry, config, data->cvar_name, false)) < 0)
+ return error;
if (!entry)
*out = data->default_value;
diff --git a/src/config_file.c b/src/config_file.c
index 5f5e309e0..584b9fa82 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -232,7 +232,10 @@ static refcounted_strmap *refcounted_strmap_take(diskfile_header *h)
{
refcounted_strmap *map;
- git_mutex_lock(&h->values_mutex);
+ if (git_mutex_lock(&h->values_mutex) < 0) {
+ giterr_set(GITERR_OS, "Failed to lock config backend");
+ return NULL;
+ }
map = h->values;
git_atomic_inc(&map->refcount);
@@ -318,7 +321,10 @@ static int config__refresh(git_config_backend *cfg)
if ((error = config_read(values->values, b, reader, b->level, 0)) < 0)
goto out;
- git_mutex_lock(&b->header.values_mutex);
+ if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
+ giterr_set(GITERR_OS, "Failed to lock config backend");
+ goto out;
+ }
tmp = b->header.values;
b->header.values = values;
@@ -460,7 +466,8 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
if ((rval = git_config__normalize_name(name, &key)) < 0)
return rval;
- map = refcounted_strmap_take(&b->header);
+ if ((map = refcounted_strmap_take(&b->header)) == NULL)
+ return -1;
values = map->values;
/*
@@ -527,7 +534,8 @@ static int config_get(git_config_backend *cfg, const char *key, git_config_entry
if (!h->parent.readonly && ((error = config_refresh(cfg)) < 0))
return error;
- map = refcounted_strmap_take(h);
+ if ((map = refcounted_strmap_take(h)) == NULL)
+ return -1;
values = map->values;
pos = git_strmap_lookup_index(values, key);
@@ -565,7 +573,8 @@ static int config_set_multivar(
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
- map = refcounted_strmap_take(&b->header);
+ if ((map = refcounted_strmap_take(&b->header)) == NULL)
+ return -1;
values = b->header.values->values;
pos = git_strmap_lookup_index(values, key);
@@ -610,7 +619,8 @@ static int config_delete(git_config_backend *cfg, const char *name)
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
- map = refcounted_strmap_take(&b->header);
+ if ((map = refcounted_strmap_take(&b->header)) == NULL)
+ return -1;
values = b->header.values->values;
pos = git_strmap_lookup_index(values, key);
@@ -649,7 +659,8 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
- map = refcounted_strmap_take(&b->header);
+ if ((map = refcounted_strmap_take(&b->header)) == NULL)
+ return -1;
values = b->header.values->values;
pos = git_strmap_lookup_index(values, key);
@@ -832,7 +843,8 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
/* We're just copying data, don't care about the level */
GIT_UNUSED(level);
- src_map = refcounted_strmap_take(src_header);
+ if ((src_map = refcounted_strmap_take(src_header)) == NULL)
+ return -1;
b->header.values = src_map;
return 0;
@@ -1032,6 +1044,11 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
*/
first_quote = strchr(line, '"');
+ if (first_quote == NULL) {
+ set_parse_error(reader, 0, "Missing quotation marks in section header");
+ return -1;
+ }
+
last_quote = strrchr(line, '"');
quoted_len = last_quote - first_quote;
diff --git a/src/describe.c b/src/describe.c
index 48f04e858..13ddad5be 100644
--- a/src/describe.c
+++ b/src/describe.c
@@ -582,7 +582,8 @@ static int describe(
best = (struct possible_tag *)git_vector_get(&all_matches, 0);
if (gave_up_on) {
- git_pqueue_insert(&list, gave_up_on);
+ if ((error = git_pqueue_insert(&list, gave_up_on)) < 0)
+ goto cleanup;
seen_commits--;
}
if ((error = finish_depth_computation(
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 8577f06b8..6a6a62811 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -261,7 +261,7 @@ static int normalize_find_opts(
if (!given ||
(given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG)
{
- if (diff->repo) {
+ if (cfg) {
char *rule =
git_config__get_string_force(cfg, "diff.renames", "true");
int boolval;
@@ -318,8 +318,10 @@ static int normalize_find_opts(
#undef USE_DEFAULT
if (!opts->rename_limit) {
- opts->rename_limit = git_config__get_int_force(
- cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT);
+ if (cfg) {
+ opts->rename_limit = git_config__get_int_force(
+ cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT);
+ }
if (opts->rename_limit <= 0)
opts->rename_limit = DEFAULT_RENAME_LIMIT;
diff --git a/src/filebuf.c b/src/filebuf.c
index 17efe872e..101d5082a 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -70,6 +70,7 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode)
git_file source;
char buffer[FILEIO_BUFSIZE];
ssize_t read_bytes;
+ int error;
source = p_open(file->path_original, O_RDONLY);
if (source < 0) {
@@ -80,7 +81,8 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode)
}
while ((read_bytes = p_read(source, buffer, sizeof(buffer))) > 0) {
- p_write(file->fd, buffer, read_bytes);
+ if ((error = p_write(file->fd, buffer, read_bytes)) < 0)
+ break;
if (file->compute_digest)
git_hash_update(&file->digest, buffer, read_bytes);
}
@@ -90,6 +92,9 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode)
if (read_bytes < 0) {
giterr_set(GITERR_OS, "Failed to read file '%s'", file->path_original);
return -1;
+ } else if (error < 0) {
+ giterr_set(GITERR_OS, "Failed to write file '%s'", file->path_lock);
+ return -1;
}
}
diff --git a/src/global.c b/src/global.c
index 0bfde1e04..c725b5184 100644
--- a/src/global.c
+++ b/src/global.c
@@ -27,6 +27,7 @@ static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
static git_atomic git__n_shutdown_callbacks;
static git_atomic git__n_inits;
char *git__user_agent;
+char *git__ssl_ciphers;
void git__on_shutdown(git_global_shutdown_fn callback)
{
@@ -83,6 +84,7 @@ static void shutdown_common(void)
}
git__free(git__user_agent);
+ git__free(git__ssl_ciphers);
#if defined(GIT_MSVC_CRTDBG)
git_win32__crtdbg_stacktrace_cleanup();
diff --git a/src/global.h b/src/global.h
index 9fdcee573..219951525 100644
--- a/src/global.h
+++ b/src/global.h
@@ -36,5 +36,6 @@ extern void git__on_shutdown(git_global_shutdown_fn callback);
extern void git__free_tls_data(void);
extern const char *git_libgit2__user_agent(void);
+extern const char *git_libgit2__ssl_ciphers(void);
#endif
diff --git a/src/index.c b/src/index.c
index b97f8091d..62aacf959 100644
--- a/src/index.c
+++ b/src/index.c
@@ -963,14 +963,20 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
*reuc_out = reuc = reuc_entry_alloc(path);
GITERR_CHECK_ALLOC(reuc);
- if ((reuc->mode[0] = ancestor_mode) > 0)
+ if ((reuc->mode[0] = ancestor_mode) > 0) {
+ assert(ancestor_oid);
git_oid_cpy(&reuc->oid[0], ancestor_oid);
+ }
- if ((reuc->mode[1] = our_mode) > 0)
+ if ((reuc->mode[1] = our_mode) > 0) {
+ assert(our_oid);
git_oid_cpy(&reuc->oid[1], our_oid);
+ }
- if ((reuc->mode[2] = their_mode) > 0)
+ if ((reuc->mode[2] = their_mode) > 0) {
+ assert(their_oid);
git_oid_cpy(&reuc->oid[2], their_oid);
+ }
return 0;
}
diff --git a/src/indexer.c b/src/indexer.c
index 9aa092556..1ffbc2790 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -449,7 +449,7 @@ static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t size)
{
git_file fd = idx->pack->mwf.fd;
- size_t page_size;
+ size_t mmap_alignment;
size_t page_offset;
git_off_t page_start;
unsigned char *map_data;
@@ -458,11 +458,11 @@ static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t
assert(data && size);
- if ((error = git__page_size(&page_size)) < 0)
+ if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
return error;
- /* the offset needs to be at the beginning of the a page boundary */
- page_offset = offset % page_size;
+ /* the offset needs to be at the mmap boundary for the platform */
+ page_offset = offset % mmap_alignment;
page_start = offset - page_offset;
if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0)
diff --git a/src/mwindow.c b/src/mwindow.c
index 55c8d894b..d3e9be78b 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -296,8 +296,18 @@ static git_mwindow *new_window(
*/
if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
- git__free(w);
- return NULL;
+ /*
+ * The first error might be down to memory fragmentation even if
+ * we're below our soft limits, so free up what we can and try again.
+ */
+
+ while (git_mwindow_close_lru(mwf) == 0)
+ /* nop */;
+
+ if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
+ git__free(w);
+ return NULL;
+ }
}
ctl->mmap_calls++;
diff --git a/src/object.c b/src/object.c
index ebf77fb47..1d45f9f1b 100644
--- a/src/object.c
+++ b/src/object.c
@@ -12,6 +12,7 @@
#include "commit.h"
#include "tree.h"
#include "blob.h"
+#include "oid.h"
#include "tag.h"
bool git_object__strict_input_validation = true;
@@ -166,13 +167,9 @@ int git_object_lookup_prefix(
error = git_odb_read(&odb_obj, odb, id);
}
} else {
- git_oid short_oid;
+ git_oid short_oid = {{ 0 }};
- /* We copy the first len*4 bits from id and fill the remaining with 0s */
- memcpy(short_oid.id, id->id, (len + 1) / 2);
- if (len % 2)
- short_oid.id[len / 2] &= 0xF0;
- memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2);
+ git_oid__cpy_prefix(&short_oid, id, len);
/* If len < GIT_OID_HEXSZ (a strict short oid was given), we have
* 2 options :
diff --git a/src/odb.c b/src/odb.c
index 1c877c9fc..890e6e2f8 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -18,6 +18,7 @@
#include "git2/odb_backend.h"
#include "git2/oid.h"
+#include "git2/oidarray.h"
#define GIT_ALTERNATES_FILE "info/alternates"
@@ -48,8 +49,37 @@ static git_cache *odb_cache(git_odb *odb)
return &odb->own_cache;
}
+static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id);
static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
+static git_otype odb_hardcoded_type(const git_oid *id)
+{
+ static git_oid empty_blob = {{ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
+ 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 }};
+ static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
+ 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }};
+
+ if (!git_oid_cmp(id, &empty_blob))
+ return GIT_OBJ_BLOB;
+
+ if (!git_oid_cmp(id, &empty_tree))
+ return GIT_OBJ_TREE;
+
+ return GIT_OBJ_BAD;
+}
+
+static int odb_read_hardcoded(git_rawobj *raw, const git_oid *id)
+{
+ git_otype type = odb_hardcoded_type(id);
+ if (type == GIT_OBJ_BAD)
+ return -1;
+
+ raw->type = type;
+ raw->len = 0;
+ raw->data = git__calloc(1, sizeof(uint8_t));
+ return 0;
+}
+
int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type)
{
const char *type_str = git_object_type2string(obj_type);
@@ -651,7 +681,7 @@ int git_odb_exists(git_odb *db, const git_oid *id)
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
git_odb_object_free(object);
- return (int)true;
+ return 1;
}
if (odb_exists_1(db, id, false))
@@ -716,23 +746,19 @@ int git_odb_exists_prefix(
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
- if (len > GIT_OID_HEXSZ)
- len = GIT_OID_HEXSZ;
- if (len == GIT_OID_HEXSZ) {
+ if (len >= GIT_OID_HEXSZ) {
if (git_odb_exists(db, short_id)) {
if (out)
git_oid_cpy(out, short_id);
return 0;
} else {
- return git_odb__error_notfound("no match for id prefix", short_id);
+ return git_odb__error_notfound(
+ "no match for id prefix", short_id, len);
}
}
- /* just copy valid part of short_id */
- memcpy(&key.id, short_id->id, (len + 1) / 2);
- if (len & 1)
- key.id[len / 2] &= 0xF0;
+ git_oid__cpy_prefix(&key, short_id, len);
error = odb_exists_prefix_1(out, db, &key, len, false);
@@ -740,11 +766,77 @@ int git_odb_exists_prefix(
error = odb_exists_prefix_1(out, db, &key, len, true);
if (error == GIT_ENOTFOUND)
- return git_odb__error_notfound("no match for id prefix", &key);
+ return git_odb__error_notfound("no match for id prefix", &key, len);
return error;
}
+int git_odb_expand_ids(
+ git_odb *db,
+ git_odb_expand_id *ids,
+ size_t count)
+{
+ size_t i;
+
+ assert(db && ids);
+
+ for (i = 0; i < count; i++) {
+ git_odb_expand_id *query = &ids[i];
+ int error = GIT_EAMBIGUOUS;
+
+ if (!query->type)
+ query->type = GIT_OBJ_ANY;
+
+ /* if we have a short OID, expand it first */
+ if (query->length >= GIT_OID_MINPREFIXLEN && query->length < GIT_OID_HEXSZ) {
+ git_oid actual_id;
+
+ error = odb_exists_prefix_1(&actual_id, db, &query->id, query->length, false);
+ if (!error) {
+ git_oid_cpy(&query->id, &actual_id);
+ query->length = GIT_OID_HEXSZ;
+ }
+ }
+
+ /*
+ * now we ought to have a 40-char OID, either because we've expanded it
+ * or because the user passed a full OID. Ensure its type is right.
+ */
+ if (query->length >= GIT_OID_HEXSZ) {
+ git_otype actual_type;
+
+ error = odb_otype_fast(&actual_type, db, &query->id);
+ if (!error) {
+ if (query->type != GIT_OBJ_ANY && query->type != actual_type)
+ error = GIT_ENOTFOUND;
+ else
+ query->type = actual_type;
+ }
+ }
+
+ switch (error) {
+ /* no errors, so we've successfully expanded the OID */
+ case 0:
+ continue;
+
+ /* the object is missing or ambiguous */
+ case GIT_ENOTFOUND:
+ case GIT_EAMBIGUOUS:
+ memset(&query->id, 0, sizeof(git_oid));
+ query->length = 0;
+ query->type = 0;
+ break;
+
+ /* something went very wrong with the ODB; bail hard */
+ default:
+ return error;
+ }
+ }
+
+ giterr_clear();
+ return 0;
+}
+
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
{
int error;
@@ -758,11 +850,53 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git
return error;
}
+static int odb_read_header_1(
+ size_t *len_p, git_otype *type_p, git_odb *db,
+ const git_oid *id, bool only_refreshed)
+{
+ size_t i;
+ git_otype ht;
+ bool passthrough = false;
+ int error;
+
+ if (!only_refreshed && (ht = odb_hardcoded_type(id)) != GIT_OBJ_BAD) {
+ *type_p = ht;
+ *len_p = 0;
+ return 0;
+ }
+
+ for (i = 0; i < db->backends.length; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (only_refreshed && !b->refresh)
+ continue;
+
+ if (!b->read_header) {
+ passthrough = true;
+ continue;
+ }
+
+ error = b->read_header(len_p, type_p, b, id);
+
+ switch (error) {
+ case GIT_PASSTHROUGH:
+ passthrough = true;
+ break;
+ case GIT_ENOTFOUND:
+ break;
+ default:
+ return error;
+ }
+ }
+
+ return passthrough ? GIT_PASSTHROUGH : GIT_ENOTFOUND;
+}
+
int git_odb__read_header_or_object(
git_odb_object **out, size_t *len_p, git_otype *type_p,
git_odb *db, const git_oid *id)
{
- size_t i;
int error = GIT_ENOTFOUND;
git_odb_object *object;
@@ -776,52 +910,32 @@ int git_odb__read_header_or_object(
}
*out = NULL;
+ error = odb_read_header_1(len_p, type_p, db, id, false);
- for (i = 0; i < db->backends.length && error < 0; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
+ if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
+ error = odb_read_header_1(len_p, type_p, db, id, true);
- if (b->read_header != NULL)
- error = b->read_header(len_p, type_p, b, id);
- }
+ if (error == GIT_ENOTFOUND)
+ return git_odb__error_notfound("cannot read header for", id, GIT_OID_HEXSZ);
- if (!error || error == GIT_PASSTHROUGH)
+ /* we found the header; return early */
+ if (!error)
return 0;
- /*
- * no backend could read only the header.
- * try reading the whole object and freeing the contents
- */
- if ((error = git_odb_read(&object, db, id)) < 0)
- return error; /* error already set - pass along */
-
- *len_p = object->cached.size;
- *type_p = object->cached.type;
- *out = object;
-
- return 0;
-}
-
-static git_oid empty_blob = {{ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
- 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 }};
-static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
- 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }};
-
-static int hardcoded_objects(git_rawobj *raw, const git_oid *id)
-{
- if (!git_oid_cmp(id, &empty_blob)) {
- raw->type = GIT_OBJ_BLOB;
- raw->len = 0;
- raw->data = git__calloc(1, sizeof(uint8_t));
- return 0;
- } else if (!git_oid_cmp(id, &empty_tree)) {
- raw->type = GIT_OBJ_TREE;
- raw->len = 0;
- raw->data = git__calloc(1, sizeof(uint8_t));
- return 0;
- } else {
- return GIT_ENOTFOUND;
+ if (error == GIT_PASSTHROUGH) {
+ /*
+ * no backend has header-reading functionality
+ * so try using `git_odb_read` instead
+ */
+ error = git_odb_read(&object, db, id);
+ if (!error) {
+ *len_p = object->cached.size;
+ *type_p = object->cached.type;
+ *out = object;
+ }
}
+
+ return error;
}
static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
@@ -832,7 +946,7 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
git_odb_object *object;
bool found = false;
- if (!hardcoded_objects(&raw, id))
+ if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0)
found = true;
for (i = 0; i < db->backends.length && !found; ++i) {
@@ -881,7 +995,30 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
error = odb_read_1(out, db, id, true);
if (error == GIT_ENOTFOUND)
- return git_odb__error_notfound("no match for id", id);
+ return git_odb__error_notfound("no match for id", id, GIT_OID_HEXSZ);
+
+ return error;
+}
+
+static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id)
+{
+ git_odb_object *object;
+ size_t _unused;
+ int error;
+
+ if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
+ *type_p = object->cached.type;
+ return 0;
+ }
+
+ error = odb_read_header_1(&_unused, type_p, db, id, false);
+
+ if (error == GIT_PASSTHROUGH) {
+ error = odb_read_1(&object, db, id, false);
+ if (!error)
+ *type_p = object->cached.type;
+ git_odb_object_free(object);
+ }
return error;
}
@@ -956,10 +1093,7 @@ int git_odb_read_prefix(
return 0;
}
- /* just copy valid part of short_id */
- memcpy(&key.id, short_id->id, (len + 1) / 2);
- if (len & 1)
- key.id[len / 2] &= 0xF0;
+ git_oid__cpy_prefix(&key, short_id, len);
error = read_prefix_1(out, db, &key, len, false);
@@ -967,7 +1101,7 @@ int git_odb_read_prefix(
error = read_prefix_1(out, db, &key, len, true);
if (error == GIT_ENOTFOUND)
- return git_odb__error_notfound("no match for prefix", &key);
+ return git_odb__error_notfound("no match for prefix", &key, len);
return error;
}
@@ -1223,12 +1357,14 @@ int git_odb_refresh(struct git_odb *db)
return 0;
}
-int git_odb__error_notfound(const char *message, const git_oid *oid)
+int git_odb__error_notfound(
+ const char *message, const git_oid *oid, size_t oid_len)
{
if (oid != NULL) {
char oid_str[GIT_OID_HEXSZ + 1];
- git_oid_tostr(oid_str, sizeof(oid_str), oid);
- giterr_set(GITERR_ODB, "Object not found - %s (%s)", message, oid_str);
+ git_oid_tostr(oid_str, oid_len, oid);
+ giterr_set(GITERR_ODB, "Object not found - %s (%.*s)",
+ message, oid_len, oid_str);
} else
giterr_set(GITERR_ODB, "Object not found - %s", message);
diff --git a/src/odb.h b/src/odb.h
index 281bd3a4d..31a9fd1b9 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -82,7 +82,8 @@ int git_odb__hashlink(git_oid *out, const char *path);
/*
* Generate a GIT_ENOTFOUND error for the ODB.
*/
-int git_odb__error_notfound(const char *message, const git_oid *oid);
+int git_odb__error_notfound(
+ const char *message, const git_oid *oid, size_t oid_len);
/*
* Generate a GIT_EAMBIGUOUS error for the ODB.
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 730c4b1e1..9d9bffd21 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -547,7 +547,8 @@ static int locate_object_short_oid(
/* Check that directory exists */
if (git_path_isdir(object_location->ptr) == false)
- return git_odb__error_notfound("no matching loose object for prefix", short_oid);
+ return git_odb__error_notfound("no matching loose object for prefix",
+ short_oid, len);
state.dir_len = git_buf_len(object_location);
state.short_oid_len = len;
@@ -560,7 +561,8 @@ static int locate_object_short_oid(
return error;
if (!state.found)
- return git_odb__error_notfound("no matching loose object for prefix", short_oid);
+ return git_odb__error_notfound("no matching loose object for prefix",
+ short_oid, len);
if (state.found > 1)
return git_odb__error_ambiguous("multiple matches in loose objects");
@@ -613,9 +615,10 @@ static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_
raw.len = 0;
raw.type = GIT_OBJ_BAD;
- if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
- error = git_odb__error_notfound("no matching loose object", oid);
- else if ((error = read_header_loose(&raw, &object_path)) == 0) {
+ if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
+ error = git_odb__error_notfound("no matching loose object",
+ oid, GIT_OID_HEXSZ);
+ } else if ((error = read_header_loose(&raw, &object_path)) == 0) {
*len_p = raw.len;
*type_p = raw.type;
}
@@ -633,9 +636,10 @@ static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p
assert(backend && oid);
- if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
- error = git_odb__error_notfound("no matching loose object", oid);
- else if ((error = read_loose(&raw, &object_path)) == 0) {
+ if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
+ error = git_odb__error_notfound("no matching loose object",
+ oid, GIT_OID_HEXSZ);
+ } else if ((error = read_loose(&raw, &object_path)) == 0) {
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 77d2c75b9..5a57864ad 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -264,7 +264,8 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
if (!pack_entry_find_inner(e, backend, oid, last_found))
return 0;
- return git_odb__error_notfound("failed to find pack entry", oid);
+ return git_odb__error_notfound(
+ "failed to find pack entry", oid, GIT_OID_HEXSZ);
}
static int pack_entry_find_prefix(
@@ -309,7 +310,8 @@ static int pack_entry_find_prefix(
}
if (!found)
- return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
+ return git_odb__error_notfound("no matching pack entry for prefix",
+ short_oid, len);
else
return 0;
}
@@ -333,7 +335,7 @@ static int pack_backend__refresh(git_odb_backend *backend_)
return 0;
if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
- return git_odb__error_notfound("failed to refresh packfiles", NULL);
+ return git_odb__error_notfound("failed to refresh packfiles", NULL, 0);
git_buf_sets(&path, backend->pack_folder);
diff --git a/src/oid.h b/src/oid.h
index aa1f0bfdc..922a2a347 100644
--- a/src/oid.h
+++ b/src/oid.h
@@ -44,4 +44,13 @@ GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
return git_oid__hashcmp(a->id, b->id);
}
+GIT_INLINE(void) git_oid__cpy_prefix(
+ git_oid *out, const git_oid *id, size_t len)
+{
+ memcpy(&out->id, id->id, (len + 1) / 2);
+
+ if (len & 1)
+ out->id[len / 2] &= 0xF0;
+}
+
#endif
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index 97736b714..a65f5586e 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -34,6 +34,8 @@
SSL_CTX *git__ssl_ctx;
+#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
+
#ifdef GIT_THREADS
static git_mutex *openssl_locks;
@@ -85,6 +87,7 @@ int git_openssl_stream_global_init(void)
{
#ifdef GIT_OPENSSL
long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+ const char *ciphers = git_libgit2__ssl_ciphers();
/* Older OpenSSL and MacOS OpenSSL doesn't have this */
#ifdef SSL_OP_NO_COMPRESSION
@@ -108,6 +111,16 @@ int git_openssl_stream_global_init(void)
git__ssl_ctx = NULL;
return -1;
}
+
+ if (!ciphers) {
+ ciphers = GIT_SSL_DEFAULT_CIPHERS;
+ }
+
+ if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
+ SSL_CTX_free(git__ssl_ctx);
+ git__ssl_ctx = NULL;
+ return -1;
+ }
#endif
git__on_shutdown(shutdown_ssl);
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 46fe8f3db..11e13f7d4 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -848,8 +848,10 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg,
git_packbuilder__cache_unlock(pb);
- if (overflow)
+ if (overflow) {
+ git__free(delta_buf);
return -1;
+ }
trg_object->delta_data = git__realloc(delta_buf, delta_size);
GITERR_CHECK_ALLOC(trg_object->delta_data);
diff --git a/src/pack.c b/src/pack.c
index 52c652178..e8bde71f3 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -1018,7 +1018,7 @@ static int packfile_open(struct git_pack_file *p)
unsigned char *idx_sha1;
if (p->index_version == -1 && pack_index_open(p) < 0)
- return git_odb__error_notfound("failed to open packfile", NULL);
+ return git_odb__error_notfound("failed to open packfile", NULL, 0);
/* if mwf opened by another thread, return now */
if (git_mutex_lock(&p->lock) < 0)
@@ -1099,7 +1099,7 @@ int git_packfile__name(char **out, const char *path)
path_len = strlen(path);
if (path_len < strlen(".idx"))
- return git_odb__error_notfound("invalid packfile path", NULL);
+ return git_odb__error_notfound("invalid packfile path", NULL, 0);
if (git_buf_printf(&buf, "%.*s.pack", (int)(path_len - strlen(".idx")), path) < 0)
return -1;
@@ -1117,7 +1117,7 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
*pack_out = NULL;
if (path_len < strlen(".idx"))
- return git_odb__error_notfound("invalid packfile path", NULL);
+ return git_odb__error_notfound("invalid packfile path", NULL, 0);
GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*p), path_len);
GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
@@ -1143,7 +1143,7 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
git__free(p);
- return git_odb__error_notfound("packfile not found", NULL);
+ return git_odb__error_notfound("packfile not found", NULL, 0);
}
/* ok, it looks sane as far as we can check without
@@ -1344,7 +1344,7 @@ static int pack_entry_find_offset(
}
if (!found)
- return git_odb__error_notfound("failed to find offset for pack entry", short_oid);
+ return git_odb__error_notfound("failed to find offset for pack entry", short_oid, len);
if (found > 1)
return git_odb__error_ambiguous("found multiple offsets for pack entry");
diff --git a/src/posix.c b/src/posix.c
index c7201ba14..b3f1a1cd3 100644
--- a/src/posix.c
+++ b/src/posix.c
@@ -224,6 +224,13 @@ int git__page_size(size_t *page_size)
return 0;
}
+int git__mmap_alignment(size_t *alignment)
+{
+ /* dummy; here we don't need any alignment anyway */
+ *alignment = 4096;
+ return 0;
+}
+
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
{
diff --git a/src/posix.h b/src/posix.h
index 8785a4c99..f204751cf 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -109,6 +109,7 @@ extern int p_getcwd(char *buffer_out, size_t size);
extern int p_rename(const char *from, const char *to);
extern int git__page_size(size_t *page_size);
+extern int git__mmap_alignment(size_t *page_size);
/**
* Platform-dependent methods
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index f6ed7201a..f978038e6 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -962,6 +962,7 @@ static int packed_write(refdb_fs_backend *backend)
for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
struct packref *ref = git_sortedcache_entry(refcache, i);
+ assert(ref);
if (packed_find_peel(backend, ref) < 0)
goto fail;
diff --git a/src/settings.c b/src/settings.c
index 88602bad0..0da19ea03 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -71,12 +71,18 @@ static int config_level_to_sysdir(int config_level)
}
extern char *git__user_agent;
+extern char *git__ssl_ciphers;
const char *git_libgit2__user_agent()
{
return git__user_agent;
}
+const char *git_libgit2__ssl_ciphers()
+{
+ return git__ssl_ciphers;
+}
+
int git_libgit2_opts(int key, ...)
{
int error = 0;
@@ -169,7 +175,7 @@ int git_libgit2_opts(int key, ...)
}
}
#else
- giterr_set(GITERR_NET, "Cannot set certificate locations: OpenSSL is not enabled");
+ giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL is not enabled");
error = -1;
#endif
break;
@@ -187,6 +193,22 @@ int git_libgit2_opts(int key, ...)
git_object__strict_input_validation = (va_arg(ap, int) != 0);
break;
+ case GIT_OPT_SET_SSL_CIPHERS:
+#ifdef GIT_OPENSSL
+ {
+ git__free(git__ssl_ciphers);
+ git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
+ if (!git__ssl_ciphers) {
+ giterr_set_oom();
+ error = -1;
+ }
+ }
+#else
+ giterr_set(GITERR_NET, "cannot set custom ciphers: OpenSSL is not enabled");
+ error = -1;
+#endif
+ break;
+
default:
giterr_set(GITERR_INVALID, "invalid option key");
error = -1;
diff --git a/src/submodule.c b/src/submodule.c
index 38db41529..3f39b9ef0 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -80,7 +80,8 @@ static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
if (blen > 0 && b[blen - 1] == '/')
blen--;
- return (alen == blen && strncmp(a, b, alen) == 0);
+ return (alen == 0 && blen == 0) ||
+ (alen == blen && strncmp(a, b, alen) == 0);
}
__KHASH_IMPL(
diff --git a/src/unix/map.c b/src/unix/map.c
index 72abb3418..c55ad1aa7 100644
--- a/src/unix/map.c
+++ b/src/unix/map.c
@@ -24,6 +24,11 @@ int git__page_size(size_t *page_size)
return 0;
}
+int git__mmap_alignment(size_t *alignment)
+{
+ return git__page_size(alignment);
+}
+
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
{
int mprot = PROT_READ;
diff --git a/src/win32/map.c b/src/win32/map.c
index a99c30f7e..03a3646a6 100644
--- a/src/win32/map.c
+++ b/src/win32/map.c
@@ -17,22 +17,41 @@ static DWORD get_page_size(void)
if (!page_size) {
GetSystemInfo(&sys);
- page_size = sys.dwAllocationGranularity;
+ page_size = sys.dwPageSize;
}
return page_size;
}
+static DWORD get_allocation_granularity(void)
+{
+ static DWORD granularity;
+ SYSTEM_INFO sys;
+
+ if (!granularity) {
+ GetSystemInfo(&sys);
+ granularity = sys.dwAllocationGranularity;
+ }
+
+ return granularity;
+}
+
int git__page_size(size_t *page_size)
{
*page_size = get_page_size();
return 0;
}
+int git__mmap_alignment(size_t *page_size)
+{
+ *page_size = get_allocation_granularity();
+ return 0;
+}
+
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
{
HANDLE fh = (HANDLE)_get_osfhandle(fd);
- DWORD page_size = get_page_size();
+ DWORD alignment = get_allocation_granularity();
DWORD fmap_prot = 0;
DWORD view_prot = 0;
DWORD off_low = 0;
@@ -62,12 +81,12 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
if (prot & GIT_PROT_READ)
view_prot |= FILE_MAP_READ;
- page_start = (offset / page_size) * page_size;
+ page_start = (offset / alignment) * alignment;
page_offset = offset - page_start;
- if (page_offset != 0) { /* offset must be multiple of page size */
+ if (page_offset != 0) { /* offset must be multiple of the allocation granularity */
errno = EINVAL;
- giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of page size");
+ giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of allocation granularity");
return -1;
}
diff --git a/tests/commit/parse.c b/tests/commit/parse.c
index 838cfb467..297fccc6b 100644
--- a/tests/commit/parse.c
+++ b/tests/commit/parse.c
@@ -498,6 +498,21 @@ committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
\n\
a simple commit which works\n";
+ const char *oneline_signature = "tree 51832e6397b30309c8bcad9c55fa6ae67778f378\n\
+parent a1b6decaaac768b5e01e1b5dbf5b2cc081bed1eb\n\
+author Some User <someuser@gmail.com> 1454537944 -0700\n\
+committer Some User <someuser@gmail.com> 1454537944 -0700\n\
+gpgsig bad\n\
+\n\
+corrupt signature\n";
+
+ const char *oneline_data = "tree 51832e6397b30309c8bcad9c55fa6ae67778f378\n\
+parent a1b6decaaac768b5e01e1b5dbf5b2cc081bed1eb\n\
+author Some User <someuser@gmail.com> 1454537944 -0700\n\
+committer Some User <someuser@gmail.com> 1454537944 -0700\n\
+\n\
+corrupt signature\n";
+
cl_git_pass(git_repository_odb__weakptr(&odb, g_repo));
cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[4], strlen(passing_commit_cases[4]), GIT_OBJ_COMMIT));
@@ -523,6 +538,15 @@ a simple commit which works\n";
cl_git_fail_with(GIT_ENOTFOUND, git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
cl_assert_equal_i(GITERR_OBJECT, giterr_last()->klass);
+ /* Parse the commit with a single-line signature */
+ git_buf_clear(&signature);
+ git_buf_clear(&signed_data);
+ cl_git_pass(git_odb_write(&commit_id, odb, oneline_signature, strlen(oneline_signature), GIT_OBJ_COMMIT));
+ cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
+ cl_assert_equal_s("bad", signature.ptr);
+ cl_assert_equal_s(oneline_data, signed_data.ptr);
+
+
git_buf_free(&signature);
git_buf_free(&signed_data);
diff --git a/tests/odb/mixed.c b/tests/odb/mixed.c
index 2dad4b64e..515eadfde 100644
--- a/tests/odb/mixed.c
+++ b/tests/odb/mixed.c
@@ -108,3 +108,158 @@ void test_odb_mixed__dup_oid_prefix_0(void) {
cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
git_odb_object_free(obj);
}
+
+struct expand_id_test_data {
+ char *lookup_id;
+ char *expected_id;
+ git_otype expected_type;
+};
+
+struct expand_id_test_data expand_id_test_data[] = {
+ /* some prefixes and their expected values */
+ { "dea509d0", NULL, GIT_OBJ_ANY },
+ { "00000000", NULL, GIT_OBJ_ANY },
+ { "dea509d0", NULL, GIT_OBJ_ANY },
+ { "dea509d09", "dea509d097ce692e167dfc6a48a7a280cc5e877e", GIT_OBJ_BLOB },
+ { "dea509d0b", "dea509d0b3cb8ee0650f6ca210bc83f4678851ba", GIT_OBJ_BLOB },
+ { "ce0136250", "ce013625030ba8dba906f756967f9e9ca394464a", GIT_OBJ_BLOB },
+ { "0ddeaded", NULL, GIT_OBJ_ANY },
+ { "4d5979b", "4d5979b468252190cb572ae758aca36928e8a91e", GIT_OBJ_TREE },
+ { "0ddeaded", NULL, GIT_OBJ_ANY },
+ { "0ddeadede", "0ddeadede9e6d6ccddce0ee1e5749eed0485e5ea", GIT_OBJ_BLOB },
+ { "0ddeaded9", "0ddeaded9502971eefe1e41e34d0e536853ae20f", GIT_OBJ_BLOB },
+ { "f00b4e", NULL, GIT_OBJ_ANY },
+
+ /* this OID is too short and should be ambiguous! */
+ { "f00", NULL, GIT_OBJ_ANY },
+
+ /* some full-length object ids */
+ { "0000000000000000000000000000000000000000", NULL, GIT_OBJ_ANY },
+ {
+ "dea509d097ce692e167dfc6a48a7a280cc5e877e",
+ "dea509d097ce692e167dfc6a48a7a280cc5e877e",
+ GIT_OBJ_BLOB
+ },
+ { "f00f00f00f00f00f00f00f00f00f00f00f00f00f", NULL, GIT_OBJ_ANY },
+ {
+ "4d5979b468252190cb572ae758aca36928e8a91e",
+ "4d5979b468252190cb572ae758aca36928e8a91e",
+ GIT_OBJ_TREE
+ },
+
+ /*
+ * ensure we're not leaking the return error code for the
+ * last lookup if the last object is invalid
+ */
+ { "0ddeadedfff", NULL, GIT_OBJ_ANY },
+};
+
+static void setup_prefix_query(
+ git_odb_expand_id **out_ids,
+ size_t *out_num)
+{
+ git_odb_expand_id *ids;
+ size_t num, i;
+
+ num = ARRAY_SIZE(expand_id_test_data);
+
+ cl_assert((ids = git__calloc(num, sizeof(git_odb_expand_id))));
+
+ for (i = 0; i < num; i++) {
+ git_odb_expand_id *id = &ids[i];
+
+ size_t len = strlen(expand_id_test_data[i].lookup_id);
+
+ git_oid_fromstrn(&id->id, expand_id_test_data[i].lookup_id, len);
+ id->length = (unsigned short)len;
+ id->type = expand_id_test_data[i].expected_type;
+ }
+
+ *out_ids = ids;
+ *out_num = num;
+}
+
+static void assert_found_objects(git_odb_expand_id *ids)
+{
+ size_t num, i;
+
+ num = ARRAY_SIZE(expand_id_test_data);
+
+ for (i = 0; i < num; i++) {
+ git_oid expected_id = {{0}};
+ size_t expected_len = 0;
+ git_otype expected_type = 0;
+
+ if (expand_id_test_data[i].expected_id) {
+ git_oid_fromstr(&expected_id, expand_id_test_data[i].expected_id);
+ expected_len = GIT_OID_HEXSZ;
+ expected_type = expand_id_test_data[i].expected_type;
+ }
+
+ cl_assert_equal_oid(&expected_id, &ids[i].id);
+ cl_assert_equal_i(expected_len, ids[i].length);
+ cl_assert_equal_i(expected_type, ids[i].type);
+ }
+}
+
+static void assert_notfound_objects(git_odb_expand_id *ids)
+{
+ git_oid expected_id = {{0}};
+ size_t num, i;
+
+ num = ARRAY_SIZE(expand_id_test_data);
+
+ for (i = 0; i < num; i++) {
+ cl_assert_equal_oid(&expected_id, &ids[i].id);
+ cl_assert_equal_i(0, ids[i].length);
+ cl_assert_equal_i(0, ids[i].type);
+ }
+}
+
+void test_odb_mixed__expand_ids(void)
+{
+ git_odb_expand_id *ids;
+ size_t i, num;
+
+ /* test looking for the actual (correct) types */
+
+ setup_prefix_query(&ids, &num);
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_found_objects(ids);
+ git__free(ids);
+
+ /* test looking for an explicit `type == 0` */
+
+ setup_prefix_query(&ids, &num);
+
+ for (i = 0; i < num; i++)
+ ids[i].type = 0;
+
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_found_objects(ids);
+ git__free(ids);
+
+ /* test looking for an explicit GIT_OBJ_ANY */
+
+ setup_prefix_query(&ids, &num);
+
+ for (i = 0; i < num; i++)
+ ids[i].type = GIT_OBJ_ANY;
+
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_found_objects(ids);
+ git__free(ids);
+
+ /* test looking for the completely wrong type */
+
+ setup_prefix_query(&ids, &num);
+
+ for (i = 0; i < num; i++)
+ ids[i].type = (ids[i].type == GIT_OBJ_BLOB) ?
+ GIT_OBJ_TREE : GIT_OBJ_BLOB;
+
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_notfound_objects(ids);
+ git__free(ids);
+}
+
diff --git a/tests/online/badssl.c b/tests/online/badssl.c
index 12badbda3..66b090df4 100644
--- a/tests/online/badssl.c
+++ b/tests/online/badssl.c
@@ -36,3 +36,11 @@ void test_online_badssl__self_signed(void)
cl_git_fail_with(GIT_ECERTIFICATE,
git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", NULL));
}
+
+void test_online_badssl__old_cipher(void)
+{
+ if (!g_has_ssl)
+ cl_skip();
+
+ cl_git_fail(git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", NULL));
+}
diff --git a/tests/rebase/abort.c b/tests/rebase/abort.c
index c4b3890bc..4cf14ddce 100644
--- a/tests/rebase/abort.c
+++ b/tests/rebase/abort.c
@@ -86,19 +86,41 @@ void test_rebase_abort__merge(void)
git_rebase_free(rebase);
}
+void test_rebase_abort__merge_by_id(void)
+{
+ git_rebase *rebase;
+ git_oid branch_id, onto_id;
+ git_annotated_commit *branch_head, *onto_head;
+
+ cl_git_pass(git_oid_fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64"));
+ cl_git_pass(git_oid_fromstr(&onto_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&onto_head, repo, &onto_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, NULL));
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ test_abort(branch_head, onto_head);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+
+ git_rebase_free(rebase);
+}
+
void test_rebase_abort__detached_head(void)
{
git_rebase *rebase;
- git_oid branch_id;
- git_reference *onto_ref;
+ git_oid branch_id, onto_id;
git_signature *signature;
git_annotated_commit *branch_head, *onto_head;
git_oid_fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64");
- cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
+ git_oid_fromstr(&onto_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
- cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref));
+ cl_git_pass(git_annotated_commit_lookup(&onto_head, repo, &onto_id));
cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400));
@@ -112,7 +134,6 @@ void test_rebase_abort__detached_head(void)
git_annotated_commit_free(branch_head);
git_annotated_commit_free(onto_head);
- git_reference_free(onto_ref);
git_rebase_free(rebase);
}
diff --git a/tests/rebase/merge.c b/tests/rebase/merge.c
index c60113b64..d090e02e8 100644
--- a/tests/rebase/merge.c
+++ b/tests/rebase/merge.c
@@ -252,6 +252,63 @@ void test_rebase_merge__commit(void)
git_rebase_free(rebase);
}
+void test_rebase_merge__commit_with_id(void)
+{
+ git_rebase *rebase;
+ git_oid branch_id, upstream_id;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, tree_id, parent_id;
+ git_signature *author;
+ git_commit *commit;
+ git_reflog *reflog;
+ const git_reflog_entry *reflog_entry;
+
+ cl_git_pass(git_oid_fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64"));
+ cl_git_pass(git_oid_fromstr(&upstream_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+
+ git_oid_fromstr(&parent_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+ cl_assert_equal_oid(&parent_id, git_commit_parent_id(commit, 0));
+
+ git_oid_fromstr(&tree_id, "4461379789c777d2a6c1f2ee0e9d6c86731b9992");
+ cl_assert_equal_oid(&tree_id, git_commit_tree_id(commit));
+
+ cl_assert_equal_s(NULL, git_commit_message_encoding(commit));
+ cl_assert_equal_s("Modification 1 to beef\n", git_commit_message(commit));
+
+ cl_git_pass(git_signature_new(&author,
+ "Edward Thomson", "ethomson@edwardthomson.com", 1405621769, 0-(4*60)));
+ cl_assert(git_signature__equal(author, git_commit_author(commit)));
+
+ cl_assert(git_signature__equal(signature, git_commit_committer(commit)));
+
+ /* Make sure the reflogs are updated appropriately */
+ cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
+ cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_oid(&parent_id, git_reflog_entry_id_old(reflog_entry));
+ cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry));
+ cl_assert_equal_s("rebase: Modification 1 to beef", git_reflog_entry_message(reflog_entry));
+
+ git_reflog_free(reflog);
+ git_signature_free(author);
+ git_commit_free(commit);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_rebase_free(rebase);
+}
+
void test_rebase_merge__blocked_when_dirty(void)
{
git_rebase *rebase;
diff --git a/tests/rebase/setup.c b/tests/rebase/setup.c
index 627d3b9de..b07a83af6 100644
--- a/tests/rebase/setup.c
+++ b/tests/rebase/setup.c
@@ -196,6 +196,115 @@ void test_rebase_setup__merge_onto_and_upstream(void)
git_rebase_free(rebase);
}
+/* git checkout beef && git rebase --merge --onto master gravy veal */
+void test_rebase_setup__merge_onto_upstream_and_branch(void)
+{
+ git_rebase *rebase;
+ git_reference *upstream_ref, *branch_ref, *onto_ref;
+ git_annotated_commit *upstream_head, *branch_head, *onto_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/beef"));
+ cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/veal"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, onto_head, NULL));
+
+ git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_assert_equal_file("3e8989b5a16d5258c935d998ef0e6bb139cc4757\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("4cacc6f6e740a5bc64faa33e04b8ef0733d8a127\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("3\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(upstream_head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+ git_reference_free(upstream_ref);
+ git_reference_free(branch_ref);
+ git_reference_free(onto_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout beef && git rebase --merge --onto `git rev-parse master`
+ * `git rev-parse veal` `git rev-parse gravy`
+ */
+void test_rebase_setup__merge_onto_upstream_and_branch_by_id(void)
+{
+ git_rebase *rebase;
+ git_oid upstream_id, branch_id, onto_id;
+ git_annotated_commit *upstream_head, *branch_head, *onto_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/beef"));
+ cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+ cl_git_pass(git_oid_fromstr(&upstream_id, "f87d14a4a236582a0278a916340a793714256864"));
+ cl_git_pass(git_oid_fromstr(&branch_id, "d616d97082eb7bb2dc6f180a7cca940993b7a56f"));
+ cl_git_pass(git_oid_fromstr(&onto_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"));
+
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&onto_head, repo, &onto_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, onto_head, NULL));
+
+ git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(upstream_head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+ git_rebase_free(rebase);
+}
+
/* Ensure merge commits are dropped in a rebase */
/* git checkout veal && git rebase --merge master */
void test_rebase_setup__branch_with_merges(void)
@@ -342,6 +451,102 @@ void test_rebase_setup__merge_null_branch_uses_HEAD(void)
git_rebase_free(rebase);
}
+/* git checkout b146bd7608eac53d9bf9e1a6963543588b555c64 && git rebase --merge master */
+void test_rebase_setup__merge_from_detached(void)
+{
+ git_rebase *rebase;
+ git_reference *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid branch_id, head_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_oid_fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64"));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout beef && git rebase --merge efad0b11c47cb2f0220cbd6f5b0f93bb99064b00 */
+void test_rebase_setup__merge_branch_by_id(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id, upstream_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+
+ cl_git_pass(git_oid_fromstr(&upstream_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_rebase_free(rebase);
+}
+
static int rebase_is_blocked(void)
{
git_rebase *rebase = NULL;