diff options
author | Brian Lopez <seniorlopez@gmail.com> | 2018-01-02 13:29:49 -0800 |
---|---|---|
committer | Brian Lopez <seniorlopez@gmail.com> | 2018-01-02 13:29:49 -0800 |
commit | e8bc855834e188dd87515ef232758a70357f4c85 (patch) | |
tree | 5ed7b6d899a644b5c46d8ab90c0758a55900e9f9 | |
parent | 72fbf05ceb088e6592b44d7656ed2bca14506696 (diff) | |
parent | 7610638ec829ffd6da6d5f74b5b14dbb32b74924 (diff) | |
download | libgit2-e8bc855834e188dd87515ef232758a70357f4c85.tar.gz |
Merge remote-tracking branch 'origin/master' into charliesome/trailer-info
44 files changed, 1397 insertions, 427 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 549f34ad8..bedf85819 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,6 @@ OPTION( ENABLE_TRACE "Enables tracing support" OFF ) OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF ) OPTION( USE_SHA1DC "Use SHA-1 with collision detection" OFF ) -OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) OPTION( USE_HTTPS "Enable HTTPS support. Can be set to a specific backend" ON ) OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) @@ -52,10 +51,15 @@ OPTION( CURL "Use curl for HTTP if available" ON) OPTION( USE_EXT_HTTP_PARSER "Use system HTTP_Parser if available" ON) OPTION( DEBUG_POOL "Enable debug pool allocator" OFF ) OPTION( ENABLE_WERROR "Enable compilation with -Werror" OFF ) +OPTION( USE_BUNDLED_ZLIB "Use the bundled version of zlib" OFF ) + IF (UNIX AND NOT APPLE) OPTION( ENABLE_REPRODUCIBLE_BUILDS "Enable reproducible builds" OFF ) ENDIF() -OPTION( USE_BUNDLED_ZLIB "Use the bundled version of zlib" OFF ) + +IF (APPLE) + OPTION( USE_ICONV "Link with and use iconv library" ON ) +ENDIF() IF(MSVC) # This option is only available when building with MSVC. By default, libgit2 diff --git a/git.git-authors b/git.git-authors index 6a85224b4..3bc009fcc 100644 --- a/git.git-authors +++ b/git.git-authors @@ -53,6 +53,7 @@ ok Jeff King <peff@peff.net> ok Johannes Schindelin <Johannes.Schindelin@gmx.de> ok Johannes Sixt <j6t@kdbg.org> ask Jonathan Nieder <jrnieder@gmail.com> +ok Jonathan Tan <jonathantanmy@google.com> ok Junio C Hamano <gitster@pobox.com> ok Kristian Høgsberg <krh@redhat.com> ok Linus Torvalds <torvalds@linux-foundation.org> diff --git a/include/git2/notes.h b/include/git2/notes.h index 3a626cafd..853e5de59 100644 --- a/include/git2/notes.h +++ b/include/git2/notes.h @@ -52,6 +52,20 @@ GIT_EXTERN(int) git_note_iterator_new( const char *notes_ref); /** + * Creates a new iterator for notes from a commit + * + * The iterator must be freed manually by the user. + * + * @param out pointer to the iterator + * @param notes_commit a pointer to the notes commit object + * + * @return 0 or an error code + */ +GIT_EXTERN(int) git_note_commit_iterator_new( + git_note_iterator **out, + git_commit *notes_commit); + +/** * Frees an git_note_iterator * * @param it pointer to the iterator @@ -94,6 +108,25 @@ GIT_EXTERN(int) git_note_read( const char *notes_ref, const git_oid *oid); + +/** + * Read the note for an object from a note commit + * + * The note must be freed manually by the user. + * + * @param out pointer to the read note; NULL in case of error + * @param repo repository where to look up the note + * @param notes_commit a pointer to the notes commit object + * @param oid OID of the git object to read the note from + * + * @return 0 or an error code + */ +GIT_EXTERN(int) git_note_commit_read( + git_note **out, + git_repository *repo, + git_commit *notes_commit, + const git_oid *oid); + /** * Get the note author * @@ -153,6 +186,36 @@ GIT_EXTERN(int) git_note_create( const char *note, int force); +/** + * Add a note for an object from a commit + * + * This function will create a notes commit for a given object, + * the commit is a dangling commit, no reference is created. + * + * @param notes_commit_out pointer to store the commit (optional); + * NULL in case of error + * @param notes_blob_out a point to the id of a note blob (optional) + * @param repo repository where the note will live + * @param parent Pointer to parent note + * or NULL if this shall start a new notes tree + * @param author signature of the notes commit author + * @param committer signature of the notes commit committer + * @param oid OID of the git object to decorate + * @param note Content of the note to add for object oid + * @param allow_note_overwrite Overwrite existing note + * + * @return 0 or an error code + */ +GIT_EXTERN(int) git_note_commit_create( + git_oid *notes_commit_out, + git_oid *notes_blob_out, + git_repository *repo, + git_commit *parent, + const git_signature *author, + const git_signature *committer, + const git_oid *oid, + const char *note, + int allow_note_overwrite); /** * Remove the note for an object @@ -174,6 +237,32 @@ GIT_EXTERN(int) git_note_remove( const git_oid *oid); /** + * Remove the note for an object + * + * @param notes_commit_out pointer to store the new notes commit (optional); + * NULL in case of error. + * When removing a note a new tree containing all notes + * sans the note to be removed is created and a new commit + * pointing to that tree is also created. + * In the case where the resulting tree is an empty tree + * a new commit pointing to this empty tree will be returned. + * @param repo repository where the note lives + * @param notes_commit a pointer to the notes commit object + * @param author signature of the notes commit author + * @param committer signature of the notes commit committer + * @param oid OID of the git object to remove the note from + * + * @return 0 or an error code + */ +GIT_EXTERN(int) git_note_commit_remove( + git_oid *notes_commit_out, + git_repository *repo, + git_commit *notes_commit, + const git_signature *author, + const git_signature *committer, + const git_oid *oid); + +/** * Free a git_note object * * @param note git_note object diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e456ab725..2c82d1f59 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -310,7 +310,7 @@ ENDIF() ADD_FEATURE_INFO(SPNEGO GIT_GSSAPI "SPNEGO authentication support") # Optional external dependency: iconv -IF (USE_ICONV OR CMAKE_SYSTEM_NAME MATCHES "Darwin") +IF (USE_ICONV) FIND_PACKAGE(Iconv) ENDIF() IF (ICONV_FOUND) diff --git a/src/checkout.c b/src/checkout.c index caed6cdf1..528fbdf92 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -2022,8 +2022,11 @@ static int checkout_write_entry( (error = checkout_safe_for_update_only(data, fullpath->ptr, side->mode)) <= 0) return error; - return checkout_write_content(data, - &side->id, fullpath->ptr, hint_path, side->mode, &st); + if (!S_ISGITLINK(side->mode)) + return checkout_write_content(data, + &side->id, fullpath->ptr, hint_path, side->mode, &st); + + return 0; } static int checkout_write_entries( diff --git a/src/diff_file.c b/src/diff_file.c index 270d59bbb..0813315f5 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -139,7 +139,6 @@ int git_diff_file_content__init_from_src( memset(fc, 0, sizeof(*fc)); fc->repo = repo; fc->file = as_file; - fc->blob = src->blob; if (!src->blob && !src->buf) { fc->flags |= GIT_DIFF_FLAG__NO_DATA; @@ -149,12 +148,15 @@ int git_diff_file_content__init_from_src( fc->file->mode = GIT_FILEMODE_BLOB; if (src->blob) { + git_blob_dup((git_blob **)&fc->blob, (git_blob *) src->blob); fc->file->size = git_blob_rawsize(src->blob); git_oid_cpy(&fc->file->id, git_blob_id(src->blob)); fc->file->id_abbrev = GIT_OID_HEXSZ; fc->map.len = (size_t)fc->file->size; fc->map.data = (char *)git_blob_rawcontent(src->blob); + + fc->flags |= GIT_DIFF_FLAG__FREE_BLOB; } else { fc->file->size = src->buflen; git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB); diff --git a/src/fetchhead.c b/src/fetchhead.c index ac25723d3..e55e7c85b 100644 --- a/src/fetchhead.c +++ b/src/fetchhead.c @@ -118,7 +118,7 @@ int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs) if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0) return -1; - if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) { + if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_APPEND, GIT_REFS_FILE_MODE) < 0) { git_buf_free(&path); return -1; } diff --git a/src/fileops.c b/src/fileops.c index ad3f67e2b..58988c2d2 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -102,6 +102,16 @@ int git_futils_open_ro(const char *path) return fd; } +int git_futils_truncate(const char *path, int mode) +{ + int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode); + if (fd < 0) + return git_path_set_error(errno, path, "open"); + + close(fd); + return 0; +} + git_off_t git_futils_filesize(git_file fd) { struct stat sb; diff --git a/src/fileops.h b/src/fileops.h index fd5441243..57b9d173e 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -248,6 +248,11 @@ extern int git_futils_cp_r( extern int git_futils_open_ro(const char *path); /** + * Truncate a file, creating it if it doesn't exist. + */ +extern int git_futils_truncate(const char *path, int mode); + +/** * Get the filesize in bytes of a file */ extern git_off_t git_futils_filesize(git_file fd); diff --git a/src/hash/hash_common_crypto.h b/src/hash/hash_common_crypto.h index eeeddd0cc..4cd229d3c 100644 --- a/src/hash/hash_common_crypto.h +++ b/src/hash/hash_common_crypto.h @@ -16,6 +16,8 @@ struct git_hash_ctx { CC_SHA1_CTX c; }; +#define CC_LONG_MAX ((CC_LONG)-1) + #define git_hash_global_init() 0 #define git_hash_ctx_init(ctx) git_hash_init(ctx) #define git_hash_ctx_cleanup(ctx) @@ -27,10 +29,21 @@ GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx) return 0; } -GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) +GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *_data, size_t len) { + const unsigned char *data = _data; + assert(ctx); - CC_SHA1_Update(&ctx->c, data, len); + + while (len > 0) { + CC_LONG chunk = (len > CC_LONG_MAX) ? CC_LONG_MAX : (CC_LONG)len; + + CC_SHA1_Update(&ctx->c, data, chunk); + + data += chunk; + len -= chunk; + } + return 0; } diff --git a/src/hash/hash_win32.c b/src/hash/hash_win32.c index 4d53a57bd..20ba9a5fe 100644 --- a/src/hash/hash_win32.c +++ b/src/hash/hash_win32.c @@ -136,12 +136,21 @@ GIT_INLINE(int) hash_cryptoapi_init(git_hash_ctx *ctx) return 0; } -GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *data, size_t len) +GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *_data, size_t len) { + const BYTE *data = (BYTE *)_data; + assert(ctx->ctx.cryptoapi.valid); - if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, (const BYTE *)data, (DWORD)len, 0)) - return -1; + while (len > 0) { + DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len; + + if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) + return -1; + + data += chunk; + len -= chunk; + } return 0; } @@ -202,10 +211,19 @@ GIT_INLINE(int) hash_cng_init(git_hash_ctx *ctx) return 0; } -GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *data, size_t len) +GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *_data, size_t len) { - if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, (PBYTE)data, (ULONG)len, 0) < 0) - return -1; + PBYTE data = (PBYTE)_data; + + while (len > 0) { + ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len; + + if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) + return -1; + + data += chunk; + len -= chunk; + } return 0; } diff --git a/src/indexer.c b/src/indexer.c index aedefe523..a5e842272 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -844,6 +844,7 @@ static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats) static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats) { unsigned int i; + int error; struct delta_info *delta; int progressed = 0, non_null = 0, progress_cb_result; @@ -858,8 +859,13 @@ static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats) non_null = 1; idx->off = delta->delta_off; - if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0) - continue; + if ((error = git_packfile_unpack(&obj, idx->pack, &idx->off)) < 0) { + if (error == GIT_PASSTHROUGH) { + /* We have not seen the base object, we'll try again later. */ + continue; + } + return -1; + } if (hash_and_save(idx, &obj, delta->delta_off) < 0) continue; @@ -951,6 +957,10 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack"); return -1; } + if (idx->off + 20 > idx->pack->mwf.size) { + giterr_set(GITERR_INDEXER, "missing trailer at the end of the pack"); + return -1; + } packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left); if (packfile_trailer == NULL) { diff --git a/src/iterator.c b/src/iterator.c index 960031233..132b2c77c 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -23,6 +23,7 @@ #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS) #define iterator__honor_ignores(I) iterator__flag(I,HONOR_IGNORES) #define iterator__ignore_dot_git(I) iterator__flag(I,IGNORE_DOT_GIT) +#define iterator__descend_symlinks(I) iterator__flag(I,DESCEND_SYMLINKS) static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case) @@ -1491,10 +1492,41 @@ static int filesystem_iterator_current( return 0; } +static int filesystem_iterator_is_dir( + bool *is_dir, + const filesystem_iterator *iter, + const filesystem_iterator_entry *entry) +{ + struct stat st; + git_buf fullpath = GIT_BUF_INIT; + int error = 0; + + if (S_ISDIR(entry->st.st_mode)) { + *is_dir = 1; + goto done; + } + + if (!iterator__descend_symlinks(iter) || !S_ISLNK(entry->st.st_mode)) { + *is_dir = 0; + goto done; + } + + if ((error = git_buf_joinpath(&fullpath, iter->root, entry->path)) < 0 || + (error = p_stat(fullpath.ptr, &st)) < 0) + goto done; + + *is_dir = S_ISDIR(st.st_mode); + +done: + git_buf_free(&fullpath); + return error; +} + static int filesystem_iterator_advance( const git_index_entry **out, git_iterator *i) { filesystem_iterator *iter = (filesystem_iterator *)i; + bool is_dir; int error = 0; iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS; @@ -1519,7 +1551,10 @@ static int filesystem_iterator_advance( entry = frame->entries.contents[frame->next_idx]; frame->next_idx++; - if (S_ISDIR(entry->st.st_mode)) { + if ((error = filesystem_iterator_is_dir(&is_dir, iter, entry)) < 0) + break; + + if (is_dir) { if (iterator__do_autoexpand(iter)) { error = filesystem_iterator_frame_push(iter, entry); diff --git a/src/iterator.h b/src/iterator.h index 0bcb128d9..a6497d87b 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -39,6 +39,8 @@ typedef enum { GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE = (1u << 5), /** include conflicts */ GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 6), + /** descend into symlinked directories */ + GIT_ITERATOR_DESCEND_SYMLINKS = (1u << 7), } git_iterator_flag_t; typedef enum { diff --git a/src/notes.c b/src/notes.c index 75108b9c9..f63ef3667 100644 --- a/src/notes.c +++ b/src/notes.c @@ -268,7 +268,9 @@ static int insert_note_in_tree_enotfound_cb(git_tree **out, GIT_FILEMODE_BLOB); } -static int note_write(git_oid *out, +static int note_write( + git_oid *notes_commit_out, + git_oid *notes_blob_out, git_repository *repo, const git_signature *author, const git_signature *committer, @@ -294,13 +296,17 @@ static int note_write(git_oid *out, insert_note_in_tree_enotfound_cb)) < 0) goto cleanup; - if (out) - git_oid_cpy(out, &oid); + if (notes_blob_out) + git_oid_cpy(notes_blob_out, &oid); + error = git_commit_create(&oid, repo, notes_ref, author, committer, NULL, GIT_NOTES_DEFAULT_MSG_ADD, tree, *parents == NULL ? 0 : 1, (const git_commit **) parents); + if (notes_commit_out) + git_oid_cpy(notes_commit_out, &oid); + cleanup: git_tree_free(tree); return error; @@ -363,7 +369,9 @@ cleanup: return error; } -static int note_remove(git_repository *repo, +static int note_remove( + git_oid *notes_commit_out, + git_repository *repo, const git_signature *author, const git_signature *committer, const char *notes_ref, git_tree *tree, const char *target, git_commit **parents) @@ -383,6 +391,12 @@ static int note_remove(git_repository *repo, *parents == NULL ? 0 : 1, (const git_commit **) parents); + if (error < 0) + goto cleanup; + + if (notes_commit_out) + git_oid_cpy(notes_commit_out, &oid); + cleanup: git_tree_free(tree_after_removal); return error; @@ -410,8 +424,7 @@ static int normalize_namespace(char **out, git_repository *repo, const char *not return note_get_default_ref(out, repo); } -static int retrieve_note_tree_and_commit( - git_tree **tree_out, +static int retrieve_note_commit( git_commit **commit_out, char **notes_ref_out, git_repository *repo, @@ -429,34 +442,82 @@ static int retrieve_note_tree_and_commit( if (git_commit_lookup(commit_out, repo, &oid) < 0) return error; - if ((error = git_commit_tree(tree_out, *commit_out)) < 0) - return error; - return 0; } +int git_note_commit_read( + git_note **out, + git_repository *repo, + git_commit *notes_commit, + const git_oid *oid) +{ + int error; + git_tree *tree = NULL; + char target[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(target, sizeof(target), oid); + + if ((error = git_commit_tree(&tree, notes_commit)) < 0) + goto cleanup; + + error = note_lookup(out, repo, notes_commit, tree, target); + +cleanup: + git_tree_free(tree); + return error; +} + int git_note_read(git_note **out, git_repository *repo, const char *notes_ref_in, const git_oid *oid) { int error; - char *target = NULL, *notes_ref = NULL; - git_tree *tree = NULL; + char *notes_ref = NULL; git_commit *commit = NULL; - target = git_oid_allocfmt(oid); - GITERR_CHECK_ALLOC(target); + error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in); - if (!(error = retrieve_note_tree_and_commit( - &tree, &commit, ¬es_ref, repo, notes_ref_in))) - error = note_lookup(out, repo, commit, tree, target); + if (error < 0) + goto cleanup; + error = git_note_commit_read(out, repo, commit, oid); + +cleanup: git__free(notes_ref); - git__free(target); - git_tree_free(tree); git_commit_free(commit); return error; } +int git_note_commit_create( + git_oid *notes_commit_out, + git_oid *notes_blob_out, + git_repository *repo, + git_commit *parent, + const git_signature *author, + const git_signature *committer, + const git_oid *oid, + const char *note, + int allow_note_overwrite) +{ + int error; + git_tree *tree = NULL; + char target[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(target, sizeof(target), oid); + + if (parent != NULL && (error = git_commit_tree(&tree, parent)) < 0) + goto cleanup; + + error = note_write(notes_commit_out, notes_blob_out, repo, author, + committer, NULL, note, tree, target, &parent, allow_note_overwrite); + + if (error < 0) + goto cleanup; + +cleanup: + git_tree_free(tree); + return error; +} + int git_note_create( git_oid *out, git_repository *repo, @@ -468,25 +529,59 @@ int git_note_create( int allow_note_overwrite) { int error; - char *target = NULL, *notes_ref = NULL; - git_commit *commit = NULL; - git_tree *tree = NULL; - - target = git_oid_allocfmt(oid); - GITERR_CHECK_ALLOC(target); + char *notes_ref = NULL; + git_commit *existing_notes_commit = NULL; + git_reference *ref = NULL; + git_oid notes_blob_oid, notes_commit_oid; - error = retrieve_note_tree_and_commit(&tree, &commit, ¬es_ref, repo, notes_ref_in); + error = retrieve_note_commit(&existing_notes_commit, ¬es_ref, + repo, notes_ref_in); if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; - error = note_write(out, repo, author, committer, notes_ref, - note, tree, target, &commit, allow_note_overwrite); + error = git_note_commit_create(¬es_commit_oid, + ¬es_blob_oid, + repo, existing_notes_commit, author, + committer, oid, note, + allow_note_overwrite); + if (error < 0) + goto cleanup; + + error = git_reference_create(&ref, repo, notes_ref, + ¬es_commit_oid, 1, NULL); + + if (out != NULL) + git_oid_cpy(out, ¬es_blob_oid); cleanup: git__free(notes_ref); - git__free(target); - git_commit_free(commit); + git_commit_free(existing_notes_commit); + git_reference_free(ref); + return error; +} + +int git_note_commit_remove( + git_oid *notes_commit_out, + git_repository *repo, + git_commit *notes_commit, + const git_signature *author, + const git_signature *committer, + const git_oid *oid) +{ + int error; + git_tree *tree = NULL; + char target[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(target, sizeof(target), oid); + + if ((error = git_commit_tree(&tree, notes_commit)) < 0) + goto cleanup; + + error = note_remove(notes_commit_out, + repo, author, committer, NULL, tree, target, ¬es_commit); + +cleanup: git_tree_free(tree); return error; } @@ -496,22 +591,29 @@ int git_note_remove(git_repository *repo, const char *notes_ref_in, const git_oid *oid) { int error; - char *target = NULL, *notes_ref; - git_commit *commit = NULL; - git_tree *tree = NULL; + char *notes_ref_target = NULL; + git_commit *existing_notes_commit = NULL; + git_oid new_notes_commit; + git_reference *notes_ref = NULL; - target = git_oid_allocfmt(oid); - GITERR_CHECK_ALLOC(target); + error = retrieve_note_commit(&existing_notes_commit, ¬es_ref_target, + repo, notes_ref_in); - if (!(error = retrieve_note_tree_and_commit( - &tree, &commit, ¬es_ref, repo, notes_ref_in))) - error = note_remove( - repo, author, committer, notes_ref, tree, target, &commit); + if (error < 0) + goto cleanup; - git__free(notes_ref); - git__free(target); - git_commit_free(commit); - git_tree_free(tree); + error = git_note_commit_remove(&new_notes_commit, repo, + existing_notes_commit, author, committer, oid); + if (error < 0) + goto cleanup; + + error = git_reference_create(¬es_ref, repo, notes_ref_target, + &new_notes_commit, 1, NULL); + +cleanup: + git__free(notes_ref_target); + git_reference_free(notes_ref); + git_commit_free(existing_notes_commit); return error; } @@ -639,7 +741,6 @@ int git_note_foreach( return error; } - void git_note_iterator_free(git_note_iterator *it) { if (it == NULL) @@ -648,6 +749,24 @@ void git_note_iterator_free(git_note_iterator *it) git_iterator_free(it); } +int git_note_commit_iterator_new( + git_note_iterator **it, + git_commit *notes_commit) +{ + int error; + git_tree *tree; + + if ((error = git_commit_tree(&tree, notes_commit)) < 0) + goto cleanup; + + if ((error = git_iterator_for_tree(it, tree, NULL)) < 0) + git_iterator_free(*it); + +cleanup: + git_tree_free(tree); + + return error; +} int git_note_iterator_new( git_note_iterator **it, @@ -656,19 +775,16 @@ int git_note_iterator_new( { int error; git_commit *commit = NULL; - git_tree *tree = NULL; char *notes_ref; - error = retrieve_note_tree_and_commit(&tree, &commit, ¬es_ref, repo, notes_ref_in); + error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in); if (error < 0) goto cleanup; - if ((error = git_iterator_for_tree(it, tree, NULL)) < 0) - git_iterator_free(*it); + error = git_note_commit_iterator_new(it, commit); cleanup: git__free(notes_ref); - git_tree_free(tree); git_commit_free(commit); return error; diff --git a/src/object.c b/src/object.c index 4d069a34c..48f561384 100644 --- a/src/object.c +++ b/src/object.c @@ -236,13 +236,22 @@ const char *git_object_type2string(git_otype type) git_otype git_object_string2type(const char *str) { + if (!str) + return GIT_OBJ_BAD; + + return git_object_stringn2type(str, strlen(str)); +} + +git_otype git_object_stringn2type(const char *str, size_t len) +{ size_t i; - if (!str || !*str) + if (!str || !len || !*str) return GIT_OBJ_BAD; for (i = 0; i < ARRAY_SIZE(git_objects_table); i++) - if (!strcmp(str, git_objects_table[i].str)) + if (*git_objects_table[i].str && + !git__prefixncmp(str, len, git_objects_table[i].str)) return (git_otype)i; return GIT_OBJ_BAD; diff --git a/src/object.h b/src/object.h index ff61c1d33..e46c9cafa 100644 --- a/src/object.h +++ b/src/object.h @@ -30,6 +30,8 @@ int git_object__from_odb_object( int git_object__resolve_to_type(git_object **obj, git_otype type); +git_otype git_object_stringn2type(const char *str, size_t len); + int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header); void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid); diff --git a/src/odb_loose.c b/src/odb_loose.c index 72b47f091..9900aae2a 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -16,6 +16,7 @@ #include "delta.h" #include "filebuf.h" #include "object.h" +#include "zstream.h" #include "git2/odb_backend.h" #include "git2/types.h" @@ -119,53 +120,58 @@ static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj) return used; } -static size_t get_object_header(obj_hdr *hdr, unsigned char *data) +static int parse_header( + obj_hdr *out, + size_t *out_len, + const unsigned char *_data, + size_t data_len) { - char c, typename[10]; - size_t size, used = 0; + const char *data = (char *)_data; + size_t i, typename_len, size_idx, size_len; + int64_t size; - /* - * type name string followed by space. - */ - while ((c = data[used]) != ' ') { - typename[used++] = c; - if (used >= sizeof(typename)) - return 0; + *out_len = 0; + + /* find the object type name */ + for (i = 0, typename_len = 0; i < data_len; i++, typename_len++) { + if (data[i] == ' ') + break; } - typename[used] = 0; - if (used == 0) - return 0; - hdr->type = git_object_string2type(typename); - used++; /* consume the space */ - /* - * length follows immediately in decimal (without - * leading zeros). - */ - size = data[used++] - '0'; - if (size > 9) - return 0; - if (size) { - while ((c = data[used]) != '\0') { - size_t d = c - '0'; - if (d > 9) - break; - used++; - size = size * 10 + d; - } + if (typename_len == data_len) + goto on_error; + + out->type = git_object_stringn2type(data, typename_len); + + size_idx = typename_len + 1; + for (i = size_idx, size_len = 0; i < data_len; i++, size_len++) { + if (data[i] == '\0') + break; } - hdr->size = size; - /* - * the length must be followed by a zero byte - */ - if (data[used++] != '\0') - return 0; + if (i == data_len) + goto on_error; - return used; -} + if (git__strntol64(&size, &data[size_idx], size_len, NULL, 10) < 0 || + size < 0) + goto on_error; + + if ((uint64_t)size > SIZE_MAX) { + giterr_set(GITERR_OBJECT, "object is larger than available memory"); + return -1; + } + out->size = size; + if (GIT_ADD_SIZET_OVERFLOW(out_len, i, 1)) + goto on_error; + + return 0; + +on_error: + giterr_set(GITERR_OBJECT, "failed to parse loose object: invalid header"); + return -1; +} /*********************************************************** * @@ -269,45 +275,6 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) return 0; } -static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr) -{ - unsigned char *buf, *head = hb; - size_t tail, alloc_size; - - /* - * allocate a buffer to hold the inflated data and copy the - * initial sequence of inflated data from the tail of the - * head buffer, if any. - */ - if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr->size, 1) || - (buf = git__malloc(alloc_size)) == NULL) { - inflateEnd(s); - return NULL; - } - tail = s->total_out - used; - if (used > 0 && tail > 0) { - if (tail > hdr->size) - tail = hdr->size; - memcpy(buf, head + used, tail); - } - used = tail; - - /* - * inflate the remainder of the object data, if any - */ - if (hdr->size < used) - inflateEnd(s); - else { - set_stream_output(s, buf + used, hdr->size - used); - if (finish_inflate(s)) { - git__free(buf); - return NULL; - } - } - - return buf; -} - /* * At one point, there was a loose object format that was intended to * mimic the format used in pack-files. This was to allow easy copying @@ -354,43 +321,74 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj) static int inflate_disk_obj(git_rawobj *out, git_buf *obj) { - unsigned char head[64], *buf; - z_stream zs; + git_zstream zstream = GIT_ZSTREAM_INIT; + unsigned char head[64], *body = NULL; + size_t decompressed, head_len, body_len, alloc_size; obj_hdr hdr; - size_t used; + int error; - /* - * check for a pack-like loose object - */ + /* check for a pack-like loose object */ if (!is_zlib_compressed_data((unsigned char *)obj->ptr)) return inflate_packlike_loose_disk_obj(out, obj); + if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0 || + (error = git_zstream_set_input(&zstream, git_buf_cstr(obj), git_buf_len(obj))) < 0) + goto done; + + decompressed = sizeof(head); + /* - * inflate the initial part of the io buffer in order - * to parse the object header (type and size). - */ - if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK || - (used = get_object_header(&hdr, head)) == 0 || - !git_object_typeisloose(hdr.type)) - { - abort_inflate(&zs); + * inflate the initial part of the compressed buffer in order to parse the + * header; read the largest header possible, then push back the remainder. + */ + if ((error = git_zstream_get_output(head, &decompressed, &zstream)) < 0 || + (error = parse_header(&hdr, &head_len, head, decompressed)) < 0) + goto done; + + if (!git_object_typeisloose(hdr.type)) { giterr_set(GITERR_ODB, "failed to inflate disk object"); - return -1; + error = -1; + goto done; } /* * allocate a buffer and inflate the object data into it * (including the initial sequence in the head buffer). */ - if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL) - return -1; - buf[hdr.size] = '\0'; + if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) || + (body = git__malloc(alloc_size)) == NULL) { + error = -1; + goto done; + } - out->data = buf; + assert(decompressed >= head_len); + body_len = decompressed - head_len; + + if (body_len) + memcpy(body, head + head_len, body_len); + + decompressed = hdr.size - body_len; + if ((error = git_zstream_get_output(body + body_len, &decompressed, &zstream)) < 0) + goto done; + + if (!git_zstream_done(&zstream)) { + giterr_set(GITERR_ZLIB, "failed to finish zlib inflation: stream aborted prematurely"); + error = -1; + goto done; + } + + body[hdr.size] = '\0'; + + out->data = body; out->len = hdr.size; out->type = hdr.type; - return 0; +done: + if (error < 0) + git__free(body); + + git_zstream_free(&zstream); + return error; } @@ -435,6 +433,7 @@ static int read_header_loose(git_rawobj *out, git_buf *loc) git_file fd; z_stream zs; obj_hdr header_obj; + size_t header_len; unsigned char raw_buffer[16], inflated_buffer[64]; assert(out && loc); @@ -460,7 +459,7 @@ static int read_header_loose(git_rawobj *out, git_buf *loc) } if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR) - || get_object_header(&header_obj, inflated_buffer) == 0 + || parse_header(&header_obj, &header_len, inflated_buffer, sizeof(inflated_buffer)) < 0 || git_object_typeisloose(header_obj.type) == 0) { giterr_set(GITERR_ZLIB, "failed to read loose object header"); diff --git a/src/pack-objects.c b/src/pack-objects.c index ef272e8f5..e9245143c 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -1642,7 +1642,7 @@ int insert_tree(git_packbuilder *pb, git_tree *tree) if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0) return error; - if (obj->seen) + if (obj->seen || obj->uninteresting) return 0; obj->seen = 1; @@ -1666,6 +1666,10 @@ int insert_tree(git_packbuilder *pb, git_tree *tree) break; case GIT_OBJ_BLOB: + if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0) + return error; + if (obj->uninteresting) + continue; name = git_tree_entry_name(entry); if ((error = git_packbuilder_insert(pb, entry_id, name)) < 0) return error; diff --git a/src/pack.c b/src/pack.c index 7fd95c905..9ed3ec1af 100644 --- a/src/pack.c +++ b/src/pack.c @@ -716,8 +716,11 @@ int git_packfile_unpack( error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type); git_mwindow_close(&w_curs); - if (error < 0) + if (error < 0) { + /* We have transferred ownership of the data to the cache. */ + obj->data = NULL; break; + } /* the current object becomes the new base, on which we apply the delta */ base = *obj; @@ -934,19 +937,19 @@ git_off_t get_delta_base( if (type == GIT_OBJ_OFS_DELTA) { unsigned used = 0; unsigned char c = base_info[used++]; - base_offset = c & 127; + size_t unsigned_base_offset = c & 127; while (c & 128) { if (left <= used) return GIT_EBUFS; - base_offset += 1; - if (!base_offset || MSB(base_offset, 7)) + unsigned_base_offset += 1; + if (!unsigned_base_offset || MSB(unsigned_base_offset, 7)) return 0; /* overflow */ c = base_info[used++]; - base_offset = (base_offset << 7) + (c & 127); + unsigned_base_offset = (unsigned_base_offset << 7) + (c & 127); } - base_offset = delta_obj_offset - base_offset; - if (base_offset <= 0 || base_offset >= delta_obj_offset) + if (unsigned_base_offset == 0 || (size_t)delta_obj_offset <= unsigned_base_offset) return 0; /* out of bound */ + base_offset = delta_obj_offset - unsigned_base_offset; *curpos += used; } else if (type == GIT_OBJ_REF_DELTA) { /* If we have the cooperative cache, search in it first */ diff --git a/src/patch_parse.c b/src/patch_parse.c index 48afcc1ed..27c01e96f 100644 --- a/src/patch_parse.c +++ b/src/patch_parse.c @@ -53,11 +53,9 @@ static int header_path_len(git_patch_parse_ctx *ctx) return len; } -static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx) +static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx, size_t path_len) { - int path_len, error = 0; - - path_len = header_path_len(ctx); + int error; if ((error = git_buf_put(path, ctx->parse_ctx.line, path_len)) < 0) goto done; @@ -81,7 +79,7 @@ done: static int parse_header_path(char **out, git_patch_parse_ctx *ctx) { git_buf path = GIT_BUF_INIT; - int error = parse_header_path_buf(&path, ctx); + int error = parse_header_path_buf(&path, ctx, header_path_len(ctx)); *out = git_buf_detach(&path); @@ -91,13 +89,33 @@ static int parse_header_path(char **out, git_patch_parse_ctx *ctx) static int parse_header_git_oldpath( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { - return parse_header_path(&patch->old_path, ctx); + git_buf old_path = GIT_BUF_INIT; + int error; + + if ((error = parse_header_path_buf(&old_path, ctx, ctx->parse_ctx.line_len - 1)) < 0) + goto out; + + patch->old_path = git_buf_detach(&old_path); + +out: + git_buf_free(&old_path); + return error; } static int parse_header_git_newpath( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { - return parse_header_path(&patch->new_path, ctx); + git_buf new_path = GIT_BUF_INIT; + int error; + + if ((error = parse_header_path_buf(&new_path, ctx, ctx->parse_ctx.line_len - 1)) < 0) + goto out; + + patch->new_path = git_buf_detach(&new_path); + +out: + git_buf_free(&new_path); + return error; } static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx) @@ -213,7 +231,7 @@ static int parse_header_rename( { git_buf path = GIT_BUF_INIT; - if (parse_header_path_buf(&path, ctx) < 0) + if (parse_header_path_buf(&path, ctx, header_path_len(ctx)) < 0) return -1; /* Note: the `rename from` and `rename to` lines include the literal @@ -303,6 +321,22 @@ static int parse_header_start(git_patch_parsed *patch, git_patch_parse_ctx *ctx) return git_parse_err("corrupt new path in git diff header at line %"PRIuZ, ctx->parse_ctx.line_num); + /* + * We cannot expect to be able to always parse paths correctly at this + * point. Due to the possibility of unquoted names, whitespaces in + * filenames and custom prefixes we have to allow that, though, and just + * proceeed here. We then hope for the "---" and "+++" lines to fix that + * for us. + */ + if (!git_parse_ctx_contains(&ctx->parse_ctx, "\n", 1)) { + git_parse_advance_chars(&ctx->parse_ctx, ctx->parse_ctx.line_len - 1); + + git__free(patch->header_old_path); + patch->header_old_path = NULL; + git__free(patch->header_new_path); + patch->header_new_path = NULL; + } + return 0; } diff --git a/src/push.c b/src/push.c index 41b66df06..85b683e62 100644 --- a/src/push.c +++ b/src/push.c @@ -263,12 +263,11 @@ static int enqueue_tag(git_object **out, git_push *push, git_oid *id) return error; } -static int revwalk(git_vector *commits, git_push *push) +static int queue_objects(git_push *push) { git_remote_head *head; push_spec *spec; git_revwalk *rw; - git_oid oid; unsigned int i; int error = -1; @@ -353,176 +352,10 @@ static int revwalk(git_vector *commits, git_push *push) git_revwalk_hide(rw, &head->oid); } - while ((error = git_revwalk_next(&oid, rw)) == 0) { - git_oid *o = git__malloc(GIT_OID_RAWSZ); - if (!o) { - error = -1; - goto on_error; - } - git_oid_cpy(o, &oid); - if ((error = git_vector_insert(commits, o)) < 0) - goto on_error; - } + error = git_packbuilder_insert_walk(push->pb, rw); on_error: git_revwalk_free(rw); - return error == GIT_ITEROVER ? 0 : error; -} - -static int enqueue_object( - const git_tree_entry *entry, - git_packbuilder *pb) -{ - switch (git_tree_entry_type(entry)) { - case GIT_OBJ_COMMIT: - return 0; - case GIT_OBJ_TREE: - return git_packbuilder_insert_tree(pb, entry->oid); - default: - return git_packbuilder_insert(pb, entry->oid, entry->filename); - } -} - -static int queue_differences( - git_tree *base, - git_tree *delta, - git_packbuilder *pb) -{ - git_tree *b_child = NULL, *d_child = NULL; - size_t b_length = git_tree_entrycount(base); - size_t d_length = git_tree_entrycount(delta); - size_t i = 0, j = 0; - int error; - - while (i < b_length && j < d_length) { - const git_tree_entry *b_entry = git_tree_entry_byindex(base, i); - const git_tree_entry *d_entry = git_tree_entry_byindex(delta, j); - int cmp = 0; - - if (!git_oid__cmp(b_entry->oid, d_entry->oid)) - goto loop; - - cmp = strcmp(b_entry->filename, d_entry->filename); - - /* If the entries are both trees and they have the same name but are - * different, then we'll recurse after adding the right-hand entry */ - if (!cmp && - git_tree_entry__is_tree(b_entry) && - git_tree_entry__is_tree(d_entry)) { - /* Add the right-hand entry */ - if ((error = git_packbuilder_insert(pb, d_entry->oid, - d_entry->filename)) < 0) - goto on_error; - - /* Acquire the subtrees and recurse */ - if ((error = git_tree_lookup(&b_child, - git_tree_owner(base), b_entry->oid)) < 0 || - (error = git_tree_lookup(&d_child, - git_tree_owner(delta), d_entry->oid)) < 0 || - (error = queue_differences(b_child, d_child, pb)) < 0) - goto on_error; - - git_tree_free(b_child); b_child = NULL; - git_tree_free(d_child); d_child = NULL; - } - /* If the object is new or different in the right-hand tree, - * then enumerate it */ - else if (cmp >= 0 && - (error = enqueue_object(d_entry, pb)) < 0) - goto on_error; - - loop: - if (cmp <= 0) i++; - if (cmp >= 0) j++; - } - - /* Drain the right-hand tree of entries */ - for (; j < d_length; j++) - if ((error = enqueue_object(git_tree_entry_byindex(delta, j), pb)) < 0) - goto on_error; - - error = 0; - -on_error: - if (b_child) - git_tree_free(b_child); - - if (d_child) - git_tree_free(d_child); - - return error; -} - -static int queue_objects(git_push *push) -{ - git_vector commits = GIT_VECTOR_INIT; - git_oid *oid; - size_t i; - unsigned j; - int error; - - if ((error = revwalk(&commits, push)) < 0) - goto on_error; - - git_vector_foreach(&commits, i, oid) { - git_commit *parent = NULL, *commit; - git_tree *tree = NULL, *ptree = NULL; - size_t parentcount; - - if ((error = git_commit_lookup(&commit, push->repo, oid)) < 0) - goto on_error; - - /* Insert the commit */ - if ((error = git_packbuilder_insert(push->pb, oid, NULL)) < 0) - goto loop_error; - - parentcount = git_commit_parentcount(commit); - - if (!parentcount) { - if ((error = git_packbuilder_insert_tree(push->pb, - git_commit_tree_id(commit))) < 0) - goto loop_error; - } else { - if ((error = git_tree_lookup(&tree, push->repo, - git_commit_tree_id(commit))) < 0 || - (error = git_packbuilder_insert(push->pb, - git_commit_tree_id(commit), NULL)) < 0) - goto loop_error; - - /* For each parent, add the items which are different */ - for (j = 0; j < parentcount; j++) { - if ((error = git_commit_parent(&parent, commit, j)) < 0 || - (error = git_commit_tree(&ptree, parent)) < 0 || - (error = queue_differences(ptree, tree, push->pb)) < 0) - goto loop_error; - - git_tree_free(ptree); ptree = NULL; - git_commit_free(parent); parent = NULL; - } - } - - error = 0; - - loop_error: - if (tree) - git_tree_free(tree); - - if (ptree) - git_tree_free(ptree); - - if (parent) - git_commit_free(parent); - - git_commit_free(commit); - - if (error < 0) - goto on_error; - } - - error = 0; - -on_error: - git_vector_free_deep(&commits); return error; } diff --git a/src/refdb_fs.c b/src/refdb_fs.c index ade734c6a..140879d23 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -2035,6 +2035,7 @@ int git_refdb_backend_fs( if ((!git_repository__cvar(&t, backend->repo, GIT_CVAR_FSYNCOBJECTFILES) && t) || git_repository__fsync_gitdir) backend->fsync = 1; + backend->iterator_flags |= GIT_ITERATOR_DESCEND_SYMLINKS; backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; diff --git a/src/remote.c b/src/remote.c index 303911760..4d675af82 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1541,6 +1541,20 @@ cleanup: return error; } +static int truncate_fetch_head(const char *gitdir) +{ + git_buf path = GIT_BUF_INIT; + int error; + + if ((error = git_buf_joinpath(&path, gitdir, GIT_FETCH_HEAD_FILE)) < 0) + return error; + + error = git_futils_truncate(path.ptr, GIT_REFS_FILE_MODE); + git_buf_free(&path); + + return error; +} + int git_remote_update_tips( git_remote *remote, const git_remote_callbacks *callbacks, @@ -1571,6 +1585,9 @@ int git_remote_update_tips( else tagopt = download_tags; + if ((error = truncate_fetch_head(git_repository_path(remote->repo))) < 0) + goto out; + if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, &tagspec, &refs, reflog_message)) < 0) goto out; diff --git a/src/streams/openssl.c b/src/streams/openssl.c index 2b246002f..9d566074c 100644 --- a/src/streams/openssl.c +++ b/src/streams/openssl.c @@ -332,7 +332,7 @@ static int check_host_name(const char *name, const char *host) static int verify_server_cert(SSL *ssl, const char *host) { - X509 *cert; + X509 *cert = NULL; X509_NAME *peer_name; ASN1_STRING *str; unsigned char *peer_cn = NULL; @@ -341,7 +341,7 @@ static int verify_server_cert(SSL *ssl, const char *host) struct in6_addr addr6; struct in_addr addr4; void *addr; - int i = -1,j; + int i = -1, j, error = 0; if (SSL_get_verify_result(ssl) != X509_V_OK) { giterr_set(GITERR_SSL, "the SSL certificate is invalid"); @@ -362,8 +362,9 @@ static int verify_server_cert(SSL *ssl, const char *host) cert = SSL_get_peer_certificate(ssl); if (!cert) { + error = -1; giterr_set(GITERR_SSL, "the server did not provide a certificate"); - return -1; + goto cleanup; } /* Check the alternative names */ @@ -401,8 +402,9 @@ static int verify_server_cert(SSL *ssl, const char *host) if (matched == 0) goto cert_fail_name; - if (matched == 1) - return 0; + if (matched == 1) { + goto cleanup; + } /* If no alternative names are available, check the common name */ peer_name = X509_get_subject_name(cert); @@ -444,18 +446,21 @@ static int verify_server_cert(SSL *ssl, const char *host) if (check_host_name((char *)peer_cn, host) < 0) goto cert_fail_name; - OPENSSL_free(peer_cn); + goto cleanup; - return 0; +cert_fail_name: + error = GIT_ECERTIFICATE; + giterr_set(GITERR_SSL, "hostname does not match certificate"); + goto cleanup; on_error: - OPENSSL_free(peer_cn); - return ssl_set_error(ssl, 0); + error = ssl_set_error(ssl, 0); + goto cleanup; -cert_fail_name: +cleanup: + X509_free(cert); OPENSSL_free(peer_cn); - giterr_set(GITERR_SSL, "hostname does not match certificate"); - return GIT_ECERTIFICATE; + return error; } typedef struct { diff --git a/src/streams/stransport.c b/src/streams/stransport.c index 64a5dd76f..cca17bb94 100644 --- a/src/streams/stransport.c +++ b/src/streams/stransport.c @@ -83,8 +83,10 @@ static int stransport_connect(git_stream *stream) } if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure || - sec_res == kSecTrustResultFatalTrustFailure) + sec_res == kSecTrustResultFatalTrustFailure) { + giterr_set(GITERR_SSL, "untrusted connection error"); return GIT_ECERTIFICATE; + } return 0; diff --git a/src/transports/local.c b/src/transports/local.c index 733ed2c20..ae117db29 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -507,6 +507,21 @@ static int local_counting(int stage, unsigned int current, unsigned int total, v return error; } +static int foreach_reference_cb(git_reference *reference, void *payload) +{ + git_revwalk *walk = (git_revwalk *)payload; + + int error = git_revwalk_hide(walk, git_reference_target(reference)); + /* The reference is in the local repository, so the target may not + * exist on the remote. It also may not be a commit. */ + if (error == GIT_ENOTFOUND || error == GITERR_INVALID) { + giterr_clear(); + error = 0; + } + + return error; +} + static int local_download_pack( git_transport *transport, git_repository *repo, @@ -546,11 +561,6 @@ static int local_download_pack( if (git_object_type(obj) == GIT_OBJ_COMMIT) { /* Revwalker includes only wanted commits */ error = git_revwalk_push(walk, &rhead->oid); - if (!error && !git_oid_iszero(&rhead->loid)) { - error = git_revwalk_hide(walk, &rhead->loid); - if (error == GIT_ENOTFOUND) - error = 0; - } } else { /* Tag or some other wanted object. Add it on its own */ error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name); @@ -560,6 +570,9 @@ static int local_download_pack( goto cleanup; } + if ((error = git_reference_foreach(repo, foreach_reference_cb, walk))) + goto cleanup; + if ((error = git_packbuilder_insert_walk(pack, walk))) goto cleanup; diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 98905ab61..6dad1d38a 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -172,9 +172,15 @@ static int apply_default_credentials(HINTERNET request, int mechanisms) * is "medium" which applies to the intranet and sounds like it would correspond * to Internet Explorer security zones, but in fact does not. */ DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW; + DWORD native_scheme = 0; - if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) == 0 && - (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) == 0) { + if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) != 0) + native_scheme |= WINHTTP_AUTH_SCHEME_NTLM; + + if ((mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) != 0) + native_scheme |= WINHTTP_AUTH_SCHEME_NEGOTIATE; + + if (!native_scheme) { giterr_set(GITERR_NET, "invalid authentication scheme"); return -1; } @@ -182,6 +188,9 @@ static int apply_default_credentials(HINTERNET request, int mechanisms) if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD))) return -1; + if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_SERVER, native_scheme, NULL, NULL, NULL)) + return -1; + return 0; } @@ -606,12 +615,12 @@ static int parse_unauthorized_response( if (WINHTTP_AUTH_SCHEME_NTLM & supported) { *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; *allowed_types |= GIT_CREDTYPE_DEFAULT; - *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE; + *allowed_mechanisms |= GIT_WINHTTP_AUTH_NTLM; } if (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported) { *allowed_types |= GIT_CREDTYPE_DEFAULT; - *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE; + *allowed_mechanisms |= GIT_WINHTTP_AUTH_NEGOTIATE; } if (WINHTTP_AUTH_SCHEME_BASIC & supported) { diff --git a/src/util.c b/src/util.c index 6ae5cdaec..1760a315e 100644 --- a/src/util.c +++ b/src/util.c @@ -252,35 +252,47 @@ void git__strtolower(char *str) git__strntolower(str, strlen(str)); } -int git__prefixcmp(const char *str, const char *prefix) +GIT_INLINE(int) prefixcmp(const char *str, size_t str_n, const char *prefix, bool icase) { - for (;;) { - unsigned char p = *(prefix++), s; + int s, p; + + while (str_n--) { + s = (unsigned char)*str++; + p = (unsigned char)*prefix++; + + if (icase) { + s = git__tolower(s); + p = git__tolower(p); + } + if (!p) return 0; - if ((s = *(str++)) != p) + + if (s != p) return s - p; } + + return (0 - *prefix); } -int git__prefixcmp_icase(const char *str, const char *prefix) +int git__prefixcmp(const char *str, const char *prefix) { - return strncasecmp(str, prefix, strlen(prefix)); + return prefixcmp(str, SIZE_MAX, prefix, false); } -int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix) +int git__prefixncmp(const char *str, size_t str_n, const char *prefix) { - int s, p; - - while(str_n--) { - s = (unsigned char)git__tolower(*str++); - p = (unsigned char)git__tolower(*prefix++); + return prefixcmp(str, str_n, prefix, false); +} - if (s != p) - return s - p; - } +int git__prefixcmp_icase(const char *str, const char *prefix) +{ + return prefixcmp(str, SIZE_MAX, prefix, true); +} - return (0 - *prefix); +int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix) +{ + return prefixcmp(str, str_n, prefix, true); } int git__suffixcmp(const char *str, const char *suffix) diff --git a/src/util.h b/src/util.h index 7c9a54ff1..80ee8e647 100644 --- a/src/util.h +++ b/src/util.h @@ -180,6 +180,7 @@ GIT_INLINE(void) git__free(void *ptr) extern int git__prefixcmp(const char *str, const char *prefix); extern int git__prefixcmp_icase(const char *str, const char *prefix); +extern int git__prefixncmp(const char *str, size_t str_n, const char *prefix); extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix); extern int git__suffixcmp(const char *str, const char *suffix); diff --git a/src/zstream.c b/src/zstream.c index 4895bdb16..963c9a344 100644 --- a/src/zstream.c +++ b/src/zstream.c @@ -14,17 +14,22 @@ #define ZSTREAM_BUFFER_SIZE (1024 * 1024) #define ZSTREAM_BUFFER_MIN_EXTRA 8 -static int zstream_seterr(git_zstream *zs) +GIT_INLINE(int) zstream_seterr(git_zstream *zs) { - if (zs->zerr == Z_OK || zs->zerr == Z_STREAM_END) + switch (zs->zerr) { + case Z_OK: + case Z_STREAM_END: + case Z_BUF_ERROR: /* not fatal; we retry with a larger buffer */ return 0; - - if (zs->zerr == Z_MEM_ERROR) + case Z_MEM_ERROR: giterr_set_oom(); - else if (zs->z.msg) - giterr_set_str(GITERR_ZLIB, zs->z.msg); - else - giterr_set(GITERR_ZLIB, "unknown compression error"); + break; + default: + if (zs->z.msg) + giterr_set_str(GITERR_ZLIB, zs->z.msg); + else + giterr_set(GITERR_ZLIB, "unknown compression error"); + } return -1; } @@ -98,8 +103,9 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream) /* set up in data */ zstream->z.next_in = (Bytef *)zstream->in; zstream->z.avail_in = (uInt)zstream->in_len; + if ((size_t)zstream->z.avail_in != zstream->in_len) { - zstream->z.avail_in = INT_MAX; + zstream->z.avail_in = UINT_MAX; zflush = Z_NO_FLUSH; } else { zflush = Z_FINISH; @@ -110,7 +116,7 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream) zstream->z.next_out = out; zstream->z.avail_out = (uInt)out_remain; if ((size_t)zstream->z.avail_out != out_remain) - zstream->z.avail_out = INT_MAX; + zstream->z.avail_out = UINT_MAX; out_queued = (size_t)zstream->z.avail_out; /* compress next chunk */ @@ -119,8 +125,8 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream) else zstream->zerr = deflate(&zstream->z, zflush); - if (zstream->zerr == Z_STREAM_ERROR) - return zstream_seterr(zstream); + if (zstream_seterr(zstream)) + return -1; out_used = (out_queued - zstream->z.avail_out); out_remain -= out_used; diff --git a/tests/core/string.c b/tests/core/string.c index 90e8fa027..85db0c662 100644 --- a/tests/core/string.c +++ b/tests/core/string.c @@ -40,6 +40,48 @@ void test_core_string__2(void) cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0); } +/* compare prefixes with len */ +void test_core_string__prefixncmp(void) +{ + cl_assert(git__prefixncmp("", 0, "") == 0); + cl_assert(git__prefixncmp("a", 1, "") == 0); + cl_assert(git__prefixncmp("", 0, "a") < 0); + cl_assert(git__prefixncmp("a", 1, "b") < 0); + cl_assert(git__prefixncmp("b", 1, "a") > 0); + cl_assert(git__prefixncmp("ab", 2, "a") == 0); + cl_assert(git__prefixncmp("ab", 1, "a") == 0); + cl_assert(git__prefixncmp("ab", 2, "ac") < 0); + cl_assert(git__prefixncmp("a", 1, "ac") < 0); + cl_assert(git__prefixncmp("ab", 1, "ac") < 0); + cl_assert(git__prefixncmp("ab", 2, "aa") > 0); + cl_assert(git__prefixncmp("ab", 1, "aa") < 0); +} + +/* compare prefixes with len */ +void test_core_string__prefixncmp_icase(void) +{ + cl_assert(git__prefixncmp_icase("", 0, "") == 0); + cl_assert(git__prefixncmp_icase("a", 1, "") == 0); + cl_assert(git__prefixncmp_icase("", 0, "a") < 0); + cl_assert(git__prefixncmp_icase("a", 1, "b") < 0); + cl_assert(git__prefixncmp_icase("A", 1, "b") < 0); + cl_assert(git__prefixncmp_icase("a", 1, "B") < 0); + cl_assert(git__prefixncmp_icase("b", 1, "a") > 0); + cl_assert(git__prefixncmp_icase("B", 1, "a") > 0); + cl_assert(git__prefixncmp_icase("b", 1, "A") > 0); + cl_assert(git__prefixncmp_icase("ab", 2, "a") == 0); + cl_assert(git__prefixncmp_icase("Ab", 2, "a") == 0); + cl_assert(git__prefixncmp_icase("ab", 2, "A") == 0); + cl_assert(git__prefixncmp_icase("ab", 1, "a") == 0); + cl_assert(git__prefixncmp_icase("ab", 2, "ac") < 0); + cl_assert(git__prefixncmp_icase("Ab", 2, "ac") < 0); + cl_assert(git__prefixncmp_icase("ab", 2, "Ac") < 0); + cl_assert(git__prefixncmp_icase("a", 1, "ac") < 0); + cl_assert(git__prefixncmp_icase("ab", 1, "ac") < 0); + cl_assert(git__prefixncmp_icase("ab", 2, "aa") > 0); + cl_assert(git__prefixncmp_icase("ab", 1, "aa") < 0); +} + void test_core_string__strcmp(void) { cl_assert(git__strcmp("", "") == 0); diff --git a/tests/diff/blob.c b/tests/diff/blob.c index c3933c313..05cc28218 100644 --- a/tests/diff/blob.c +++ b/tests/diff/blob.c @@ -1,6 +1,19 @@ #include "clar_libgit2.h" #include "diff_helpers.h" +#define BLOB_DIFF \ + "diff --git a/file b/file\n" \ + "index 45141a7..4d713dc 100644\n" \ + "--- a/file\n" \ + "+++ b/file\n" \ + "@@ -1 +1,6 @@\n" \ + " Hello from the root\n" \ + "+\n" \ + "+Some additional lines\n" \ + "+\n" \ + "+Down here below\n" \ + "+\n" + static git_repository *g_repo = NULL; static diff_expects expected; static git_diff_options opts; @@ -65,6 +78,32 @@ static void assert_one_modified( cl_assert_equal_i(dels, exp->line_dels); } +void test_diff_blob__patch_with_freed_blobs(void) +{ + git_oid a_oid, b_oid; + git_blob *a, *b; + git_patch *p; + git_buf buf = GIT_BUF_INIT; + + /* tests/resources/attr/root_test1 */ + cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8)); + cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4)); + /* tests/resources/attr/root_test2 */ + cl_git_pass(git_oid_fromstrn(&b_oid, "4d713dc4", 8)); + cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 4)); + + cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, NULL)); + + git_blob_free(a); + git_blob_free(b); + + cl_git_pass(git_patch_to_buf(&buf, p)); + cl_assert_equal_s(buf.ptr, BLOB_DIFF); + + git_patch_free(p); + git_buf_free(&buf); +} + void test_diff_blob__can_compare_text_blobs(void) { git_blob *a, *b, *c; diff --git a/tests/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c index ea4b70e4a..4dabb577e 100644 --- a/tests/fetchhead/nonetwork.c +++ b/tests/fetchhead/nonetwork.c @@ -353,20 +353,25 @@ void test_fetchhead_nonetwork__quote_in_branch_name(void) } static bool found_master; -static bool find_master_called; +static bool found_haacked; +static bool find_master_haacked_called; -int find_master(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload) +int find_master_haacked(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload) { GIT_UNUSED(remote_url); GIT_UNUSED(oid); GIT_UNUSED(payload); - find_master_called = true; + find_master_haacked_called = true; if (!strcmp("refs/heads/master", ref_name)) { cl_assert(is_merge); found_master = true; } + if (!strcmp("refs/heads/haacked", ref_name)) { + cl_assert(is_merge); + found_haacked = true; + } return 0; } @@ -375,10 +380,12 @@ void test_fetchhead_nonetwork__create_when_refpecs_given(void) { git_remote *remote; git_buf path = GIT_BUF_INIT; - char *refspec = "refs/heads/master"; + char *refspec1 = "refs/heads/master"; + char *refspec2 = "refs/heads/haacked"; + char *refspecs[] = { refspec1, refspec2 }; git_strarray specs = { - &refspec, - 1, + refspecs, + 2, }; cl_set_cleanup(&cleanup_repository, "./test1"); @@ -391,9 +398,74 @@ void test_fetchhead_nonetwork__create_when_refpecs_given(void) cl_git_pass(git_remote_fetch(remote, &specs, NULL, NULL)); cl_assert(git_path_exists(path.ptr)); - cl_git_pass(git_repository_fetchhead_foreach(g_repo, find_master, NULL)); - cl_assert(find_master_called); + cl_git_pass(git_repository_fetchhead_foreach(g_repo, find_master_haacked, NULL)); + cl_assert(find_master_haacked_called); cl_assert(found_master); + cl_assert(found_haacked); + + git_remote_free(remote); + git_buf_free(&path); +} + +static bool count_refs_called; +struct prefix_count { + const char *prefix; + int count; + int expected; +}; + +int count_refs(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload) +{ + int i; + struct prefix_count *prefix_counts = (struct prefix_count *) payload; + + GIT_UNUSED(remote_url); + GIT_UNUSED(oid); + GIT_UNUSED(is_merge); + + count_refs_called = true; + + for (i = 0; prefix_counts[i].prefix; i++) { + if (!git__prefixcmp(ref_name, prefix_counts[i].prefix)) + prefix_counts[i].count++; + } + + return 0; +} + +void test_fetchhead_nonetwork__create_with_multiple_refspecs(void) +{ + git_remote *remote; + git_buf path = GIT_BUF_INIT; + + cl_set_cleanup(&cleanup_repository, "./test1"); + cl_git_pass(git_repository_init(&g_repo, "./test1", 0)); + + cl_git_pass(git_remote_create(&remote, g_repo, "origin", cl_fixture("testrepo.git"))); + git_remote_free(remote); + cl_git_pass(git_remote_add_fetch(g_repo, "origin", "+refs/notes/*:refs/origin/notes/*")); + /* Pick up the new refspec */ + cl_git_pass(git_remote_lookup(&remote, g_repo, "origin")); + + cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), "FETCH_HEAD")); + cl_assert(!git_path_exists(path.ptr)); + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); + cl_assert(git_path_exists(path.ptr)); + + { + int i; + struct prefix_count prefix_counts[] = { + {"refs/notes/", 0, 1}, + {"refs/heads/", 0, 12}, + {"refs/tags/", 0, 7}, + {NULL, 0, 0}, + }; + + cl_git_pass(git_repository_fetchhead_foreach(g_repo, count_refs, &prefix_counts)); + cl_assert(count_refs_called); + for (i = 0; prefix_counts[i].prefix; i++) + cl_assert_equal_i(prefix_counts[i].expected, prefix_counts[i].count); + } git_remote_free(remote); git_buf_free(&path); diff --git a/tests/merge/workdir/submodules.c b/tests/merge/workdir/submodules.c index 7c18c2ffb..c4cc188a8 100644 --- a/tests/merge/workdir/submodules.c +++ b/tests/merge/workdir/submodules.c @@ -12,6 +12,7 @@ static git_repository *repo; #define SUBMODULE_MAIN_BRANCH "submodules" #define SUBMODULE_OTHER_BRANCH "submodules-branch" #define SUBMODULE_OTHER2_BRANCH "submodules-branch2" +#define SUBMODULE_DELETE_BRANCH "delete-submodule" #define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" @@ -93,3 +94,38 @@ void test_merge_workdir_submodules__take_changed(void) git_reference_free(their_ref); git_reference_free(our_ref); } + + +void test_merge_workdir_submodules__update_delete_conflict(void) +{ + git_reference *our_ref, *their_ref; + git_commit *our_commit; + git_annotated_commit *their_head; + git_index *index; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", 0, ".gitmodules" }, + { 0100644, "5887a5e516c53bd58efb0f02ec6aa031b6fe9ad7", 0, "file1.txt" }, + { 0100644, "4218670ab81cc219a9f94befb5c5dad90ec52648", 0, "file2.txt" }, + { 0160000, "d3d806a4bef96889117fd7ebac0e3cb5ec152932", 1, "submodule"}, + { 0160000, "297aa6cd028b3336c7802c7a6f49143da4e1602d", 3, "submodule" }, + }; + + cl_git_pass(git_reference_lookup(&our_ref, repo, "refs/heads/" SUBMODULE_DELETE_BRANCH)); + cl_git_pass(git_commit_lookup(&our_commit, repo, git_reference_target(our_ref))); + cl_git_pass(git_reset(repo, (git_object *)our_commit, GIT_RESET_HARD, NULL)); + + cl_git_pass(git_reference_lookup(&their_ref, repo, "refs/heads/" SUBMODULE_MAIN_BRANCH)); + cl_git_pass(git_annotated_commit_from_ref(&their_head, repo, their_ref)); + + cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_head, 1, NULL, NULL)); + + cl_git_pass(git_repository_index(&index, repo)); + cl_assert(merge_test_index(index, merge_index_entries, 5)); + + git_index_free(index); + git_annotated_commit_free(their_head); + git_commit_free(our_commit); + git_reference_free(their_ref); + git_reference_free(our_ref); +} diff --git a/tests/notes/notes.c b/tests/notes/notes.c index a91bf5bdf..9626f53b3 100644 --- a/tests/notes/notes.c +++ b/tests/notes/notes.c @@ -73,6 +73,128 @@ static int note_list_cb( return 0; } +struct note_create_payload { + const char *note_oid; + const char *object_oid; + unsigned seen; +}; + +static int note_list_create_cb( + const git_oid *blob_oid, const git_oid *annotated_obj_id, void *payload) +{ + git_oid expected_note_oid, expected_target_oid; + struct note_create_payload *notes = payload; + size_t i; + + for (i = 0; notes[i].note_oid != NULL; i++) { + cl_git_pass(git_oid_fromstr(&expected_note_oid, notes[i].note_oid)); + + if (git_oid_cmp(&expected_note_oid, blob_oid) != 0) + continue; + + cl_git_pass(git_oid_fromstr(&expected_target_oid, notes[i].object_oid)); + + if (git_oid_cmp(&expected_target_oid, annotated_obj_id) != 0) + continue; + + notes[i].seen = 1; + return 0; + } + + cl_fail("Did not see expected note"); + return 0; +} + +void assert_notes_seen(struct note_create_payload payload[], size_t n) +{ + size_t seen = 0, i; + + for (i = 0; payload[i].note_oid != NULL; i++) { + if (payload[i].seen) + seen++; + } + + cl_assert_equal_i(seen, n); +} + +void test_notes_notes__can_create_a_note(void) +{ + git_oid note_oid; + static struct note_create_payload can_create_a_note[] = { + { "1c9b1bc36730582a42d56eeee0dc58673d7ae869", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", 0 }, + { NULL, NULL, 0 } + }; + + create_note(¬e_oid, "refs/notes/i-can-see-dead-notes", can_create_a_note[0].object_oid, "I decorate 4a20\n"); + + cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_create_cb, &can_create_a_note)); + + assert_notes_seen(can_create_a_note, 1); +} + +void test_notes_notes__can_create_a_note_from_commit(void) +{ + git_oid oid; + git_oid notes_commit_out; + git_reference *ref; + static struct note_create_payload can_create_a_note_from_commit[] = { + { "1c9b1bc36730582a42d56eeee0dc58673d7ae869", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", 0 }, + { NULL, NULL, 0 } + }; + + cl_git_pass(git_oid_fromstr(&oid, can_create_a_note_from_commit[0].object_oid)); + + cl_git_pass(git_note_commit_create(¬es_commit_out, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 1)); + + /* create_from_commit will not update any ref, + * so we must manually create the ref, that points to the commit */ + cl_git_pass(git_reference_create(&ref, _repo, "refs/notes/i-can-see-dead-notes", ¬es_commit_out, 0, NULL)); + + cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_create_cb, &can_create_a_note_from_commit)); + + assert_notes_seen(can_create_a_note_from_commit, 1); + + git_reference_free(ref); +} + + +/* Test that we can create a note from a commit, given an existing commit */ +void test_notes_notes__can_create_a_note_from_commit_given_an_existing_commit(void) +{ + git_oid oid; + git_oid notes_commit_out; + git_commit *existing_notes_commit = NULL; + git_reference *ref; + static struct note_create_payload can_create_a_note_from_commit_given_an_existing_commit[] = { + { "1c9b1bc36730582a42d56eeee0dc58673d7ae869", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", 0 }, + { "1aaf94147c21f981e0a20bf57b89137c5a6aae52", "9fd738e8f7967c078dceed8190330fc8648ee56a", 0 }, + { NULL, NULL, 0 } + }; + + cl_git_pass(git_oid_fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045")); + + cl_git_pass(git_note_commit_create(¬es_commit_out, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 0)); + + cl_git_pass(git_oid_fromstr(&oid, "9fd738e8f7967c078dceed8190330fc8648ee56a")); + + git_commit_lookup(&existing_notes_commit, _repo, ¬es_commit_out); + + cl_assert(existing_notes_commit); + + cl_git_pass(git_note_commit_create(¬es_commit_out, NULL, _repo, existing_notes_commit, _sig, _sig, &oid, "I decorate 9fd7\n", 0)); + + /* create_from_commit will not update any ref, + * so we must manually create the ref, that points to the commit */ + cl_git_pass(git_reference_create(&ref, _repo, "refs/notes/i-can-see-dead-notes", ¬es_commit_out, 0, NULL)); + + cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_create_cb, &can_create_a_note_from_commit_given_an_existing_commit)); + + assert_notes_seen(can_create_a_note_from_commit_given_an_existing_commit, 2); + + git_commit_free(existing_notes_commit); + git_reference_free(ref); +} + /* * $ git notes --ref i-can-see-dead-notes add -m "I decorate a65f" a65fedf39aefe402d3bb6e24df4d4f5fe4547750 * $ git notes --ref i-can-see-dead-notes add -m "I decorate c478" c47800c7266a2be04c571c04d5a6614691ea99bd @@ -253,6 +375,71 @@ static char *messages[] = { #define MESSAGES_COUNT (sizeof(messages)/sizeof(messages[0])) - 1 +/* Test that we can read a note */ +void test_notes_notes__can_read_a_note(void) +{ + git_oid note_oid, target_oid; + git_note *note; + + create_note(¬e_oid, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 4a20\n"); + + cl_git_pass(git_oid_fromstr(&target_oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045")); + + cl_git_pass(git_note_read(¬e, _repo, "refs/notes/i-can-see-dead-notes", &target_oid)); + + cl_assert_equal_s(git_note_message(note), "I decorate 4a20\n"); + + git_note_free(note); +} + +/* Test that we can read a note with from commit api */ +void test_notes_notes__can_read_a_note_from_a_commit(void) +{ + git_oid oid, notes_commit_oid; + git_commit *notes_commit; + git_note *note; + + cl_git_pass(git_oid_fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045")); + + cl_git_pass(git_note_commit_create(¬es_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 1)); + + git_commit_lookup(¬es_commit, _repo, ¬es_commit_oid); + + cl_assert(notes_commit); + + cl_git_pass(git_note_commit_read(¬e, _repo, notes_commit, &oid)); + + cl_assert_equal_s(git_note_message(note), "I decorate 4a20\n"); + + git_commit_free(notes_commit); + git_note_free(note); +} + +/* Test that we can read a commit with no note fails */ +void test_notes_notes__attempt_to_read_a_note_from_a_commit_with_no_note_fails(void) +{ + git_oid oid, notes_commit_oid; + git_commit *notes_commit; + git_note *note; + + cl_git_pass(git_oid_fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045")); + + cl_git_pass(git_note_commit_create(¬es_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 1)); + + git_commit_lookup(¬es_commit, _repo, ¬es_commit_oid); + + cl_git_pass(git_note_commit_remove(¬es_commit_oid, _repo, notes_commit, _sig, _sig, &oid)); + git_commit_free(notes_commit); + + git_commit_lookup(¬es_commit, _repo, ¬es_commit_oid); + + cl_assert(notes_commit); + + cl_git_fail_with(GIT_ENOTFOUND, git_note_commit_read(¬e, _repo, notes_commit, &oid)); + + git_commit_free(notes_commit); +} + /* * $ git ls-tree refs/notes/fanout * 040000 tree 4b22b35d44b5a4f589edf3dc89196399771796ea 84 @@ -298,6 +485,50 @@ void test_notes_notes__can_read_a_note_in_an_existing_fanout(void) git_note_free(note); } +/* Can remove a note */ +void test_notes_notes__can_remove_a_note(void) +{ + git_oid note_oid, target_oid; + git_note *note; + + create_note(¬e_oid, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 4a20\n"); + + cl_git_pass(git_oid_fromstr(&target_oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045")); + cl_git_pass(git_note_remove(_repo, "refs/notes/i-can-see-dead-notes", _sig, _sig, &target_oid)); + + cl_git_fail(git_note_read(¬e, _repo, "refs/notes/i-can-see-dead-notes", &target_oid)); +} + +/* Can remove a note from a commit */ +void test_notes_notes__can_remove_a_note_from_commit(void) +{ + git_oid oid, notes_commit_oid; + git_note *note = NULL; + git_commit *existing_notes_commit; + git_reference *ref; + + cl_git_pass(git_oid_fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045")); + + cl_git_pass(git_note_commit_create(¬es_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 0)); + + git_commit_lookup(&existing_notes_commit, _repo, ¬es_commit_oid); + + cl_assert(existing_notes_commit); + + cl_git_pass(git_note_commit_remove(¬es_commit_oid, _repo, existing_notes_commit, _sig, _sig, &oid)); + + /* remove_from_commit will not update any ref, + * so we must manually create the ref, that points to the commit */ + cl_git_pass(git_reference_create(&ref, _repo, "refs/notes/i-can-see-dead-notes", ¬es_commit_oid, 0, NULL)); + + cl_git_fail(git_note_read(¬e, _repo, "refs/notes/i-can-see-dead-notes", &oid)); + + git_commit_free(existing_notes_commit); + git_reference_free(ref); + git_note_free(note); +} + + void test_notes_notes__can_remove_a_note_in_an_existing_fanout(void) { git_oid target_oid; @@ -388,3 +619,46 @@ void test_notes_notes__empty_iterate(void) cl_git_fail(git_note_iterator_new(&iter, _repo, "refs/notes/commits")); } + +void test_notes_notes__iterate_from_commit(void) +{ + git_note_iterator *iter; + git_note *note; + git_oid note_id, annotated_id; + git_oid oids[2]; + git_oid notes_commit_oids[2]; + git_commit *notes_commits[2]; + const char* note_message[] = { + "I decorate a65f\n", + "I decorate c478\n" + }; + int i, err; + + cl_git_pass(git_oid_fromstr(&(oids[0]), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750")); + cl_git_pass(git_oid_fromstr(&(oids[1]), "c47800c7266a2be04c571c04d5a6614691ea99bd")); + + cl_git_pass(git_note_commit_create(¬es_commit_oids[0], NULL, _repo, NULL, _sig, _sig, &(oids[0]), note_message[0], 0)); + + git_commit_lookup(¬es_commits[0], _repo, ¬es_commit_oids[0]); + cl_assert(notes_commits[0]); + + cl_git_pass(git_note_commit_create(¬es_commit_oids[1], NULL, _repo, notes_commits[0], _sig, _sig, &(oids[1]), note_message[1], 0)); + + git_commit_lookup(¬es_commits[1], _repo, ¬es_commit_oids[1]); + cl_assert(notes_commits[1]); + + cl_git_pass(git_note_commit_iterator_new(&iter, notes_commits[1])); + + for (i = 0; (err = git_note_next(¬e_id, &annotated_id, iter)) >= 0; ++i) { + cl_git_pass(git_note_commit_read(¬e, _repo, notes_commits[1], &annotated_id)); + cl_assert_equal_s(git_note_message(note), note_message[i]); + git_note_free(note); + } + + cl_assert_equal_i(GIT_ITEROVER, err); + cl_assert_equal_i(2, i); + + git_note_iterator_free(iter); + git_commit_free(notes_commits[0]); + git_commit_free(notes_commits[1]); +} diff --git a/tests/odb/largefiles.c b/tests/odb/largefiles.c new file mode 100644 index 000000000..22f136df5 --- /dev/null +++ b/tests/odb/largefiles.c @@ -0,0 +1,114 @@ +#include "clar_libgit2.h" +#include "git2/odb_backend.h" + +static git_repository *repo; +static git_odb *odb; + +void test_odb_largefiles__initialize(void) +{ + repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_odb(&odb, repo)); +} + +void test_odb_largefiles__cleanup(void) +{ + git_odb_free(odb); + cl_git_sandbox_cleanup(); +} + +static void writefile(git_oid *oid) +{ + static git_odb_stream *stream; + git_buf buf = GIT_BUF_INIT; + size_t i; + + for (i = 0; i < 3041; i++) + cl_git_pass(git_buf_puts(&buf, "Hello, world.\n")); + + cl_git_pass(git_odb_open_wstream(&stream, odb, 5368709122, GIT_OBJ_BLOB)); + for (i = 0; i < 126103; i++) + cl_git_pass(git_odb_stream_write(stream, buf.ptr, buf.size)); + + cl_git_pass(git_odb_stream_finalize_write(oid, stream)); + + git_odb_stream_free(stream); + git_buf_free(&buf); +} + +void test_odb_largefiles__write_from_memory(void) +{ + git_oid expected, oid; + git_buf buf = GIT_BUF_INIT; + size_t i; + +#ifndef GIT_ARCH_64 + cl_skip(); +#endif + + if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") || + !cl_is_env_set("GITTEST_INVASIVE_MEMORY") || + !cl_is_env_set("GITTEST_SLOW")) + cl_skip(); + + for (i = 0; i < (3041*126103); i++) + cl_git_pass(git_buf_puts(&buf, "Hello, world.\n")); + + git_oid_fromstr(&expected, "3fb56989cca483b21ba7cb0a6edb229d10e1c26c"); + cl_git_pass(git_odb_write(&oid, odb, buf.ptr, buf.size, GIT_OBJ_BLOB)); + + cl_assert_equal_oid(&expected, &oid); +} + +void test_odb_largefiles__streamwrite(void) +{ + git_oid expected, oid; + + if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") || + !cl_is_env_set("GITTEST_SLOW")) + cl_skip(); + + git_oid_fromstr(&expected, "3fb56989cca483b21ba7cb0a6edb229d10e1c26c"); + writefile(&oid); + + cl_assert_equal_oid(&expected, &oid); +} + +void test_odb_largefiles__read_into_memory(void) +{ + git_oid oid; + git_odb_object *obj; + +#ifndef GIT_ARCH_64 + cl_skip(); +#endif + + if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") || + !cl_is_env_set("GITTEST_INVASIVE_MEMORY") || + !cl_is_env_set("GITTEST_SLOW")) + cl_skip(); + + writefile(&oid); + cl_git_pass(git_odb_read(&obj, odb, &oid)); + + git_odb_object_free(obj); +} + +void test_odb_largefiles__read_into_memory_rejected_on_32bit(void) +{ + git_oid oid; + git_odb_object *obj = NULL; + +#ifdef GIT_ARCH_64 + cl_skip(); +#endif + + if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") || + !cl_is_env_set("GITTEST_INVASIVE_MEMORY") || + !cl_is_env_set("GITTEST_SLOW")) + cl_skip(); + + writefile(&oid); + cl_git_fail(git_odb_read(&obj, odb, &oid)); + + git_odb_object_free(obj); +} diff --git a/tests/pack/indexer.c b/tests/pack/indexer.c index a28ee3e07..f3c2204bd 100644 --- a/tests/pack/indexer.c +++ b/tests/pack/indexer.c @@ -41,6 +41,29 @@ static const unsigned char thin_pack[] = { static const unsigned int thin_pack_len = 78; /* + * Packfile with one object. It references an object which is not in the + * packfile and has a corrupt length (states the deltified stream is 1 byte + * long, where it is actually 6). + */ +static const unsigned char corrupt_thin_pack[] = { + 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x71, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, + 0x10, 0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63, + 0x62, 0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x07, + 0x67, 0x03, 0xc5, 0x40, 0x99, 0x49, 0xb1, 0x3b, 0x7d, 0xae, 0x9b, 0x0e, + 0xdd, 0xde, 0xc6, 0x76, 0x43, 0x24, 0x64 +}; +static const unsigned int corrupt_thin_pack_len = 67; + +/* + * Packfile with a missing trailer. + */ +static const unsigned char missing_trailer_pack[] = { + 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x50, 0xf4, 0x3b, +}; +static const unsigned int missing_trailer_pack_len = 12; + +/* * Packfile that causes the packfile stream to open in a way in which it leaks * the stream reader. */ @@ -71,6 +94,22 @@ void test_pack_indexer__out_of_order(void) git_indexer_free(idx); } +void test_pack_indexer__missing_trailer(void) +{ + git_indexer *idx = 0; + git_transfer_progress stats = { 0 }; + + cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); + cl_git_pass(git_indexer_append( + idx, missing_trailer_pack, missing_trailer_pack_len, &stats)); + cl_git_fail(git_indexer_commit(idx, &stats)); + + cl_assert(giterr_last() != NULL); + cl_assert_equal_i(giterr_last()->klass, GITERR_INDEXER); + + git_indexer_free(idx); +} + void test_pack_indexer__leaky(void) { git_indexer *idx = 0; @@ -153,6 +192,35 @@ void test_pack_indexer__fix_thin(void) } } +void test_pack_indexer__corrupt_length(void) +{ + git_indexer *idx = NULL; + git_transfer_progress stats = { 0 }; + git_repository *repo; + git_odb *odb; + git_oid id, should_id; + + cl_git_pass(git_repository_init(&repo, "thin.git", true)); + cl_git_pass(git_repository_odb(&odb, repo)); + + /* Store the missing base into your ODB so the indexer can fix the pack */ + cl_git_pass(git_odb_write(&id, odb, base_obj, base_obj_len, GIT_OBJ_BLOB)); + git_oid_fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18"); + cl_assert_equal_oid(&should_id, &id); + + cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL)); + cl_git_pass(git_indexer_append( + idx, corrupt_thin_pack, corrupt_thin_pack_len, &stats)); + cl_git_fail(git_indexer_commit(idx, &stats)); + + cl_assert(giterr_last() != NULL); + cl_assert_equal_i(giterr_last()->klass, GITERR_ZLIB); + + git_indexer_free(idx); + git_odb_free(odb); + git_repository_free(repo); +} + static int find_tmp_file_recurs(void *opaque, git_buf *path) { int error = 0; diff --git a/tests/patch/parse.c b/tests/patch/parse.c index 8350ac2dd..a40ad7b23 100644 --- a/tests/patch/parse.c +++ b/tests/patch/parse.c @@ -102,3 +102,9 @@ void test_patch_parse__invalid_patches_fails(void) strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL)); } +void test_patch_parse__files_with_whitespaces_succeeds(void) +{ + git_patch *patch; + cl_git_pass(git_patch_from_buffer(&patch, PATCH_NAME_WHITESPACE, strlen(PATCH_NAME_WHITESPACE), NULL)); + git_patch_free(patch); +} diff --git a/tests/patch/patch_common.h b/tests/patch/patch_common.h index a20ebd617..e838e6089 100644 --- a/tests/patch/patch_common.h +++ b/tests/patch/patch_common.h @@ -575,6 +575,16 @@ "+added line with no nl\n" \ "\\ No newline at end of file\n" +#define PATCH_NAME_WHITESPACE \ + "diff --git a/file with spaces.txt b/file with spaces.txt\n" \ + "index 9432026..83759c0 100644\n" \ + "--- a/file with spaces.txt\n" \ + "+++ b/file with spaces.txt\n" \ + "@@ -0,3 +0,2 @@\n" \ + " and this\n" \ + "-is additional context\n" \ + " below it!\n" \ + #define PATCH_CORRUPT_GIT_HEADER \ "diff --git a/file.txt\n" \ "index 9432026..0f39b9a 100644\n" \ diff --git a/tests/refs/iterator.c b/tests/refs/iterator.c index c77451309..56f6ce505 100644 --- a/tests/refs/iterator.c +++ b/tests/refs/iterator.c @@ -6,12 +6,12 @@ static git_repository *repo; void test_refs_iterator__initialize(void) { - cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + repo = cl_git_sandbox_init("testrepo.git"); } void test_refs_iterator__cleanup(void) { - git_repository_free(repo); + cl_git_sandbox_cleanup(); } static const char *refnames[] = { @@ -36,6 +36,36 @@ static const char *refnames[] = { "refs/tags/taggerless", "refs/tags/test", "refs/tags/wrapped_tag", + NULL +}; + +static const char *refnames_with_symlink[] = { + "refs/heads/br2", + "refs/heads/cannot-fetch", + "refs/heads/chomped", + "refs/heads/haacked", + "refs/heads/link/a", + "refs/heads/link/b", + "refs/heads/link/c", + "refs/heads/link/d", + "refs/heads/master", + "refs/heads/not-good", + "refs/heads/packed", + "refs/heads/packed-test", + "refs/heads/subtrees", + "refs/heads/test", + "refs/heads/track-local", + "refs/heads/trailing", + "refs/notes/fanout", + "refs/remotes/test/master", + "refs/tags/annotated_tag_to_blob", + "refs/tags/e90810b", + "refs/tags/hard_tag", + "refs/tags/point_to_blob", + "refs/tags/taggerless", + "refs/tags/test", + "refs/tags/wrapped_tag", + NULL }; static int refcmp_cb(const void *a, const void *b) @@ -46,21 +76,21 @@ static int refcmp_cb(const void *a, const void *b) return strcmp(refa->name, refb->name); } -static void assert_all_refnames_match(git_vector *output) +static void assert_all_refnames_match(const char **expected, git_vector *names) { size_t i; git_reference *ref; - cl_assert_equal_sz(output->length, ARRAY_SIZE(refnames)); - - git_vector_sort(output); + git_vector_sort(names); - git_vector_foreach(output, i, ref) { - cl_assert_equal_s(ref->name, refnames[i]); + git_vector_foreach(names, i, ref) { + cl_assert(expected[i] != NULL); + cl_assert_equal_s(expected[i], ref->name); git_reference_free(ref); } + cl_assert(expected[i] == NULL); - git_vector_free(output); + git_vector_free(names); } void test_refs_iterator__list(void) @@ -82,7 +112,7 @@ void test_refs_iterator__list(void) git_reference_iterator_free(iter); - assert_all_refnames_match(&output); + assert_all_refnames_match(refnames, &output); } void test_refs_iterator__empty(void) @@ -115,7 +145,29 @@ void test_refs_iterator__foreach(void) git_vector output; cl_git_pass(git_vector_init(&output, 32, &refcmp_cb)); cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output)); - assert_all_refnames_match(&output); + assert_all_refnames_match(refnames, &output); +} + +void test_refs_iterator__foreach_through_symlink(void) +{ + git_vector output; + +#ifdef GIT_WIN32 + cl_skip(); +#endif + + cl_git_pass(git_vector_init(&output, 32, &refcmp_cb)); + + cl_git_pass(p_mkdir("refs", 0777)); + cl_git_mkfile("refs/a", "1234567890123456789012345678901234567890"); + cl_git_mkfile("refs/b", "1234567890123456789012345678901234567890"); + cl_git_mkfile("refs/c", "1234567890123456789012345678901234567890"); + cl_git_mkfile("refs/d", "1234567890123456789012345678901234567890"); + + cl_git_pass(p_symlink("../../../refs", "testrepo.git/refs/heads/link")); + + cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output)); + assert_all_refnames_match(refnames_with_symlink, &output); } static int refs_foreach_cancel_cb(git_reference *reference, void *payload) @@ -156,12 +208,11 @@ void test_refs_iterator__foreach_name(void) cl_git_pass( git_reference_foreach_name(repo, refs_foreach_name_cb, &output)); - cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames)); git_vector_sort(&output); git_vector_foreach(&output, i, name) { - cl_assert_equal_s(name, refnames[i]); - git__free(name); + cl_assert(refnames[i] != NULL); + cl_assert_equal_s(refnames[i], name); } git_vector_free(&output); @@ -194,7 +245,7 @@ void test_refs_iterator__concurrent_delete(void) const char *name; int error; - git_repository_free(repo); + cl_git_sandbox_cleanup(); repo = cl_git_sandbox_init("testrepo"); cl_git_pass(git_reference_iterator_new(&iter, repo)); @@ -215,7 +266,4 @@ void test_refs_iterator__concurrent_delete(void) cl_assert_equal_i(GIT_ITEROVER, error); cl_assert_equal_i(full_count, concurrent_count); - - cl_git_sandbox_cleanup(); - repo = NULL; } diff --git a/tests/resources/merge-resolve/.gitted/objects/50/c5dc8cdfe40c688eb0a0e23be54dd57cae2e78 b/tests/resources/merge-resolve/.gitted/objects/50/c5dc8cdfe40c688eb0a0e23be54dd57cae2e78 new file mode 100644 index 000000000..c04baa14b --- /dev/null +++ b/tests/resources/merge-resolve/.gitted/objects/50/c5dc8cdfe40c688eb0a0e23be54dd57cae2e78 @@ -0,0 +1,2 @@ +x] +0})Jv">x/I6ZhIӞ*aeZC`F6;KLO).y8N^ }a'Ѱ S*gpmHp_sh/O>.PiF?,kJZGoJT
\ No newline at end of file diff --git a/tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4 b/tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4 Binary files differnew file mode 100644 index 000000000..b9c06303b --- /dev/null +++ b/tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4 diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/delete-submodule b/tests/resources/merge-resolve/.gitted/refs/heads/delete-submodule new file mode 100644 index 000000000..1951316d5 --- /dev/null +++ b/tests/resources/merge-resolve/.gitted/refs/heads/delete-submodule @@ -0,0 +1 @@ +50c5dc8cdfe40c688eb0a0e23be54dd57cae2e78 |