diff options
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 : @@ -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); @@ -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); @@ -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; |