Unverified Commit e8bc8558 by Brian Lopez

Merge remote-tracking branch 'origin/master' into charliesome/trailer-info

parents 72fbf05c 7610638e
......@@ -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
......
......@@ -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>
......
......@@ -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
......
......@@ -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)
......
......@@ -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(
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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) {
......
......@@ -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);
......
......@@ -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 {
......
......@@ -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, &notes_ref, repo, notes_ref_in);
if (!(error = retrieve_note_tree_and_commit(
&tree, &commit, &notes_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, &notes_ref, repo, notes_ref_in);
error = retrieve_note_commit(&existing_notes_commit, &notes_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(&notes_commit_oid,
&notes_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,
&notes_commit_oid, 1, NULL);
if (out != NULL)
git_oid_cpy(out, &notes_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, &notes_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, &notes_ref_target,
repo, notes_ref_in);
if (!(error = retrieve_note_tree_and_commit(
&tree, &commit, &notes_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(&notes_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, &notes_ref, repo, notes_ref_in);
error = retrieve_note_commit(&commit, &notes_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;
......
......@@ -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;
......
......@@ -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);
......
......@@ -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");
......
......@@ -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;
......
......@@ -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 */
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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;
......
......@@ -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 {
......
......@@ -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;
......
......@@ -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;
......
......@@ -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) {
......
......@@ -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)
......
......@@ -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);
......
......@@ -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;
......
......@@ -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);
......
#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;
......
......@@ -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);
......
......@@ -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);
}
#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);
}
......@@ -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;
......
......@@ -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);
}
......@@ -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" \
......
......@@ -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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment