From 5d7c940b07bfd2bc14ef67ef8a00fb03de59b97c Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Tue, 16 Apr 2013 13:25:38 -0700 Subject: [PATCH 001/384] /mailmap me --- .mailmap | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index 582dae0b9..fb44eb826 100644 --- a/.mailmap +++ b/.mailmap @@ -1,3 +1,5 @@ -Vicent Martí Vicent Marti +Vicent Martí Vicent Marti Vicent Martí Vicent Martí Michael Schubert schu +Ben Straub Ben Straub +Ben Straub Ben Straub From a442ed687d4c01a68de9aa7b0e50902f17a1ea84 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 17 Apr 2013 04:46:37 +0200 Subject: [PATCH 002/384] repository: Add `git_repository_open_bare` --- include/git2/repository.h | 15 +++++++++++++++ src/repository.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/include/git2/repository.h b/include/git2/repository.h index e75c8b136..e3320975c 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -123,6 +123,21 @@ GIT_EXTERN(int) git_repository_open_ext( unsigned int flags, const char *ceiling_dirs); +/** + * Open a bare repository on the serverside. + * + * This is a fast open for bare repositories that will come in handy + * if you're e.g. hosting git repositories and need to access them + * efficiently + * + * @param out Pointer to the repo which will be opened. + * @param bare_path Direct path to the bare repository + * @return 0 on success, or an error code + */ +GIT_EXTERN(int) git_repository_open_bare( + git_repository **out, + const char *bare_path); + /** * Free a previously allocated repository * diff --git a/src/repository.c b/src/repository.c index 0ad7449ba..64ab2f4db 100644 --- a/src/repository.c +++ b/src/repository.c @@ -368,6 +368,37 @@ static int find_repo( return error; } +int git_repository_open_bare( + git_repository **repo_ptr, + const char *bare_path) +{ + int error; + git_buf path = GIT_BUF_INIT; + git_repository *repo = NULL; + + if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0) + return error; + + if (!valid_repository_path(&path)) { + git_buf_free(&path); + giterr_set(GITERR_REPOSITORY, "Path is not a repository: %s", bare_path); + return GIT_ENOTFOUND; + } + + repo = repository_alloc(); + GITERR_CHECK_ALLOC(repo); + + repo->path_repository = git_buf_detach(&path); + GITERR_CHECK_ALLOC(repo->path_repository); + + /* of course we're bare! */ + repo->is_bare = 1; + repo->workdir = NULL; + + *repo_ptr = repo; + return 0; +} + int git_repository_open_ext( git_repository **repo_ptr, const char *start_path, From 6edad4d8a4c5e20544eb3b1daa926413518dd020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 17 Apr 2013 11:03:18 +0200 Subject: [PATCH 003/384] Add mailmap entries for me --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index fb44eb826..a8b171908 100644 --- a/.mailmap +++ b/.mailmap @@ -3,3 +3,5 @@ Vicent Martí Vicent Martí Michael Schubert schu Ben Straub Ben Straub Ben Straub Ben Straub +Carlos Martín Nieto +Carlos Martín Nieto From 1cfaaa9e48655c4a5b4ebc4f8c20fcb8c6537e49 Mon Sep 17 00:00:00 2001 From: Alessandro Ghedini Date: Wed, 17 Apr 2013 13:48:26 +0200 Subject: [PATCH 004/384] Update link to Perl bindings --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 790e202d7..fdddc5ca1 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Here are the bindings to libgit2 that are currently available: * Parrot Virtual Machine * parrot-libgit2 * Perl - * git-xs-pm + * Git-Raw * PHP * php-git * Python From 3be933b143731bbe3a5cadcdf70b8ab205a629c0 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 17 Apr 2013 17:33:51 +0200 Subject: [PATCH 005/384] refs: Add `git_referene_target_peel` --- include/git2/refdb.h | 7 +- include/git2/refs.h | 11 +++ src/refdb_fs.c | 7 +- src/refs.c | 169 +++++++++++++++++++++++------------- src/refs.h | 7 +- tests-clar/refdb/inmemory.c | 4 +- tests-clar/refdb/testdb.c | 16 ++-- 7 files changed, 149 insertions(+), 72 deletions(-) diff --git a/include/git2/refdb.h b/include/git2/refdb.h index 0586b119e..76b8fda0d 100644 --- a/include/git2/refdb.h +++ b/include/git2/refdb.h @@ -35,7 +35,12 @@ GIT_EXTERN(git_reference *) git_reference__alloc( git_refdb *refdb, const char *name, const git_oid *oid, - const char *symbolic); + const git_oid *peel); + +GIT_EXTERN(git_reference *) git_reference__alloc_symbolic( + git_refdb *refdb, + const char *name, + const char *target); /** * Create a new reference database with no backends. diff --git a/include/git2/refs.h b/include/git2/refs.h index e0451ba82..1ff0d4544 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -132,6 +132,17 @@ GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, */ GIT_EXTERN(const git_oid *) git_reference_target(const git_reference *ref); +/** + * Return the peeled OID target of this reference. + * + * This peeled OID only applies to direct references that point to + * a hard Tag object: it is the result of peeling such Tag. + * + * @param ref The reference + * @return a pointer to the oid if available, NULL otherwise + */ +GIT_EXTERN(const git_oid *) git_reference_target_peel(const git_reference *ref); + /** * Get full name to the reference pointed to by a symbolic reference. * diff --git a/src/refdb_fs.c b/src/refdb_fs.c index f00bd72a0..730148a8f 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -430,7 +430,7 @@ static int loose_lookup( goto done; } - *out = git_reference__alloc(backend->refdb, ref_name, NULL, target); + *out = git_reference__alloc_symbolic(backend->refdb, ref_name, target); } else { if ((error = loose_parse_oid(&oid, &ref_file)) < 0) goto done; @@ -484,7 +484,8 @@ static int packed_lookup( if ((error = packed_map_entry(&entry, &pos, backend, ref_name)) < 0) return error; - if ((*out = git_reference__alloc(backend->refdb, ref_name, &entry->oid, NULL)) == NULL) + if ((*out = git_reference__alloc(backend->refdb, ref_name, + &entry->oid, &entry->peel)) == NULL) return -1; return 0; @@ -644,7 +645,7 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref) if (ref->type == GIT_REF_OID) { char oid[GIT_OID_HEXSZ + 1]; - git_oid_fmt(oid, &ref->target.oid); + git_oid_fmt(oid, &ref->target.direct.oid); oid[GIT_OID_HEXSZ] = '\0'; git_filebuf_printf(&file, "%s\n", oid); diff --git a/src/refs.c b/src/refs.c index b1f679632..290b89b41 100644 --- a/src/refs.c +++ b/src/refs.c @@ -31,37 +31,62 @@ enum { GIT_PACKREF_WAS_LOOSE = 2 }; +static git_reference *alloc_ref(git_refdb *refdb, const char *name) +{ + git_reference *ref; + size_t namelen = strlen(name); + + if ((ref = git__calloc(1, sizeof(git_reference) + namelen + 1)) == NULL) + return NULL; + + ref->db = refdb; + memcpy(ref->name, name, namelen + 1); + + return ref; +} + +git_reference *git_reference__alloc_symbolic( + git_refdb *refdb, + const char *name, + const char *target) +{ + git_reference *ref; + + assert(refdb && name && target); + + ref = alloc_ref(refdb, name); + if (!ref) + return NULL; + + ref->type = GIT_REF_SYMBOLIC; + + if ((ref->target.symbolic = git__strdup(target)) == NULL) { + git__free(ref); + return NULL; + } + + return ref; +} git_reference *git_reference__alloc( git_refdb *refdb, const char *name, const git_oid *oid, - const char *symbolic) + const git_oid *peel) { git_reference *ref; - size_t namelen; - assert(refdb && name && ((oid && !symbolic) || (!oid && symbolic))); + assert(refdb && name && oid); - namelen = strlen(name); - - if ((ref = git__calloc(1, sizeof(git_reference) + namelen + 1)) == NULL) + ref = alloc_ref(refdb, name); + if (!ref) return NULL; - if (oid) { - ref->type = GIT_REF_OID; - git_oid_cpy(&ref->target.oid, oid); - } else { - ref->type = GIT_REF_SYMBOLIC; + ref->type = GIT_REF_OID; + git_oid_cpy(&ref->target.direct.oid, oid); - if ((ref->target.symbolic = git__strdup(symbolic)) == NULL) { - git__free(ref); - return NULL; - } - } - - ref->db = refdb; - memcpy(ref->name, name, namelen + 1); + if (peel != NULL) + git_oid_cpy(&ref->target.direct.peel, peel); return ref; } @@ -71,13 +96,8 @@ void git_reference_free(git_reference *reference) if (reference == NULL) return; - if (reference->type == GIT_REF_SYMBOLIC) { + if (reference->type == GIT_REF_SYMBOLIC) git__free(reference->target.symbolic); - reference->target.symbolic = NULL; - } - - reference->db = NULL; - reference->type = GIT_REF_INVALID; git__free(reference); } @@ -302,7 +322,17 @@ const git_oid *git_reference_target(const git_reference *ref) if (ref->type != GIT_REF_OID) return NULL; - return &ref->target.oid; + return &ref->target.direct.oid; +} + +const git_oid *git_reference_target_peel(const git_reference *ref) +{ + assert(ref); + + if (ref->type != GIT_REF_OID || git_oid_iszero(&ref->target.direct.peel)) + return NULL; + + return &ref->target.direct.peel; } const char *git_reference_symbolic_target(const git_reference *ref) @@ -335,8 +365,15 @@ static int reference__create( (error = reference_can_write(repo, normalized, NULL, force)) < 0 || (error = git_repository_refdb__weakptr(&refdb, repo)) < 0) return error; + + if (oid != NULL) { + assert(symbolic == NULL); + ref = git_reference__alloc(refdb, name, oid, NULL); + } else { + ref = git_reference__alloc_symbolic(refdb, name, symbolic); + } - if ((ref = git_reference__alloc(refdb, name, oid, symbolic)) == NULL) + if (ref == NULL) return -1; if ((error = git_refdb_write(refdb, ref)) < 0) { @@ -437,8 +474,6 @@ int git_reference_rename( char normalized[GIT_REFNAME_MAX]; bool should_head_be_updated = false; git_reference *result = NULL; - git_oid *oid; - const char *symbolic; int error = 0; int reference_has_log; @@ -447,7 +482,8 @@ int git_reference_rename( normalization_flags = ref->type == GIT_REF_SYMBOLIC ? GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL; - if ((error = git_reference_normalize_name(normalized, sizeof(normalized), new_name, normalization_flags)) < 0 || + if ((error = git_reference_normalize_name( + normalized, sizeof(normalized), new_name, normalization_flags)) < 0 || (error = reference_can_write(ref->db->repo, normalized, ref->name, force)) < 0) return error; @@ -455,14 +491,15 @@ int git_reference_rename( * Create the new reference. */ if (ref->type == GIT_REF_OID) { - oid = &ref->target.oid; - symbolic = NULL; + result = git_reference__alloc(ref->db, new_name, + &ref->target.direct.oid, &ref->target.direct.peel); + } else if (ref->type == GIT_REF_SYMBOLIC) { + result = git_reference__alloc_symbolic(ref->db, new_name, ref->target.symbolic); } else { - oid = NULL; - symbolic = ref->target.symbolic; + assert(0); } - - if ((result = git_reference__alloc(ref->db, new_name, oid, symbolic)) == NULL) + + if (result == NULL) return -1; /* Check if we have to update HEAD. */ @@ -509,11 +546,17 @@ on_error: int git_reference_resolve(git_reference **ref_out, const git_reference *ref) { - if (ref->type == GIT_REF_OID) + switch (git_reference_type(ref)) { + case GIT_REF_OID: return git_reference_lookup(ref_out, ref->db->repo, ref->name); - else - return git_reference_lookup_resolved(ref_out, ref->db->repo, - ref->target.symbolic, -1); + + case GIT_REF_SYMBOLIC: + return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1); + + default: + giterr_set(GITERR_REFERENCE, "Invalid reference"); + return -1; + } } int git_reference_foreach( @@ -778,16 +821,20 @@ int git_reference__normalize_name_lax( int git_reference_cmp(git_reference *ref1, git_reference *ref2) { + git_ref_t type1, type2; assert(ref1 && ref2); - /* let's put symbolic refs before OIDs */ - if (ref1->type != ref2->type) - return (ref1->type == GIT_REF_SYMBOLIC) ? -1 : 1; + type1 = git_reference_type(ref1); + type2 = git_reference_type(ref2); - if (ref1->type == GIT_REF_SYMBOLIC) + /* let's put symbolic refs before OIDs */ + if (type1 != type2) + return (type1 == GIT_REF_SYMBOLIC) ? -1 : 1; + + if (type1 == GIT_REF_SYMBOLIC) return strcmp(ref1->target.symbolic, ref2->target.symbolic); - return git_oid_cmp(&ref1->target.oid, &ref2->target.oid); + return git_oid_cmp(&ref1->target.direct.oid, &ref2->target.direct.oid); } static int reference__update_terminal( @@ -905,15 +952,6 @@ static int peel_error(int error, git_reference *ref, const char* msg) return error; } -static int reference_target(git_object **object, git_reference *ref) -{ - const git_oid *oid; - - oid = git_reference_target(ref); - - return git_object_lookup(object, git_reference_owner(ref), oid, GIT_OBJ_ANY); -} - int git_reference_peel( git_object **peeled, git_reference *ref, @@ -925,10 +963,22 @@ int git_reference_peel( assert(ref); - if ((error = git_reference_resolve(&resolved, ref)) < 0) - return peel_error(error, ref, "Cannot resolve reference"); + if (ref->type == GIT_REF_OID) { + resolved = ref; + } else { + if ((error = git_reference_resolve(&resolved, ref)) < 0) + return peel_error(error, ref, "Cannot resolve reference"); + } - if ((error = reference_target(&target, resolved)) < 0) { + if (!git_oid_iszero(&resolved->target.direct.peel)) { + error = git_object_lookup(&target, + git_reference_owner(ref), &resolved->target.direct.peel, GIT_OBJ_ANY); + } else { + error = git_object_lookup(&target, + git_reference_owner(ref), &resolved->target.direct.oid, GIT_OBJ_ANY); + } + + if (error < 0) { peel_error(error, ref, "Cannot retrieve reference target"); goto cleanup; } @@ -940,7 +990,10 @@ int git_reference_peel( cleanup: git_object_free(target); - git_reference_free(resolved); + + if (resolved != ref) + git_reference_free(resolved); + return error; } diff --git a/src/refs.h b/src/refs.h index 7d63c3fbd..b0aa56a54 100644 --- a/src/refs.h +++ b/src/refs.h @@ -49,11 +49,14 @@ struct git_reference { git_refdb *db; - git_ref_t type; union { - git_oid oid; + struct { + git_oid oid; + git_oid peel; + } direct; + char *symbolic; } target; diff --git a/tests-clar/refdb/inmemory.c b/tests-clar/refdb/inmemory.c index 6f5651964..2cccd8eb2 100644 --- a/tests-clar/refdb/inmemory.c +++ b/tests-clar/refdb/inmemory.c @@ -135,7 +135,7 @@ int foreach_test(const char *ref_name, void *payload) else if (*i == 2) cl_git_pass(git_oid_fromstr(&expected, "763d71aadf09a7951596c9746c024e7eece7c7af")); - cl_assert(git_oid_cmp(&expected, &ref->target.oid) == 0); + cl_assert(git_oid_cmp(&expected, git_reference_target(ref)) == 0); ++(*i); @@ -176,7 +176,7 @@ int delete_test(const char *ref_name, void *payload) cl_git_pass(git_reference_lookup(&ref, repo, ref_name)); cl_git_pass(git_oid_fromstr(&expected, "e90810b8df3e80c413d903f631643c716887138d")); - cl_assert(git_oid_cmp(&expected, &ref->target.oid) == 0); + cl_assert(git_oid_cmp(&expected, git_reference_target(ref)) == 0); ++(*i); diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index a8e7ba5fe..e60f6790e 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -98,12 +98,16 @@ static int refdb_test_backend__lookup( git_vector_foreach(&backend->refs, i, entry) { if (strcmp(entry->name, ref_name) == 0) { - const git_oid *oid = - entry->type == GIT_REF_OID ? &entry->target.oid : NULL; - const char *symbolic = - entry->type == GIT_REF_SYMBOLIC ? entry->target.symbolic : NULL; - - if ((*out = git_reference__alloc(backend->refdb, ref_name, oid, symbolic)) == NULL) + + if (entry->type == GIT_REF_OID) { + *out = git_reference__alloc(backend->refdb, ref_name, + &entry->target.oid, NULL); + } else if (entry->type == GIT_REF_SYMBOLIC) { + *out = git_reference__alloc_symbolic(backend->refdb, ref_name, + entry->target.symbolic); + } + + if (*out == NULL) return -1; return 0; From 0da62c5cf094394e7d9a4f7ef0832f8459ab3d40 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 17 Apr 2013 10:52:49 -0500 Subject: [PATCH 006/384] checkout: use cache when possible to determine if workdir item is dirty If the on-disk file has been staged (it's stat data matches the stat data in the cache) then we need not hash the file to determine whether it differs from the checkout target; instead we can simply use the oid in the index. This prevents recomputing a file's hash unnecessarily, prevents loading the file (when filtering) and prevents edge cases where filters suggest that a file is dirty immediately after git writes the file. --- src/checkout.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/checkout.c b/src/checkout.c index 24fa21024..81dc5e3da 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -119,6 +119,7 @@ static bool checkout_is_workdir_modified( const git_index_entry *wditem) { git_oid oid; + const git_index_entry *ie; /* handle "modified" submodule */ if (wditem->mode == GIT_FILEMODE_COMMIT) { @@ -140,6 +141,17 @@ static bool checkout_is_workdir_modified( return (git_oid_cmp(&baseitem->oid, sm_oid) != 0); } + /* Look at the cache to decide if the workdir is modified. If not, + * we can simply compare the oid in the cache to the baseitem instead + * of hashing the file. + */ + if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) { + if (wditem->mtime.seconds == ie->mtime.seconds && + wditem->mtime.nanoseconds == ie->mtime.nanoseconds && + wditem->file_size == ie->file_size) + return (git_oid_cmp(&baseitem->oid, &ie->oid) != 0); + } + /* depending on where base is coming from, we may or may not know * the actual size of the data, so we can't rely on this shortcut. */ From 13421eee1ac89a90f45524d8158ace98aae3d0b9 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 17 Apr 2013 22:32:39 +0200 Subject: [PATCH 007/384] refs: Check alloc is cleaner --- src/refs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/refs.c b/src/refs.c index 290b89b41..982cd05b7 100644 --- a/src/refs.c +++ b/src/refs.c @@ -373,8 +373,7 @@ static int reference__create( ref = git_reference__alloc_symbolic(refdb, name, symbolic); } - if (ref == NULL) - return -1; + GITERR_CHECK_ALLOC(ref); if ((error = git_refdb_write(refdb, ref)) < 0) { git_reference_free(ref); From fedd0f9e90e3046a8c50f6209c37d3b4566bab10 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 17 Apr 2013 23:29:34 +0200 Subject: [PATCH 008/384] refs: Do not union the peel --- src/refdb_fs.c | 2 +- src/refs.c | 20 ++++++++++---------- src/refs.h | 9 +++------ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 730148a8f..784749fd3 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -645,7 +645,7 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref) if (ref->type == GIT_REF_OID) { char oid[GIT_OID_HEXSZ + 1]; - git_oid_fmt(oid, &ref->target.direct.oid); + git_oid_fmt(oid, &ref->target.oid); oid[GIT_OID_HEXSZ] = '\0'; git_filebuf_printf(&file, "%s\n", oid); diff --git a/src/refs.c b/src/refs.c index 982cd05b7..29d1c4fa9 100644 --- a/src/refs.c +++ b/src/refs.c @@ -83,10 +83,10 @@ git_reference *git_reference__alloc( return NULL; ref->type = GIT_REF_OID; - git_oid_cpy(&ref->target.direct.oid, oid); + git_oid_cpy(&ref->target.oid, oid); if (peel != NULL) - git_oid_cpy(&ref->target.direct.peel, peel); + git_oid_cpy(&ref->peel, peel); return ref; } @@ -322,17 +322,17 @@ const git_oid *git_reference_target(const git_reference *ref) if (ref->type != GIT_REF_OID) return NULL; - return &ref->target.direct.oid; + return &ref->target.oid; } const git_oid *git_reference_target_peel(const git_reference *ref) { assert(ref); - if (ref->type != GIT_REF_OID || git_oid_iszero(&ref->target.direct.peel)) + if (ref->type != GIT_REF_OID || git_oid_iszero(&ref->peel)) return NULL; - return &ref->target.direct.peel; + return &ref->peel; } const char *git_reference_symbolic_target(const git_reference *ref) @@ -491,7 +491,7 @@ int git_reference_rename( */ if (ref->type == GIT_REF_OID) { result = git_reference__alloc(ref->db, new_name, - &ref->target.direct.oid, &ref->target.direct.peel); + &ref->target.oid, &ref->peel); } else if (ref->type == GIT_REF_SYMBOLIC) { result = git_reference__alloc_symbolic(ref->db, new_name, ref->target.symbolic); } else { @@ -833,7 +833,7 @@ int git_reference_cmp(git_reference *ref1, git_reference *ref2) if (type1 == GIT_REF_SYMBOLIC) return strcmp(ref1->target.symbolic, ref2->target.symbolic); - return git_oid_cmp(&ref1->target.direct.oid, &ref2->target.direct.oid); + return git_oid_cmp(&ref1->target.oid, &ref2->target.oid); } static int reference__update_terminal( @@ -969,12 +969,12 @@ int git_reference_peel( return peel_error(error, ref, "Cannot resolve reference"); } - if (!git_oid_iszero(&resolved->target.direct.peel)) { + if (!git_oid_iszero(&resolved->peel)) { error = git_object_lookup(&target, - git_reference_owner(ref), &resolved->target.direct.peel, GIT_OBJ_ANY); + git_reference_owner(ref), &resolved->peel, GIT_OBJ_ANY); } else { error = git_object_lookup(&target, - git_reference_owner(ref), &resolved->target.direct.oid, GIT_OBJ_ANY); + git_reference_owner(ref), &resolved->target.oid, GIT_OBJ_ANY); } if (error < 0) { diff --git a/src/refs.h b/src/refs.h index b0aa56a54..97d4d2eb5 100644 --- a/src/refs.h +++ b/src/refs.h @@ -52,14 +52,11 @@ struct git_reference { git_ref_t type; union { - struct { - git_oid oid; - git_oid peel; - } direct; - + git_oid oid; char *symbolic; } target; - + + git_oid peel; char name[0]; }; From 437d36662e7bf9680b8eaeec626cfba5f4d68bd9 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 18 Apr 2013 00:15:08 +0200 Subject: [PATCH 009/384] repository: Doc fix --- include/git2/repository.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/git2/repository.h b/include/git2/repository.h index e3320975c..ed837b359 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -134,9 +134,7 @@ GIT_EXTERN(int) git_repository_open_ext( * @param bare_path Direct path to the bare repository * @return 0 on success, or an error code */ -GIT_EXTERN(int) git_repository_open_bare( - git_repository **out, - const char *bare_path); +GIT_EXTERN(int) git_repository_open_bare(git_repository **out, const char *bare_path); /** * Free a previously allocated repository From 8023b83a945eaf3be7baad4fa74d93f4a079be0f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 17 Apr 2013 17:21:17 -0500 Subject: [PATCH 010/384] use a longer string for dummy data in test to avoid conflicting w/ index --- tests-clar/checkout/typechange.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-clar/checkout/typechange.c b/tests-clar/checkout/typechange.c index b92cc23fa..6cf99ac15 100644 --- a/tests-clar/checkout/typechange.c +++ b/tests-clar/checkout/typechange.c @@ -187,7 +187,7 @@ static void force_create_file(const char *file) GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS); cl_assert(!error || error == GIT_ENOTFOUND); cl_git_pass(git_futils_mkpath2file(file, 0777)); - cl_git_rewritefile(file, "yowza!"); + cl_git_rewritefile(file, "yowza!!"); } void test_checkout_typechange__checkout_with_conflicts(void) From 9e46f6761891dbf05b733baae48e8c7161a213b5 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 18 Apr 2013 00:55:20 -0400 Subject: [PATCH 011/384] Return error for empty name/email --- src/signature.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signature.c b/src/signature.c index 164e8eb67..649dbcd3d 100644 --- a/src/signature.c +++ b/src/signature.c @@ -69,7 +69,7 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema if (p->name == NULL || p->email == NULL || p->name[0] == '\0' || p->email[0] == '\0') { git_signature_free(p); - return -1; + return signature_error("Empty name or email"); } p->when.time = time; From f90391ea5fdcd5ef972958ac375d8223a5045cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 18 Apr 2013 14:47:54 +0200 Subject: [PATCH 012/384] treebuilder: don't overwrite the error message --- src/tree.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tree.c b/src/tree.c index 17b3c378d..d2db84055 100644 --- a/src/tree.c +++ b/src/tree.c @@ -525,7 +525,6 @@ static int write_tree( /* Write out the subtree */ written = write_tree(&sub_oid, repo, index, subdir, i); if (written < 0) { - tree_error("Failed to write subtree", subdir); git__free(subdir); goto on_error; } else { From ff0ddfa4bbec2e27491c822cf6b882c54d8675c8 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 17 Apr 2013 15:56:31 -0700 Subject: [PATCH 013/384] Add filesystem iterator variant This adds a new variant iterator that is a raw filesystem iterator for scanning directories from a root. There is still more work to do to blend this with the working directory iterator. --- src/iterator.c | 301 +++++++++++++++++++++++++++++++++++++ src/iterator.h | 11 ++ tests-clar/repo/iterator.c | 44 ++++++ 3 files changed, 356 insertions(+) diff --git a/src/iterator.c b/src/iterator.c index 5b5ed9525..dd8a6133d 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -833,6 +833,307 @@ int git_iterator_for_index( } +typedef struct fs_iterator_frame fs_iterator_frame; +struct fs_iterator_frame { + fs_iterator_frame *next; + git_vector entries; + size_t index; +}; + +typedef struct { + git_iterator base; + git_iterator_callbacks cb; + fs_iterator_frame *stack; + git_index_entry entry; + git_buf path; + size_t root_len; + int depth; +} fs_iterator; + +#define FS_MAX_DEPTH 100 + +static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi) +{ + fs_iterator_frame *ff = git__calloc(1, sizeof(fs_iterator_frame)); + git_vector_cmp entry_compare = CASESELECT( + iterator__ignore_case(fi), + git_path_with_stat_cmp_icase, git_path_with_stat_cmp); + + if (ff && git_vector_init(&ff->entries, 0, entry_compare) < 0) { + git__free(ff); + ff = NULL; + } + + return ff; +} + +static void fs_iterator__free_frame(fs_iterator_frame *ff) +{ + size_t i; + git_path_with_stat *path; + + git_vector_foreach(&ff->entries, i, path) + git__free(path); + git_vector_free(&ff->entries); + git__free(ff); +} + +static int fs_iterator__update_entry(fs_iterator *fi); + +static int fs_iterator__entry_cmp(const void *i, const void *item) +{ + const fs_iterator *fi = (const fs_iterator *)i; + const git_path_with_stat *ps = item; + return fi->base.prefixcomp(fi->base.start, ps->path); +} + +static void fs_iterator__seek_frame_start( + fs_iterator *fi, fs_iterator_frame *ff) +{ + if (!ff) + return; + + if (fi->base.start) + git_vector_bsearch2( + &ff->index, &ff->entries, fs_iterator__entry_cmp, fi); + else + ff->index = 0; +} + +static int fs_iterator__expand_dir(fs_iterator *fi) +{ + int error; + fs_iterator_frame *ff; + + ff = fs_iterator__alloc_frame(fi); + GITERR_CHECK_ALLOC(ff); + + error = git_path_dirload_with_stat( + fi->path.ptr, fi->root_len, iterator__ignore_case(fi), + fi->base.start, fi->base.end, &ff->entries); + + if (error < 0 || ff->entries.length == 0) { + fs_iterator__free_frame(ff); + return GIT_ENOTFOUND; + } + + if (++(fi->depth) > FS_MAX_DEPTH) { + giterr_set(GITERR_REPOSITORY, + "Directory nesting is too deep (%d)", fi->depth); + fs_iterator__free_frame(ff); + return -1; + } + + fs_iterator__seek_frame_start(fi, ff); + + ff->next = fi->stack; + fi->stack = ff; + + return fs_iterator__update_entry(fi); +} + +static int fs_iterator__current( + const git_index_entry **entry, git_iterator *self) +{ + fs_iterator *fi = (fs_iterator *)self; + if (entry) + *entry = (fi->entry.path == NULL) ? NULL : &fi->entry; + return 0; +} + +static int fs_iterator__at_end(git_iterator *self) +{ + return (((fs_iterator *)self)->entry.path == NULL); +} + +static int fs_iterator__advance_into( + const git_index_entry **entry, git_iterator *iter) +{ + int error = 0; + fs_iterator *fi = (fs_iterator *)iter; + + iterator__clear_entry(entry); + + /* Allow you to explicitly advance into a commit/submodule (as well as a + * tree) to avoid cases where an entry is mislabeled as a submodule in + * the working directory. The fs iterator will never have COMMMIT + * entries on it's own, but a wrapper might add them. + */ + if (fi->entry.path != NULL && + (fi->entry.mode == GIT_FILEMODE_TREE || + fi->entry.mode == GIT_FILEMODE_COMMIT)) + /* returns GIT_ENOTFOUND if the directory is empty */ + error = fs_iterator__expand_dir(fi); + + if (!error && entry) + error = fs_iterator__current(entry, iter); + + return error; +} + +static int fs_iterator__advance( + const git_index_entry **entry, git_iterator *self) +{ + int error = 0; + fs_iterator *fi = (fs_iterator *)self; + fs_iterator_frame *ff; + git_path_with_stat *next; + + /* given include_trees & autoexpand, we might have to go into a tree */ + if (iterator__do_autoexpand(fi) && + fi->entry.path != NULL && + fi->entry.mode == GIT_FILEMODE_TREE) + { + error = fs_iterator__advance_into(entry, self); + + /* continue silently past empty directories if autoexpanding */ + if (error != GIT_ENOTFOUND) + return error; + giterr_clear(); + error = 0; + } + + if (entry != NULL) + *entry = NULL; + + while (fi->entry.path != NULL) { + ff = fi->stack; + next = git_vector_get(&ff->entries, ++ff->index); + + if (next != NULL) + break; + + /* pop stack if anything is left to pop */ + if (!ff->next) { + memset(&fi->entry, 0, sizeof(fi->entry)); + return 0; + } + + fi->stack = ff->next; + fi->depth--; + fs_iterator__free_frame(ff); + } + + error = fs_iterator__update_entry(fi); + + if (!error && entry != NULL) + error = fs_iterator__current(entry, self); + + return error; +} + +static int fs_iterator__seek(git_iterator *self, const char *prefix) +{ + GIT_UNUSED(self); + GIT_UNUSED(prefix); + /* pop stack until matching prefix */ + /* find prefix item in current frame */ + /* push subdirectories as deep as possible while matching */ + return 0; +} + +static int fs_iterator__reset( + git_iterator *self, const char *start, const char *end) +{ + fs_iterator *fi = (fs_iterator *)self; + + while (fi->stack != NULL && fi->stack->next != NULL) { + fs_iterator_frame *ff = fi->stack; + fi->stack = ff->next; + fs_iterator__free_frame(ff); + } + fi->depth = 0; + + if (iterator__reset_range(self, start, end) < 0) + return -1; + + fs_iterator__seek_frame_start(fi, fi->stack); + + return fs_iterator__update_entry(fi); +} + +static void fs_iterator__free(git_iterator *self) +{ + fs_iterator *fi = (fs_iterator *)self; + + while (fi->stack != NULL) { + fs_iterator_frame *ff = fi->stack; + fi->stack = ff->next; + fs_iterator__free_frame(ff); + } + + git_buf_free(&fi->path); +} + +static int fs_iterator__update_entry(fs_iterator *fi) +{ + git_path_with_stat *ps = + git_vector_get(&fi->stack->entries, fi->stack->index); + + git_buf_truncate(&fi->path, fi->root_len); + memset(&fi->entry, 0, sizeof(fi->entry)); + + if (!ps) + return 0; + + if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0) + return -1; + + if (iterator__past_end(fi, fi->path.ptr + fi->root_len)) + return 0; + + fi->entry.path = ps->path; + git_index_entry__init_from_stat(&fi->entry, &ps->st); + + /* need different mode here to keep directories during iteration */ + fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode); + + /* if this isn't a tree, then we're done */ + if (fi->entry.mode != GIT_FILEMODE_TREE) + return 0; + + if (iterator__include_trees(fi)) + return 0; + + return fs_iterator__advance(NULL, (git_iterator *)fi); +} + +int git_iterator_for_filesystem( + git_iterator **out, + const char *root, + git_iterator_flag_t flags, + const char *start, + const char *end) +{ + int error; + fs_iterator *fi; + + ITERATOR_BASE_INIT(fi, fs, FS, NULL); + + if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0) + fi->base.flags |= GIT_ITERATOR_IGNORE_CASE; + + if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) { + git__free(fi); + return -1; + } + fi->root_len = fi->path.size; + + if ((error = fs_iterator__expand_dir(fi)) < 0) { + if (error != GIT_ENOTFOUND) + goto fail; + giterr_clear(); + } + + *out = (git_iterator *)fi; + return 0; + +fail: + git_iterator_free((git_iterator *)fi); + return error; +} + + #define WORKDIR_MAX_DEPTH 100 typedef struct workdir_iterator_frame workdir_iterator_frame; diff --git a/src/iterator.h b/src/iterator.h index 4a4e6a9d8..7998f7c6b 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -19,6 +19,7 @@ typedef enum { GIT_ITERATOR_TYPE_TREE = 1, GIT_ITERATOR_TYPE_INDEX = 2, GIT_ITERATOR_TYPE_WORKDIR = 3, + GIT_ITERATOR_TYPE_FS = 4, } git_iterator_type_t; typedef enum { @@ -88,6 +89,16 @@ extern int git_iterator_for_workdir( const char *start, const char *end); +/* for filesystem iterators, you have to explicitly pass in the ignore_case + * behavior that you desire + */ +extern int git_iterator_for_filesystem( + git_iterator **out, + const char *root, + git_iterator_flag_t flags, + const char *start, + const char *end); + extern void git_iterator_free(git_iterator *iter); /* Return a git_index_entry structure for the current value the iterator diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 00c46d6b1..ef9bfc33d 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -808,3 +808,47 @@ void test_repo_iterator__workdir_depth(void) expect_iterator_items(iter, 337, NULL, 337, NULL); git_iterator_free(iter); } + +void test_repo_iterator__fs(void) +{ + git_iterator *i; + static const char *expect_subdir[] = { + "current_file", + "modified_file", + "new_file", + NULL, + }; + + g_repo = cl_git_sandbox_init("status"); + + cl_git_pass(git_iterator_for_filesystem( + &i, "status/subdir", 0, NULL, NULL)); + expect_iterator_items(i, 3, expect_subdir, 3, expect_subdir); + git_iterator_free(i); +} + +void test_repo_iterator__fs2(void) +{ + git_iterator *i; + static const char *expect_subdir[] = { + "heads/br2", + "heads/dir", + "heads/master", + "heads/packed-test", + "heads/subtrees", + "heads/test", + "tags/e90810b", + "tags/foo/bar", + "tags/foo/foo/bar", + "tags/point_to_blob", + "tags/test", + NULL, + }; + + g_repo = cl_git_sandbox_init("testrepo"); + + cl_git_pass(git_iterator_for_filesystem( + &i, "testrepo/.git/refs", 0, NULL, NULL)); + expect_iterator_items(i, 11, expect_subdir, 11, expect_subdir); + git_iterator_free(i); +} From 71f85226eb5c2ef9e8c8ddb1943fc7fc1e6615ff Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 18 Apr 2013 11:11:38 -0700 Subject: [PATCH 014/384] Make workdir iterator use filesystem iterator This adds some hooks into the filesystem iterator so that the workdir iterator can just become a wrapper around it. Then we remove most of the workdir iterator code and just have it augment the filesystem iterator with skipping .git entries, updating the ignore stack, and checking for submodules. --- src/iterator.c | 557 ++++++++++++++----------------------------------- 1 file changed, 160 insertions(+), 397 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index dd8a6133d..6eee5a805 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -26,8 +26,6 @@ (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE) #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \ - (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \ - GITERR_CHECK_ALLOC(P); \ (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \ (P)->base.cb = &(P)->cb; \ ITERATOR_SET_CB(P,NAME_LC); \ @@ -148,7 +146,8 @@ int git_iterator_for_nothing( const char *start, const char *end) { - empty_iterator *i; + empty_iterator *i = git__calloc(1, sizeof(empty_iterator)); + GITERR_CHECK_ALLOC(i); #define empty_iterator__current empty_iterator__noop #define empty_iterator__advance empty_iterator__noop @@ -581,6 +580,9 @@ int git_iterator_for_tree( if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0) return error; + ti = git__calloc(1, sizeof(tree_iterator)); + GITERR_CHECK_ALLOC(ti); + ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree)); if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0) @@ -810,7 +812,8 @@ int git_iterator_for_index( const char *start, const char *end) { - index_iterator *ii; + index_iterator *ii = git__calloc(1, sizeof(index_iterator)); + GITERR_CHECK_ALLOC(ii); ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index)); @@ -840,7 +843,8 @@ struct fs_iterator_frame { size_t index; }; -typedef struct { +typedef struct fs_iterator fs_iterator; +struct fs_iterator { git_iterator base; git_iterator_callbacks cb; fs_iterator_frame *stack; @@ -848,7 +852,11 @@ typedef struct { git_buf path; size_t root_len; int depth; -} fs_iterator; + + int (*frame_added)(fs_iterator *self); + int (*frame_removed)(fs_iterator *self); + int (*entry_updated)(fs_iterator *self); +}; #define FS_MAX_DEPTH 100 @@ -867,15 +875,31 @@ static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi) return ff; } -static void fs_iterator__free_frame(fs_iterator_frame *ff) +static void fs_iterator__pop_frame( + fs_iterator *fi, fs_iterator_frame *ff, bool pop_last) { size_t i; git_path_with_stat *path; + fs_iterator_frame *ff_next = ff->next; + + if (fi && !ff_next && !pop_last) { + memset(&fi->entry, 0, sizeof(fi->entry)); + return; + } git_vector_foreach(&ff->entries, i, path) git__free(path); git_vector_free(&ff->entries); git__free(ff); + + if (!fi || fi->stack != ff) + return; + + fi->stack = ff_next; + fi->depth--; + + if (fi->frame_removed) + fi->frame_removed(fi); } static int fs_iterator__update_entry(fs_iterator *fi); @@ -913,14 +937,14 @@ static int fs_iterator__expand_dir(fs_iterator *fi) fi->base.start, fi->base.end, &ff->entries); if (error < 0 || ff->entries.length == 0) { - fs_iterator__free_frame(ff); + fs_iterator__pop_frame(NULL, ff, true); return GIT_ENOTFOUND; } if (++(fi->depth) > FS_MAX_DEPTH) { giterr_set(GITERR_REPOSITORY, "Directory nesting is too deep (%d)", fi->depth); - fs_iterator__free_frame(ff); + fs_iterator__pop_frame(NULL, ff, true); return -1; } @@ -929,6 +953,9 @@ static int fs_iterator__expand_dir(fs_iterator *fi) ff->next = fi->stack; fi->stack = ff; + if (fi->frame_added && (error = fi->frame_added(fi)) < 0) + return error; + return fs_iterator__update_entry(fi); } @@ -971,7 +998,7 @@ static int fs_iterator__advance_into( return error; } -static int fs_iterator__advance( +static int fs_iterator__advance_over( const git_index_entry **entry, git_iterator *self) { int error = 0; @@ -979,20 +1006,6 @@ static int fs_iterator__advance( fs_iterator_frame *ff; git_path_with_stat *next; - /* given include_trees & autoexpand, we might have to go into a tree */ - if (iterator__do_autoexpand(fi) && - fi->entry.path != NULL && - fi->entry.mode == GIT_FILEMODE_TREE) - { - error = fs_iterator__advance_into(entry, self); - - /* continue silently past empty directories if autoexpanding */ - if (error != GIT_ENOTFOUND) - return error; - giterr_clear(); - error = 0; - } - if (entry != NULL) *entry = NULL; @@ -1003,15 +1016,7 @@ static int fs_iterator__advance( if (next != NULL) break; - /* pop stack if anything is left to pop */ - if (!ff->next) { - memset(&fi->entry, 0, sizeof(fi->entry)); - return 0; - } - - fi->stack = ff->next; - fi->depth--; - fs_iterator__free_frame(ff); + fs_iterator__pop_frame(fi, ff, false); } error = fs_iterator__update_entry(fi); @@ -1022,6 +1027,26 @@ static int fs_iterator__advance( return error; } +static int fs_iterator__advance( + const git_index_entry **entry, git_iterator *self) +{ + fs_iterator *fi = (fs_iterator *)self; + + /* given include_trees & autoexpand, we might have to go into a tree */ + if (iterator__do_autoexpand(fi) && + fi->entry.path != NULL && + fi->entry.mode == GIT_FILEMODE_TREE) + { + int error = fs_iterator__advance_into(entry, self); + if (error != GIT_ENOTFOUND) + return error; + /* continue silently past empty directories if autoexpanding */ + giterr_clear(); + } + + return fs_iterator__advance_over(entry, self); +} + static int fs_iterator__seek(git_iterator *self, const char *prefix) { GIT_UNUSED(self); @@ -1037,11 +1062,8 @@ static int fs_iterator__reset( { fs_iterator *fi = (fs_iterator *)self; - while (fi->stack != NULL && fi->stack->next != NULL) { - fs_iterator_frame *ff = fi->stack; - fi->stack = ff->next; - fs_iterator__free_frame(ff); - } + while (fi->stack != NULL && fi->stack->next != NULL) + fs_iterator__pop_frame(fi, fi->stack, false); fi->depth = 0; if (iterator__reset_range(self, start, end) < 0) @@ -1056,11 +1078,8 @@ static void fs_iterator__free(git_iterator *self) { fs_iterator *fi = (fs_iterator *)self; - while (fi->stack != NULL) { - fs_iterator_frame *ff = fi->stack; - fi->stack = ff->next; - fs_iterator__free_frame(ff); - } + while (fi->stack != NULL) + fs_iterator__pop_frame(fi, fi->stack, true); git_buf_free(&fi->path); } @@ -1075,10 +1094,8 @@ static int fs_iterator__update_entry(fs_iterator *fi) if (!ps) return 0; - if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0) return -1; - if (iterator__past_end(fi, fi->path.ptr + fi->root_len)) return 0; @@ -1088,14 +1105,41 @@ static int fs_iterator__update_entry(fs_iterator *fi) /* need different mode here to keep directories during iteration */ fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode); - /* if this isn't a tree, then we're done */ - if (fi->entry.mode != GIT_FILEMODE_TREE) - return 0; + /* allow wrapper to check/update the entry (can force skip) */ + if (fi->entry_updated && + fi->entry_updated(fi) == GIT_ENOTFOUND) + return fs_iterator__advance_over(NULL, (git_iterator *)fi); - if (iterator__include_trees(fi)) - return 0; + /* if this is a tree and trees aren't included, then skip */ + if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) + return git_iterator_advance(NULL, (git_iterator *)fi); - return fs_iterator__advance(NULL, (git_iterator *)fi); + return 0; +} + +static int fs_iterator__initialize( + git_iterator **out, fs_iterator *fi, const char *root) +{ + int error; + + if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) { + git__free(fi); + return -1; + } + + fi->root_len = fi->path.size; + + if ((error = fs_iterator__expand_dir(fi)) == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + if (error) { + git_iterator_free((git_iterator *)fi); + fi = NULL; + } + + *out = (git_iterator *)fi; + return error; } int git_iterator_for_filesystem( @@ -1105,372 +1149,97 @@ int git_iterator_for_filesystem( const char *start, const char *end) { - int error; - fs_iterator *fi; + fs_iterator *fi = git__calloc(1, sizeof(fs_iterator)); + GITERR_CHECK_ALLOC(fi); ITERATOR_BASE_INIT(fi, fs, FS, NULL); if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0) fi->base.flags |= GIT_ITERATOR_IGNORE_CASE; - if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) { - git__free(fi); - return -1; - } - fi->root_len = fi->path.size; - - if ((error = fs_iterator__expand_dir(fi)) < 0) { - if (error != GIT_ENOTFOUND) - goto fail; - giterr_clear(); - } - - *out = (git_iterator *)fi; - return 0; - -fail: - git_iterator_free((git_iterator *)fi); - return error; + return fs_iterator__initialize(out, fi, root); } -#define WORKDIR_MAX_DEPTH 100 - -typedef struct workdir_iterator_frame workdir_iterator_frame; -struct workdir_iterator_frame { - workdir_iterator_frame *next; - git_vector entries; - size_t index; -}; - typedef struct { - git_iterator base; - git_iterator_callbacks cb; - workdir_iterator_frame *stack; + fs_iterator fi; git_ignores ignores; - git_index_entry entry; - git_buf path; - size_t root_len; int is_ignored; - int depth; } workdir_iterator; -GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps) +GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path) { - if (!ps) + size_t len; + + if (!path || (len = path->size) < 4) return false; - else { - const char *path = ps->path; - size_t len = ps->path_len; - if (len < 4) - return false; - if (path[len - 1] == '/') - len--; - if (tolower(path[len - 1]) != 't' || - tolower(path[len - 2]) != 'i' || - tolower(path[len - 3]) != 'g' || - tolower(path[len - 4]) != '.') - return false; - return (len == 4 || path[len - 5] == '/'); - } + if (path->ptr[len - 1] == '/') + len--; + + if (tolower(path->ptr[len - 1]) != 't' || + tolower(path->ptr[len - 2]) != 'i' || + tolower(path->ptr[len - 3]) != 'g' || + tolower(path->ptr[len - 4]) != '.') + return false; + + return (len == 4 || path->ptr[len - 5] == '/'); } -static workdir_iterator_frame *workdir_iterator__alloc_frame( - workdir_iterator *wi) +static int workdir_iterator__frame_added(fs_iterator *fi) { - workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame)); - git_vector_cmp entry_compare = CASESELECT( - iterator__ignore_case(wi), - git_path_with_stat_cmp_icase, git_path_with_stat_cmp); - - if (wf == NULL) - return NULL; - - if (git_vector_init(&wf->entries, 0, entry_compare) != 0) { - git__free(wf); - return NULL; - } - - return wf; -} - -static void workdir_iterator__free_frame(workdir_iterator_frame *wf) -{ - unsigned int i; - git_path_with_stat *path; - - git_vector_foreach(&wf->entries, i, path) - git__free(path); - git_vector_free(&wf->entries); - git__free(wf); -} - -static int workdir_iterator__update_entry(workdir_iterator *wi); - -static int workdir_iterator__entry_cmp(const void *i, const void *item) -{ - const workdir_iterator *wi = (const workdir_iterator *)i; - const git_path_with_stat *ps = item; - return wi->base.prefixcomp(wi->base.start, ps->path); -} - -static void workdir_iterator__seek_frame_start( - workdir_iterator *wi, workdir_iterator_frame *wf) -{ - if (!wf) - return; - - if (wi->base.start) - git_vector_bsearch2( - &wf->index, &wf->entries, workdir_iterator__entry_cmp, wi); - else - wf->index = 0; - - if (path_is_dotgit(git_vector_get(&wf->entries, wf->index))) - wf->index++; -} - -static int workdir_iterator__expand_dir(workdir_iterator *wi) -{ - int error; - workdir_iterator_frame *wf; - - wf = workdir_iterator__alloc_frame(wi); - GITERR_CHECK_ALLOC(wf); - - error = git_path_dirload_with_stat( - wi->path.ptr, wi->root_len, iterator__ignore_case(wi), - wi->base.start, wi->base.end, &wf->entries); - - if (error < 0 || wf->entries.length == 0) { - workdir_iterator__free_frame(wf); - return GIT_ENOTFOUND; - } - - if (++(wi->depth) > WORKDIR_MAX_DEPTH) { - giterr_set(GITERR_REPOSITORY, - "Working directory is too deep (%d)", wi->depth); - workdir_iterator__free_frame(wf); - return -1; - } - - workdir_iterator__seek_frame_start(wi, wf); - /* only push new ignores if this is not top level directory */ - if (wi->stack != NULL) { - ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/'); - (void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]); + if (fi->stack->next != NULL) { + workdir_iterator *wi = (workdir_iterator *)fi; + ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/'); + + (void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]); } - wf->next = wi->stack; - wi->stack = wf; - - return workdir_iterator__update_entry(wi); -} - -static int workdir_iterator__current( - const git_index_entry **entry, git_iterator *self) -{ - workdir_iterator *wi = (workdir_iterator *)self; - if (entry) - *entry = (wi->entry.path == NULL) ? NULL : &wi->entry; return 0; } -static int workdir_iterator__at_end(git_iterator *self) +static int workdir_iterator__frame_removed(fs_iterator *fi) { - return (((workdir_iterator *)self)->entry.path == NULL); + workdir_iterator *wi = (workdir_iterator *)fi; + git_ignore__pop_dir(&wi->ignores); + return 0; } -static int workdir_iterator__advance_into( - const git_index_entry **entry, git_iterator *iter) +static int workdir_iterator__entry_updated(fs_iterator *fi) { int error = 0; - workdir_iterator *wi = (workdir_iterator *)iter; + workdir_iterator *wi = (workdir_iterator *)fi; - iterator__clear_entry(entry); + /* skip over .git entries */ + if (workdir_path_is_dotgit(&fi->path)) + return GIT_ENOTFOUND; - /* workdir iterator will allow you to explicitly advance into a - * commit/submodule (as well as a tree) to avoid some cases where an - * entry is mislabeled as a submodule in the working directory - */ - if (wi->entry.path != NULL && - (wi->entry.mode == GIT_FILEMODE_TREE || - wi->entry.mode == GIT_FILEMODE_COMMIT)) - /* returns GIT_ENOTFOUND if the directory is empty */ - error = workdir_iterator__expand_dir(wi); + /* reset is_ignored since we haven't checked yet */ + wi->is_ignored = -1; - if (!error && entry) - error = workdir_iterator__current(entry, iter); + /* check if apparent tree entries are actually submodules */ + if (fi->entry.mode != GIT_FILEMODE_TREE) + return 0; - return error; -} - -static int workdir_iterator__advance( - const git_index_entry **entry, git_iterator *self) -{ - int error = 0; - workdir_iterator *wi = (workdir_iterator *)self; - workdir_iterator_frame *wf; - git_path_with_stat *next; - - /* given include_trees & autoexpand, we might have to go into a tree */ - if (iterator__do_autoexpand(wi) && - wi->entry.path != NULL && - wi->entry.mode == GIT_FILEMODE_TREE) - { - error = workdir_iterator__advance_into(entry, self); - - /* continue silently past empty directories if autoexpanding */ - if (error != GIT_ENOTFOUND) - return error; + error = git_submodule_lookup(NULL, fi->base.repo, fi->entry.path); + if (error < 0) giterr_clear(); - error = 0; + + /* mark submodule (or any dir with .git) as GITLINK and remove slash */ + if (!error || error == GIT_EEXISTS) { + fi->entry.mode = S_IFGITLINK; + fi->entry.path[strlen(fi->entry.path) - 1] = '\0'; } - if (entry != NULL) - *entry = NULL; - - while (wi->entry.path != NULL) { - wf = wi->stack; - next = git_vector_get(&wf->entries, ++wf->index); - - if (next != NULL) { - /* match git's behavior of ignoring anything named ".git" */ - if (path_is_dotgit(next)) - continue; - /* else found a good entry */ - break; - } - - /* pop stack if anything is left to pop */ - if (!wf->next) { - memset(&wi->entry, 0, sizeof(wi->entry)); - return 0; - } - - wi->stack = wf->next; - wi->depth--; - workdir_iterator__free_frame(wf); - git_ignore__pop_dir(&wi->ignores); - } - - error = workdir_iterator__update_entry(wi); - - if (!error && entry != NULL) - error = workdir_iterator__current(entry, self); - - return error; -} - -static int workdir_iterator__seek(git_iterator *self, const char *prefix) -{ - GIT_UNUSED(self); - GIT_UNUSED(prefix); - /* pop stack until matching prefix */ - /* find prefix item in current frame */ - /* push subdirectories as deep as possible while matching */ return 0; } -static int workdir_iterator__reset( - git_iterator *self, const char *start, const char *end) -{ - workdir_iterator *wi = (workdir_iterator *)self; - - while (wi->stack != NULL && wi->stack->next != NULL) { - workdir_iterator_frame *wf = wi->stack; - wi->stack = wf->next; - workdir_iterator__free_frame(wf); - git_ignore__pop_dir(&wi->ignores); - } - wi->depth = 0; - - if (iterator__reset_range(self, start, end) < 0) - return -1; - - workdir_iterator__seek_frame_start(wi, wi->stack); - - return workdir_iterator__update_entry(wi); -} - static void workdir_iterator__free(git_iterator *self) { workdir_iterator *wi = (workdir_iterator *)self; - - while (wi->stack != NULL) { - workdir_iterator_frame *wf = wi->stack; - wi->stack = wf->next; - workdir_iterator__free_frame(wf); - } - + fs_iterator__free(self); git_ignore__free(&wi->ignores); - git_buf_free(&wi->path); -} - -static int workdir_iterator__update_entry(workdir_iterator *wi) -{ - int error = 0; - git_path_with_stat *ps = - git_vector_get(&wi->stack->entries, wi->stack->index); - - git_buf_truncate(&wi->path, wi->root_len); - memset(&wi->entry, 0, sizeof(wi->entry)); - - if (!ps) - return 0; - - /* skip over .git entries */ - if (path_is_dotgit(ps)) - return workdir_iterator__advance(NULL, (git_iterator *)wi); - - if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0) - return -1; - - if (iterator__past_end(wi, wi->path.ptr + wi->root_len)) - return 0; - - wi->entry.path = ps->path; - - wi->is_ignored = -1; - - git_index_entry__init_from_stat(&wi->entry, &ps->st); - - /* need different mode here to keep directories during iteration */ - wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode); - - /* if this is a file type we don't handle, treat as ignored */ - if (wi->entry.mode == 0) { - wi->is_ignored = 1; - return 0; - } - - /* if this isn't a tree, then we're done */ - if (wi->entry.mode != GIT_FILEMODE_TREE) - return 0; - - /* detect submodules */ - error = git_submodule_lookup(NULL, wi->base.repo, wi->entry.path); - if (error == GIT_ENOTFOUND) - giterr_clear(); - - if (error == GIT_EEXISTS) /* if contains .git, treat as untracked submod */ - error = 0; - - /* if submodule, mark as GITLINK and remove trailing slash */ - if (!error) { - size_t len = strlen(wi->entry.path); - assert(wi->entry.path[len - 1] == '/'); - wi->entry.path[len - 1] = '\0'; - wi->entry.mode = S_IFGITLINK; - return 0; - } - - if (iterator__include_trees(wi)) - return 0; - - return workdir_iterator__advance(NULL, (git_iterator *)wi); } int git_iterator_for_workdir( @@ -1481,7 +1250,8 @@ int git_iterator_for_workdir( const char *end) { int error; - workdir_iterator *wi; + workdir_iterator *wi = git__calloc(1, sizeof(workdir_iterator)); + GITERR_CHECK_ALLOC(wi); assert(iter && repo); @@ -1489,32 +1259,24 @@ int git_iterator_for_workdir( repo, "scan working directory")) < 0) return error; - ITERATOR_BASE_INIT(wi, workdir, WORKDIR, repo); + /* initialize as an fs iterator then do overrides */ + ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo); - if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0) - goto fail; + wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR; + wi->fi.cb.free = workdir_iterator__free; + wi->fi.frame_added = workdir_iterator__frame_added; + wi->fi.frame_removed = workdir_iterator__frame_removed; + wi->fi.entry_updated = workdir_iterator__entry_updated; - if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 || - git_path_to_dir(&wi->path) < 0 || - git_ignore__for_path(repo, "", &wi->ignores) < 0) + if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 || + (error = git_ignore__for_path(repo, "", &wi->ignores)) < 0) { - git__free(wi); - return -1; - } - wi->root_len = wi->path.size; - - if ((error = workdir_iterator__expand_dir(wi)) < 0) { - if (error != GIT_ENOTFOUND) - goto fail; - giterr_clear(); + git_iterator_free((git_iterator *)wi); + return error; } - *iter = (git_iterator *)wi; - return 0; - -fail: - git_iterator_free((git_iterator *)wi); - return error; + return fs_iterator__initialize( + iter, (fs_iterator *)wi, git_repository_workdir(repo)); } @@ -1616,7 +1378,8 @@ bool git_iterator_current_is_ignored(git_iterator *iter) if (wi->is_ignored != -1) return (bool)(wi->is_ignored != 0); - if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0) + if (git_ignore__lookup( + &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0) wi->is_ignored = true; return (bool)wi->is_ignored; @@ -1641,10 +1404,10 @@ int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter) { workdir_iterator *wi = (workdir_iterator *)iter; - if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->entry.path) + if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->fi.entry.path) *path = NULL; else - *path = &wi->path; + *path = &wi->fi.path; return 0; } From fc57471a0c1834143ae6b18e65c1484c5c04e7e3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 18 Apr 2013 14:13:53 -0700 Subject: [PATCH 015/384] More filesystem iterator cleanup Renamed the callback functions and made some minor rearrangements to clean up the flow of some code. --- src/iterator.c | 83 +++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index 6eee5a805..a72f97eca 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -853,9 +853,9 @@ struct fs_iterator { size_t root_len; int depth; - int (*frame_added)(fs_iterator *self); - int (*frame_removed)(fs_iterator *self); - int (*entry_updated)(fs_iterator *self); + int (*enter_dir_cb)(fs_iterator *self); + int (*leave_dir_cb)(fs_iterator *self); + int (*update_entry_cb)(fs_iterator *self); }; #define FS_MAX_DEPTH 100 @@ -875,31 +875,34 @@ static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi) return ff; } -static void fs_iterator__pop_frame( - fs_iterator *fi, fs_iterator_frame *ff, bool pop_last) +static void fs_iterator__free_frame(fs_iterator_frame *ff) { size_t i; git_path_with_stat *path; - fs_iterator_frame *ff_next = ff->next; - - if (fi && !ff_next && !pop_last) { - memset(&fi->entry, 0, sizeof(fi->entry)); - return; - } git_vector_foreach(&ff->entries, i, path) git__free(path); git_vector_free(&ff->entries); git__free(ff); +} - if (!fi || fi->stack != ff) - return; +static void fs_iterator__pop_frame( + fs_iterator *fi, fs_iterator_frame *ff, bool pop_last) +{ + if (fi && fi->stack == ff) { + if (!ff->next && !pop_last) { + memset(&fi->entry, 0, sizeof(fi->entry)); + return; + } - fi->stack = ff_next; - fi->depth--; + if (fi->leave_dir_cb) + (void)fi->leave_dir_cb(fi); - if (fi->frame_removed) - fi->frame_removed(fi); + fi->stack = ff->next; + fi->depth--; + } + + fs_iterator__free_frame(ff); } static int fs_iterator__update_entry(fs_iterator *fi); @@ -929,6 +932,12 @@ static int fs_iterator__expand_dir(fs_iterator *fi) int error; fs_iterator_frame *ff; + if (fi->depth > FS_MAX_DEPTH) { + giterr_set(GITERR_REPOSITORY, + "Directory nesting is too deep (%d)", fi->depth); + return -1; + } + ff = fs_iterator__alloc_frame(fi); GITERR_CHECK_ALLOC(ff); @@ -937,23 +946,17 @@ static int fs_iterator__expand_dir(fs_iterator *fi) fi->base.start, fi->base.end, &ff->entries); if (error < 0 || ff->entries.length == 0) { - fs_iterator__pop_frame(NULL, ff, true); + fs_iterator__free_frame(ff); return GIT_ENOTFOUND; } - if (++(fi->depth) > FS_MAX_DEPTH) { - giterr_set(GITERR_REPOSITORY, - "Directory nesting is too deep (%d)", fi->depth); - fs_iterator__pop_frame(NULL, ff, true); - return -1; - } - fs_iterator__seek_frame_start(fi, ff); ff->next = fi->stack; fi->stack = ff; + fi->depth++; - if (fi->frame_added && (error = fi->frame_added(fi)) < 0) + if (fi->enter_dir_cb && (error = fi->enter_dir_cb(fi)) < 0) return error; return fs_iterator__update_entry(fi); @@ -1106,8 +1109,8 @@ static int fs_iterator__update_entry(fs_iterator *fi) fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode); /* allow wrapper to check/update the entry (can force skip) */ - if (fi->entry_updated && - fi->entry_updated(fi) == GIT_ENOTFOUND) + if (fi->update_entry_cb && + fi->update_entry_cb(fi) == GIT_ENOTFOUND) return fs_iterator__advance_over(NULL, (git_iterator *)fi); /* if this is a tree and trees aren't included, then skip */ @@ -1126,7 +1129,6 @@ static int fs_iterator__initialize( git__free(fi); return -1; } - fi->root_len = fi->path.size; if ((error = fs_iterator__expand_dir(fi)) == GIT_ENOTFOUND) { @@ -1186,7 +1188,7 @@ GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path) return (len == 4 || path->ptr[len - 5] == '/'); } -static int workdir_iterator__frame_added(fs_iterator *fi) +static int workdir_iterator__enter_dir(fs_iterator *fi) { /* only push new ignores if this is not top level directory */ if (fi->stack->next != NULL) { @@ -1199,14 +1201,14 @@ static int workdir_iterator__frame_added(fs_iterator *fi) return 0; } -static int workdir_iterator__frame_removed(fs_iterator *fi) +static int workdir_iterator__leave_dir(fs_iterator *fi) { workdir_iterator *wi = (workdir_iterator *)fi; git_ignore__pop_dir(&wi->ignores); return 0; } -static int workdir_iterator__entry_updated(fs_iterator *fi) +static int workdir_iterator__update_entry(fs_iterator *fi) { int error = 0; workdir_iterator *wi = (workdir_iterator *)fi; @@ -1243,7 +1245,7 @@ static void workdir_iterator__free(git_iterator *self) } int git_iterator_for_workdir( - git_iterator **iter, + git_iterator **out, git_repository *repo, git_iterator_flag_t flags, const char *start, @@ -1253,7 +1255,7 @@ int git_iterator_for_workdir( workdir_iterator *wi = git__calloc(1, sizeof(workdir_iterator)); GITERR_CHECK_ALLOC(wi); - assert(iter && repo); + assert(out && repo); if ((error = git_repository__ensure_not_bare( repo, "scan working directory")) < 0) @@ -1262,11 +1264,11 @@ int git_iterator_for_workdir( /* initialize as an fs iterator then do overrides */ ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo); - wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR; - wi->fi.cb.free = workdir_iterator__free; - wi->fi.frame_added = workdir_iterator__frame_added; - wi->fi.frame_removed = workdir_iterator__frame_removed; - wi->fi.entry_updated = workdir_iterator__entry_updated; + wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR; + wi->fi.cb.free = workdir_iterator__free; + wi->fi.enter_dir_cb = workdir_iterator__enter_dir; + wi->fi.leave_dir_cb = workdir_iterator__leave_dir; + wi->fi.update_entry_cb = workdir_iterator__update_entry; if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 || (error = git_ignore__for_path(repo, "", &wi->ignores)) < 0) @@ -1275,8 +1277,7 @@ int git_iterator_for_workdir( return error; } - return fs_iterator__initialize( - iter, (fs_iterator *)wi, git_repository_workdir(repo)); + return fs_iterator__initialize(out, &wi->fi, git_repository_workdir(repo)); } From 627d590819efa7f43b605ce1a22278c0c6b42516 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 18 Apr 2013 14:14:22 -0700 Subject: [PATCH 016/384] More filesystem iterator tests Refactors the helper function that builds a directory hierarchy and then made use of it to try more variations on filesystem iterator tests. --- tests-clar/repo/iterator.c | 150 +++++++++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 39 deletions(-) diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index ef9bfc33d..2e53c48d7 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -755,47 +755,52 @@ void test_repo_iterator__workdir_icase(void) git_iterator_free(i); } -void test_repo_iterator__workdir_depth(void) +static void build_workdir_tree(const char *root, int dirs, int subs) { int i, j; + char buf[64], sub[64]; + + for (i = 0; i < dirs; ++i) { + if (i % 2 == 0) { + p_snprintf(buf, sizeof(buf), "%s/dir%02d", root, i); + cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH)); + + p_snprintf(buf, sizeof(buf), "%s/dir%02d/file", root, i); + cl_git_mkfile(buf, buf); + buf[strlen(buf) - 5] = '\0'; + } else { + p_snprintf(buf, sizeof(buf), "%s/DIR%02d", root, i); + cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH)); + } + + for (j = 0; j < subs; ++j) { + switch (j % 4) { + case 0: p_snprintf(sub, sizeof(sub), "%s/sub%02d", buf, j); break; + case 1: p_snprintf(sub, sizeof(sub), "%s/sUB%02d", buf, j); break; + case 2: p_snprintf(sub, sizeof(sub), "%s/Sub%02d", buf, j); break; + case 3: p_snprintf(sub, sizeof(sub), "%s/SUB%02d", buf, j); break; + } + cl_git_pass(git_futils_mkdir(sub, NULL, 0775, GIT_MKDIR_PATH)); + + if (j % 2 == 0) { + size_t sublen = strlen(sub); + memcpy(&sub[sublen], "/file", sizeof("/file")); + cl_git_mkfile(sub, sub); + sub[sublen] = '\0'; + } + } + } +} + +void test_repo_iterator__workdir_depth(void) +{ git_iterator *iter; - char buf[64]; g_repo = cl_git_sandbox_init("icase"); - for (i = 0; i < 10; ++i) { - p_snprintf(buf, sizeof(buf), "icase/dir%02d", i); - cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH)); - - if (i % 2 == 0) { - p_snprintf(buf, sizeof(buf), "icase/dir%02d/file", i); - cl_git_mkfile(buf, buf); - } - - for (j = 0; j < 10; ++j) { - p_snprintf(buf, sizeof(buf), "icase/dir%02d/sub%02d", i, j); - cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH)); - - if (j % 2 == 0) { - p_snprintf( - buf, sizeof(buf), "icase/dir%02d/sub%02d/file", i, j); - cl_git_mkfile(buf, buf); - } - } - } - - for (i = 1; i < 3; ++i) { - for (j = 0; j < 50; ++j) { - p_snprintf(buf, sizeof(buf), "icase/dir%02d/sub01/moar%02d", i, j); - cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH)); - - if (j % 2 == 0) { - p_snprintf(buf, sizeof(buf), - "icase/dir%02d/sub01/moar%02d/file", i, j); - cl_git_mkfile(buf, buf); - } - } - } + build_workdir_tree("icase", 10, 10); + build_workdir_tree("icase/dir01/Sub01", 50, 0); + build_workdir_tree("icase/DIR02/Sub01", 50, 0); /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_workdir(&iter, g_repo, 0, NULL, NULL)); @@ -812,8 +817,42 @@ void test_repo_iterator__workdir_depth(void) void test_repo_iterator__fs(void) { git_iterator *i; - static const char *expect_subdir[] = { + static const char *expect_base[] = { + "DIR01/Sub02/file", + "DIR01/sub00/file", "current_file", + "dir00/Sub02/file", + "dir00/file", + "dir00/sub00/file", + "modified_file", + "new_file", + NULL, + }; + static const char *expect_trees[] = { + "DIR01/", + "DIR01/SUB03/", + "DIR01/Sub02/", + "DIR01/Sub02/file", + "DIR01/sUB01/", + "DIR01/sub00/", + "DIR01/sub00/file", + "current_file", + "dir00/", + "dir00/SUB03/", + "dir00/Sub02/", + "dir00/Sub02/file", + "dir00/file", + "dir00/sUB01/", + "dir00/sub00/", + "dir00/sub00/file", + "modified_file", + "new_file", + NULL, + }; + static const char *expect_noauto[] = { + "DIR01/", + "current_file", + "dir00/", "modified_file", "new_file", NULL, @@ -821,16 +860,49 @@ void test_repo_iterator__fs(void) g_repo = cl_git_sandbox_init("status"); + build_workdir_tree("status/subdir", 2, 4); + cl_git_pass(git_iterator_for_filesystem( &i, "status/subdir", 0, NULL, NULL)); - expect_iterator_items(i, 3, expect_subdir, 3, expect_subdir); + expect_iterator_items(i, 8, expect_base, 8, expect_base); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_filesystem( + &i, "status/subdir", GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + expect_iterator_items(i, 18, expect_trees, 18, expect_trees); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_filesystem( + &i, "status/subdir", GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + expect_iterator_items(i, 5, expect_noauto, 18, expect_trees); + git_iterator_free(i); + + git__tsort((void **)expect_base, 8, (git__tsort_cmp)git__strcasecmp); + git__tsort((void **)expect_trees, 18, (git__tsort_cmp)git__strcasecmp); + git__tsort((void **)expect_noauto, 5, (git__tsort_cmp)git__strcasecmp); + + cl_git_pass(git_iterator_for_filesystem( + &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE, NULL, NULL)); + expect_iterator_items(i, 8, expect_base, 8, expect_base); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_filesystem( + &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE | + GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + expect_iterator_items(i, 18, expect_trees, 18, expect_trees); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_filesystem( + &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE | + GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + expect_iterator_items(i, 5, expect_noauto, 18, expect_trees); git_iterator_free(i); } void test_repo_iterator__fs2(void) { git_iterator *i; - static const char *expect_subdir[] = { + static const char *expect_base[] = { "heads/br2", "heads/dir", "heads/master", @@ -849,6 +921,6 @@ void test_repo_iterator__fs2(void) cl_git_pass(git_iterator_for_filesystem( &i, "testrepo/.git/refs", 0, NULL, NULL)); - expect_iterator_items(i, 11, expect_subdir, 11, expect_subdir); + expect_iterator_items(i, 11, expect_base, 11, expect_base); git_iterator_free(i); } From 2aee1aa4165583cf77815df404d87818d53b72dc Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 18 Apr 2013 14:35:13 -0700 Subject: [PATCH 017/384] Fix uninitialized var warnings --- src/index.c | 2 +- src/revparse.c | 2 +- tests-clar/repo/iterator.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.c b/src/index.c index 6290ec4e8..2afd28158 100644 --- a/src/index.c +++ b/src/index.c @@ -1345,7 +1345,7 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer static int parse_index(git_index *index, const char *buffer, size_t buffer_size) { unsigned int i; - struct index_header header; + struct index_header header = { 0 }; git_oid checksum_calculated, checksum_expected; #define seek_forward(_increase) { \ diff --git a/src/revparse.c b/src/revparse.c index 74635ed04..8a22a04f3 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -16,7 +16,7 @@ static int disambiguate_refname(git_reference **out, git_repository *repo, const char *refname) { - int error, i; + int error = 0, i; bool fallbackmode = true; git_reference *ref; git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT; diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 2e53c48d7..9118dd17e 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -422,7 +422,7 @@ static void build_test_tree( git_treebuilder *builder; const char *scan = fmt, *next; char type, delimiter; - git_filemode_t mode; + git_filemode_t mode = GIT_FILEMODE_BLOB; git_buf name = GIT_BUF_INIT; va_list arglist; From 9ea29c8f1dd155cf62fc6c87edaeb9984da72f72 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 18 Apr 2013 14:41:16 -0700 Subject: [PATCH 018/384] Fix fs iterator test on case sensitive fs --- tests-clar/repo/iterator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 9118dd17e..ab460735c 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -799,8 +799,8 @@ void test_repo_iterator__workdir_depth(void) g_repo = cl_git_sandbox_init("icase"); build_workdir_tree("icase", 10, 10); - build_workdir_tree("icase/dir01/Sub01", 50, 0); - build_workdir_tree("icase/DIR02/Sub01", 50, 0); + build_workdir_tree("icase/DIR01/sUB01", 50, 0); + build_workdir_tree("icase/dir02/sUB01", 50, 0); /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_workdir(&iter, g_repo, 0, NULL, NULL)); From 38fd8121a2a87cc0da405b50f4439ca6578dcff5 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 18 Apr 2013 14:48:20 -0700 Subject: [PATCH 019/384] Fix win64 warnings --- src/branch.c | 2 +- tests-clar/refdb/inmemory.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/branch.c b/src/branch.c index e7088790e..956286b74 100644 --- a/src/branch.c +++ b/src/branch.c @@ -377,7 +377,7 @@ int git_branch_remote_name(char *buffer, size_t buffer_len, git_repository *repo if (buffer) git_buf_copy_cstr(buffer, buffer_len, &buf); - ret = git_buf_len(&buf) + 1; + ret = (int)git_buf_len(&buf) + 1; git_buf_free(&buf); return ret; diff --git a/tests-clar/refdb/inmemory.c b/tests-clar/refdb/inmemory.c index 2cccd8eb2..ca721e977 100644 --- a/tests-clar/refdb/inmemory.c +++ b/tests-clar/refdb/inmemory.c @@ -160,7 +160,7 @@ void test_refdb_inmemory__foreach(void) cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0)); cl_git_pass(git_reference_foreach(repo, GIT_REF_LISTALL, foreach_test, &i)); - cl_assert_equal_i(i, 3); + cl_assert_equal_i(3, (int)i); git_reference_free(write1); git_reference_free(write2); @@ -207,7 +207,7 @@ void test_refdb_inmemory__delete(void) git_reference_free(write3); cl_git_pass(git_reference_foreach(repo, GIT_REF_LISTALL, delete_test, &i)); - cl_assert_equal_i(i, 1); + cl_assert_equal_i(1, (int)i); git_reference_free(write2); } From 1af80a676613882b3e04e82874c6e7c8c14b5f49 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 18 Apr 2013 16:13:52 -0700 Subject: [PATCH 020/384] Fix workdir iterator leak When attempting to create a workdir iterator for a bare repo, don't leak the iterator structure. --- src/iterator.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index a72f97eca..ff08c1ce0 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1252,16 +1252,14 @@ int git_iterator_for_workdir( const char *end) { int error; - workdir_iterator *wi = git__calloc(1, sizeof(workdir_iterator)); - GITERR_CHECK_ALLOC(wi); + workdir_iterator *wi; - assert(out && repo); - - if ((error = git_repository__ensure_not_bare( - repo, "scan working directory")) < 0) - return error; + if (git_repository__ensure_not_bare(repo, "scan working directory") < 0) + return GIT_EBAREREPO; /* initialize as an fs iterator then do overrides */ + wi = git__calloc(1, sizeof(workdir_iterator)); + GITERR_CHECK_ALLOC(wi); ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo); wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR; From 743048f1e9d9c853ffac80093a4814a1ac7d9c62 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Fri, 19 Apr 2013 10:29:50 -0700 Subject: [PATCH 021/384] Fix some minor issues --- README.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fdddc5ca1..a2a18765a 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ release its source code. * Archives: * Website: * API documentation: +* IRC: #libgit2 on irc.freenode.net. What It Can Do ================================== @@ -155,15 +156,7 @@ we can add it to the list. How Can I Contribute? ================================== -Fork libgit2/libgit2 on GitHub, add your improvement, push it to a branch -in your fork named for the topic, send a pull request. If you change the -API or make other large changes, make a note of it in docs/rel-notes/ in a -file named after the next release. - -You can also file bugs or feature requests under the libgit2 project on -GitHub, or join us on the mailing list by sending an email to: - -libgit2@librelist.com +Check the [contribution guidelines](CONTRIBUTING.md). License From a29c6b5f47676d864af1e78c7927bc3cb2b329d7 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 19 Apr 2013 23:51:18 +0200 Subject: [PATCH 022/384] odb: Do not allow duplicate on-disk backends --- src/odb.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/odb.c b/src/odb.c index c98df247c..1c7969fcb 100644 --- a/src/odb.c +++ b/src/odb.c @@ -29,7 +29,8 @@ typedef struct { git_odb_backend *backend; int priority; - int is_alternate; + bool is_alternate; + ino_t disk_inode; } backend_internal; size_t git_odb__cache_size = GIT_DEFAULT_CACHE_SIZE; @@ -365,7 +366,9 @@ int git_odb_new(git_odb **out) return 0; } -static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int priority, int is_alternate) +static int add_backend_internal( + git_odb *odb, git_odb_backend *backend, + int priority, bool is_alternate, ino_t disk_inode) { backend_internal *internal; @@ -382,6 +385,7 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio internal->backend = backend; internal->priority = priority; internal->is_alternate = is_alternate; + internal->disk_inode = disk_inode; if (git_vector_insert(&odb->backends, internal) < 0) { git__free(internal); @@ -395,26 +399,41 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority) { - return add_backend_internal(odb, backend, priority, 0); + return add_backend_internal(odb, backend, priority, false, 0); } int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority) { - return add_backend_internal(odb, backend, priority, 1); + return add_backend_internal(odb, backend, priority, true, 0); } -static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates, int alternate_depth) +static int add_default_backends( + git_odb *db, const char *objects_dir, + bool as_alternates, int alternate_depth) { + size_t i; + struct stat st; git_odb_backend *loose, *packed; + if (p_stat(objects_dir, &st) < 0) { + giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir); + return -1; + } + + for (i = 0; i < db->backends.length; ++i) { + backend_internal *backend = git_vector_get(&db->backends, i); + if (backend->disk_inode == st.st_ino) + return 0; + } + /* add the loose object backend */ if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 || - add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates) < 0) + add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, st.st_ino) < 0) return -1; /* add the packed file backend */ if (git_odb_backend_pack(&packed, objects_dir) < 0 || - add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0) + add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates, st.st_ino) < 0) return -1; return load_alternates(db, objects_dir, alternate_depth); @@ -464,7 +483,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ alternate = git_buf_cstr(&alternates_path); } - if ((result = add_default_backends(odb, alternate, 1, alternate_depth + 1)) < 0) + if ((result = add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0) break; } @@ -476,7 +495,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ int git_odb_add_disk_alternate(git_odb *odb, const char *path) { - return add_default_backends(odb, path, 1, 0); + return add_default_backends(odb, path, true, 0); } int git_odb_open(git_odb **out, const char *objects_dir) From 4a38143c93dc705bc40109e3f79d7fac722d44d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 19 Apr 2013 23:55:37 +0200 Subject: [PATCH 023/384] remote: specify what values direction can mean in git_remote_connect() This fixes #1487 --- include/git2/remote.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 6f36a3999..9494a8b01 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -184,7 +184,8 @@ GIT_EXTERN(const git_refspec *) git_remote_pushspec(const git_remote *remote); * starts up a specific binary which can only do the one or the other. * * @param remote the remote to connect to - * @param direction whether you want to receive or send data + * @param direction GIT_DIRECTION_FETCH if you want to fetch or + * GIT_DIRECTION_PUSH if you want to push * @return 0 or an error code */ GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction); From 4e4eab52f7c5062ea21ea278a38e48700e753883 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 19 Apr 2013 18:19:53 -0500 Subject: [PATCH 024/384] alloc doesn't take a refdb; git_refdb_free nicely in the tests --- include/git2/refdb.h | 2 -- include/git2/refdb_backend.h | 3 +-- src/refdb.c | 15 +++++++++++++-- src/refdb_fs.c | 11 ++++------- src/refs.c | 25 ++++++++++++------------- tests-clar/refdb/inmemory.c | 5 +++-- tests-clar/refdb/testdb.c | 11 ++--------- tests-clar/refs/delete.c | 1 + tests-clar/refs/pack.c | 1 + tests-clar/refs/rename.c | 1 + 10 files changed, 38 insertions(+), 37 deletions(-) diff --git a/include/git2/refdb.h b/include/git2/refdb.h index 76b8fda0d..0e3ec5eaf 100644 --- a/include/git2/refdb.h +++ b/include/git2/refdb.h @@ -32,13 +32,11 @@ GIT_BEGIN_DECL * @return the created git_reference or NULL on error */ GIT_EXTERN(git_reference *) git_reference__alloc( - git_refdb *refdb, const char *name, const git_oid *oid, const git_oid *peel); GIT_EXTERN(git_reference *) git_reference__alloc_symbolic( - git_refdb *refdb, const char *name, const char *target); diff --git a/include/git2/refdb_backend.h b/include/git2/refdb_backend.h index bf33817d6..20eb6a9dd 100644 --- a/include/git2/refdb_backend.h +++ b/include/git2/refdb_backend.h @@ -101,8 +101,7 @@ struct git_refdb_backend { */ GIT_EXTERN(int) git_refdb_backend_fs( struct git_refdb_backend **backend_out, - git_repository *repo, - git_refdb *refdb); + git_repository *repo); GIT_END_DECL diff --git a/src/refdb.c b/src/refdb.c index d9b73c6e7..2a0fd702c 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -45,7 +45,7 @@ int git_refdb_open(git_refdb **out, git_repository *repo) return -1; /* Add the default (filesystem) backend */ - if (git_refdb_backend_fs(&dir, repo, db) < 0) { + if (git_refdb_backend_fs(&dir, repo) < 0) { git_refdb_free(db); return -1; } @@ -111,9 +111,20 @@ int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name) int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name) { + git_reference *ref; + int error; + assert(db && db->backend && ref_name); - return db->backend->lookup(out, db->backend, ref_name); + *out = NULL; + + if ((error = db->backend->lookup(&ref, db->backend, ref_name)) == 0) + { + ref->db = db; + *out = ref; + } + + return error; } int git_refdb_foreach( diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 784749fd3..56b2b6a99 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -42,7 +42,6 @@ typedef struct refdb_fs_backend { git_repository *repo; const char *path; - git_refdb *refdb; git_refcache refcache; } refdb_fs_backend; @@ -430,12 +429,12 @@ static int loose_lookup( goto done; } - *out = git_reference__alloc_symbolic(backend->refdb, ref_name, target); + *out = git_reference__alloc_symbolic(ref_name, target); } else { if ((error = loose_parse_oid(&oid, &ref_file)) < 0) goto done; - *out = git_reference__alloc(backend->refdb, ref_name, &oid, NULL); + *out = git_reference__alloc(ref_name, &oid, NULL); } if (*out == NULL) @@ -484,7 +483,7 @@ static int packed_lookup( if ((error = packed_map_entry(&entry, &pos, backend, ref_name)) < 0) return error; - if ((*out = git_reference__alloc(backend->refdb, ref_name, + if ((*out = git_reference__alloc(ref_name, &entry->oid, &entry->peel)) == NULL) return -1; @@ -999,8 +998,7 @@ static void refdb_fs_backend__free(git_refdb_backend *_backend) int git_refdb_backend_fs( git_refdb_backend **backend_out, - git_repository *repository, - git_refdb *refdb) + git_repository *repository) { refdb_fs_backend *backend; @@ -1009,7 +1007,6 @@ int git_refdb_backend_fs( backend->repo = repository; backend->path = repository->path_repository; - backend->refdb = refdb; backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; diff --git a/src/refs.c b/src/refs.c index 29d1c4fa9..5b5812aae 100644 --- a/src/refs.c +++ b/src/refs.c @@ -31,7 +31,7 @@ enum { GIT_PACKREF_WAS_LOOSE = 2 }; -static git_reference *alloc_ref(git_refdb *refdb, const char *name) +static git_reference *alloc_ref(const char *name) { git_reference *ref; size_t namelen = strlen(name); @@ -39,22 +39,20 @@ static git_reference *alloc_ref(git_refdb *refdb, const char *name) if ((ref = git__calloc(1, sizeof(git_reference) + namelen + 1)) == NULL) return NULL; - ref->db = refdb; memcpy(ref->name, name, namelen + 1); return ref; } git_reference *git_reference__alloc_symbolic( - git_refdb *refdb, const char *name, const char *target) { git_reference *ref; - assert(refdb && name && target); + assert(name && target); - ref = alloc_ref(refdb, name); + ref = alloc_ref(name); if (!ref) return NULL; @@ -69,16 +67,15 @@ git_reference *git_reference__alloc_symbolic( } git_reference *git_reference__alloc( - git_refdb *refdb, const char *name, const git_oid *oid, const git_oid *peel) { git_reference *ref; - assert(refdb && name && oid); + assert(name && oid); - ref = alloc_ref(refdb, name); + ref = alloc_ref(name); if (!ref) return NULL; @@ -368,12 +365,13 @@ static int reference__create( if (oid != NULL) { assert(symbolic == NULL); - ref = git_reference__alloc(refdb, name, oid, NULL); + ref = git_reference__alloc(name, oid, NULL); } else { - ref = git_reference__alloc_symbolic(refdb, name, symbolic); + ref = git_reference__alloc_symbolic(name, symbolic); } GITERR_CHECK_ALLOC(ref); + ref->db = refdb; if ((error = git_refdb_write(refdb, ref)) < 0) { git_reference_free(ref); @@ -490,10 +488,9 @@ int git_reference_rename( * Create the new reference. */ if (ref->type == GIT_REF_OID) { - result = git_reference__alloc(ref->db, new_name, - &ref->target.oid, &ref->peel); + result = git_reference__alloc(new_name, &ref->target.oid, &ref->peel); } else if (ref->type == GIT_REF_SYMBOLIC) { - result = git_reference__alloc_symbolic(ref->db, new_name, ref->target.symbolic); + result = git_reference__alloc_symbolic(new_name, ref->target.symbolic); } else { assert(0); } @@ -501,6 +498,8 @@ int git_reference_rename( if (result == NULL) return -1; + result->db = ref->db; + /* Check if we have to update HEAD. */ if ((error = git_branch_is_head(ref)) < 0) goto on_error; diff --git a/tests-clar/refdb/inmemory.c b/tests-clar/refdb/inmemory.c index ca721e977..ea76172cf 100644 --- a/tests-clar/refdb/inmemory.c +++ b/tests-clar/refdb/inmemory.c @@ -6,7 +6,6 @@ #define TEST_REPO_PATH "testrepo" static git_repository *repo; -static git_refdb *refdb; static git_refdb_backend *refdb_backend; int unlink_ref(void *payload, git_buf *file) @@ -52,6 +51,8 @@ int ref_file_foreach(git_repository *repo, int (* cb)(void *payload, git_buf *fi void test_refdb_inmemory__initialize(void) { + git_refdb *refdb; + git_buf repo_refs_dir = GIT_BUF_INIT; repo = cl_git_sandbox_init(TEST_REPO_PATH); @@ -60,10 +61,10 @@ void test_refdb_inmemory__initialize(void) cl_git_pass(refdb_backend_test(&refdb_backend, repo)); cl_git_pass(git_refdb_set_backend(refdb, refdb_backend)); - ref_file_foreach(repo, unlink_ref); git_buf_free(&repo_refs_dir); + git_refdb_free(refdb); } void test_refdb_inmemory__cleanup(void) diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index e60f6790e..4bca39878 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -10,7 +10,6 @@ typedef struct refdb_test_backend { git_refdb_backend parent; git_repository *repo; - git_refdb *refdb; git_vector refs; } refdb_test_backend; @@ -100,10 +99,10 @@ static int refdb_test_backend__lookup( if (strcmp(entry->name, ref_name) == 0) { if (entry->type == GIT_REF_OID) { - *out = git_reference__alloc(backend->refdb, ref_name, + *out = git_reference__alloc(ref_name, &entry->target.oid, NULL); } else if (entry->type == GIT_REF_SYMBOLIC) { - *out = git_reference__alloc_symbolic(backend->refdb, ref_name, + *out = git_reference__alloc_symbolic(ref_name, entry->target.symbolic); } @@ -195,11 +194,6 @@ int refdb_backend_test( git_repository *repo) { refdb_test_backend *backend; - git_refdb *refdb; - int error = 0; - - if ((error = git_repository_refdb(&refdb, repo)) < 0) - return error; backend = git__calloc(1, sizeof(refdb_test_backend)); GITERR_CHECK_ALLOC(backend); @@ -207,7 +201,6 @@ int refdb_backend_test( git_vector_init(&backend->refs, 0, ref_name_cmp); backend->repo = repo; - backend->refdb = refdb; backend->parent.exists = &refdb_test_backend__exists; backend->parent.lookup = &refdb_test_backend__lookup; diff --git a/tests-clar/refs/delete.c b/tests-clar/refs/delete.c index ac517d869..053f41229 100644 --- a/tests-clar/refs/delete.c +++ b/tests-clar/refs/delete.c @@ -88,4 +88,5 @@ void test_refs_delete__packed_only(void) /* This should pass */ cl_git_pass(git_reference_delete(ref)); git_reference_free(ref); + git_refdb_free(refdb); } diff --git a/tests-clar/refs/pack.c b/tests-clar/refs/pack.c index 973abae30..412c4c5fd 100644 --- a/tests-clar/refs/pack.c +++ b/tests-clar/refs/pack.c @@ -25,6 +25,7 @@ static void packall(void) cl_git_pass(git_repository_refdb(&refdb, g_repo)); cl_git_pass(git_refdb_compress(refdb)); + git_refdb_free(refdb); } void test_refs_pack__empty(void) diff --git a/tests-clar/refs/rename.c b/tests-clar/refs/rename.c index e39abeb05..5ab84c48e 100644 --- a/tests-clar/refs/rename.c +++ b/tests-clar/refs/rename.c @@ -284,6 +284,7 @@ void test_refs_rename__overwrite(void) git_reference_free(ref_one); git_reference_free(ref_one_new); git_reference_free(ref_two); + git_refdb_free(refdb); } From 8f24e65ff6f7573bc6778f5bbea7119fc9b7b626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 20 Apr 2013 16:20:21 +0200 Subject: [PATCH 025/384] Plug a couple of leaks --- tests-clar/stash/drop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests-clar/stash/drop.c b/tests-clar/stash/drop.c index 12f922630..60b3c72e0 100644 --- a/tests-clar/stash/drop.c +++ b/tests-clar/stash/drop.c @@ -146,6 +146,8 @@ void retrieve_top_stash_id(git_oid *out) cl_git_pass(git_reference_name_to_id(out, repo, GIT_REFS_STASH_FILE)); cl_assert_equal_i(true, git_oid_cmp(out, git_object_id(top_stash)) == 0); + + git_object_free(top_stash); } void test_stash_drop__dropping_the_top_stash_updates_the_stash_reference(void) @@ -165,4 +167,6 @@ void test_stash_drop__dropping_the_top_stash_updates_the_stash_reference(void) retrieve_top_stash_id(&oid); cl_git_pass(git_oid_cmp(&oid, git_object_id(next_top_stash))); + + git_object_free(next_top_stash); } From e5a27f039ee3ae1291fd5084707c3f9c168f10ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 20 Apr 2013 15:25:39 +0200 Subject: [PATCH 026/384] config: allow setting multivars when none exist yet Adding a multivar when there are no variables with that name set should set the variable instead of failing. --- src/config_file.c | 4 +++- tests-clar/config/multivar.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/config_file.c b/src/config_file.c index 8b51ab21b..01559221e 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -481,8 +481,10 @@ static int config_set_multivar( pos = git_strmap_lookup_index(b->values, key); if (!git_strmap_valid_index(b->values, pos)) { + /* If we don't have it, behave like a normal set */ + result = config_set(cfg, name, value); git__free(key); - return GIT_ENOTFOUND; + return result; } var = git_strmap_value_at(b->values, pos); diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c index 26537e20a..0bda6bcec 100644 --- a/tests-clar/config/multivar.c +++ b/tests-clar/config/multivar.c @@ -97,6 +97,22 @@ void test_config_multivar__add(void) git_config_free(cfg); } +void test_config_multivar__add_new(void) +{ + const char *var = "a.brand.new"; + git_config *cfg; + int n; + + cl_git_pass(git_config_open_ondisk(&cfg, "config/config11")); + + cl_git_pass(git_config_set_multivar(cfg, var, "", "variable")); + n = 0; + cl_git_pass(git_config_get_multivar(cfg, var, NULL, cb, &n)); + cl_assert(n == 1); + + git_config_free(cfg); +} + void test_config_multivar__replace(void) { git_config *cfg; From 4330ab26b53c0e1bf8cbb5e65704f65e3d116eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 20 Apr 2013 04:43:28 +0200 Subject: [PATCH 027/384] remote: handle multiple refspecs A remote can have a multitude of refspecs. Up to now our git_remote's have supported a single one for each fetch and push out of simplicity to get something working. Let the remotes and internal code know about multiple remotes and get the tests passing with them. Instead of setting a refspec, the external users can clear all and add refspecs. This should be enough for most uses, though we're still missing a querying function. --- include/git2/remote.h | 29 ++- src/branch.c | 22 +- src/clone.c | 12 +- src/fetch.c | 3 +- src/push.c | 5 +- src/refspec.c | 1 + src/refspec.h | 2 +- src/remote.c | 387 +++++++++++++++++----------- src/remote.h | 7 +- tests-clar/clone/nonetwork.c | 5 +- tests-clar/network/remote/remotes.c | 56 +++- tests-clar/online/fetchhead.c | 6 +- tests-clar/online/push.c | 5 +- tests-clar/refs/branches/remote.c | 3 +- 14 files changed, 337 insertions(+), 206 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 9494a8b01..7a161796f 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -142,30 +142,22 @@ GIT_EXTERN(int) git_remote_set_url(git_remote *remote, const char* url); GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url); /** - * Set the remote's fetch refspec + * Add a fetch refspec to the remote * * @param remote the remote - * @apram spec the new fetch refspec + * @apram refspec the new fetch refspec * @return 0 or an error value */ -GIT_EXTERN(int) git_remote_set_fetchspec(git_remote *remote, const char *spec); +GIT_EXTERN(int) git_remote_add_fetchspec(git_remote *remote, const char *refspec); /** - * Get the fetch refspec + * Add a push refspec to the remote * * @param remote the remote - * @return a pointer to the fetch refspec or NULL if it doesn't exist - */ -GIT_EXTERN(const git_refspec *) git_remote_fetchspec(const git_remote *remote); - -/** - * Set the remote's push refspec - * - * @param remote the remote - * @param spec the new push refspec + * @param refspec the new push refspec * @return 0 or an error value */ -GIT_EXTERN(int) git_remote_set_pushspec(git_remote *remote, const char *spec); +GIT_EXTERN(int) git_remote_add_pushspec(git_remote *remote, const char *refspec); /** * Get the push refspec @@ -176,6 +168,15 @@ GIT_EXTERN(int) git_remote_set_pushspec(git_remote *remote, const char *spec); GIT_EXTERN(const git_refspec *) git_remote_pushspec(const git_remote *remote); +/** + * Clear the refspecs + * + * Remove all configured fetch and push refspecs from the remote. + * + * @param remote the remote + */ +GIT_EXTERN(void) git_remote_clear_refspecs(git_remote *remote); + /** * Open a connection to a remote * diff --git a/src/branch.c b/src/branch.c index 956286b74..88f052529 100644 --- a/src/branch.c +++ b/src/branch.c @@ -11,6 +11,7 @@ #include "config.h" #include "refspec.h" #include "refs.h" +#include "remote.h" #include "git2/branch.h" @@ -283,12 +284,10 @@ int git_branch_upstream__name( if ((error = git_remote_load(&remote, repo, remote_name)) < 0) goto cleanup; - refspec = git_remote_fetchspec(remote); - if (refspec == NULL - || refspec->src == NULL - || refspec->dst == NULL) { - error = GIT_ENOTFOUND; - goto cleanup; + refspec = git_remote__matching_refspec(remote, merge_name); + if (!refspec) { + error = GIT_ENOTFOUND; + goto cleanup; } if (git_refspec_transform_r(&buf, refspec, merge_name) < 0) @@ -333,11 +332,8 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical if ((error = git_remote_load(&remote, repo, remote_list.strings[i])) < 0) continue; - fetchspec = git_remote_fetchspec(remote); - - /* Defensivly check that we have a fetchspec */ - if (fetchspec && - git_refspec_dst_matches(fetchspec, canonical_branch_name)) { + fetchspec = git_remote__matching_dst_refspec(remote, canonical_branch_name); + if (fetchspec) { /* If we have not already set out yet, then set * it to the matching remote name. Otherwise * multiple remotes match this reference, and it @@ -522,9 +518,9 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name) if (git_remote_load(&remote, repo, git_buf_cstr(&value)) < 0) goto on_error; - fetchspec = git_remote_fetchspec(remote); + fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream)); git_buf_clear(&value); - if (git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0) + if (!fetchspec || git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0) goto on_error; git_remote_free(remote); diff --git a/src/clone.c b/src/clone.c index 0bbccd44b..cb4053736 100644 --- a/src/clone.c +++ b/src/clone.c @@ -187,6 +187,7 @@ static int get_head_callback(git_remote_head *head, void *payload) static int update_head_to_remote(git_repository *repo, git_remote *remote) { int retcode = -1; + git_refspec dummy_spec; git_remote_head *remote_head; struct head_info head_info; git_buf remote_master_name = GIT_BUF_INIT; @@ -211,8 +212,13 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote) git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid); git_buf_init(&head_info.branchname, 16); head_info.repo = repo; - head_info.refspec = git_remote_fetchspec(remote); + head_info.refspec = git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE); head_info.found = 0; + + if (head_info.refspec == NULL) { + memset(&dummy_spec, 0, sizeof(git_refspec)); + head_info.refspec = &dummy_spec; + } /* Determine the remote tracking reference name from the local master */ if (git_refspec_transform_r( @@ -318,11 +324,11 @@ static int create_and_configure_origin( goto on_error; if (options->fetch_spec && - (error = git_remote_set_fetchspec(origin, options->fetch_spec)) < 0) + (error = git_remote_add_fetchspec(origin, options->fetch_spec)) < 0) goto on_error; if (options->push_spec && - (error = git_remote_set_pushspec(origin, options->push_spec)) < 0) + (error = git_remote_add_pushspec(origin, options->push_spec)) < 0) goto on_error; if (options->pushurl && diff --git a/src/fetch.c b/src/fetch.c index b60a95232..8ae34bddf 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -34,7 +34,7 @@ static int filter_ref__cb(git_remote_head *head, void *payload) if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) p->found_head = 1; - else if (git_refspec_src_matches(p->spec, head->name)) + else if (git_remote__matching_refspec(p->remote, head->name)) match = 1; else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL && git_refspec_src_matches(p->tagspec, head->name)) @@ -68,7 +68,6 @@ static int filter_wants(git_remote *remote) * not interested in any particular branch but just the remote's * HEAD, which will be stored in FETCH_HEAD after the fetch. */ - p.spec = git_remote_fetchspec(remote); p.tagspec = &tagspec; p.found_head = 0; p.remote = remote; diff --git a/src/push.c b/src/push.c index cec4c64af..b6be1a4e1 100644 --- a/src/push.c +++ b/src/push.c @@ -177,9 +177,9 @@ int git_push_add_refspec(git_push *push, const char *refspec) int git_push_update_tips(git_push *push) { - git_refspec *fetch_spec = &push->remote->fetch; git_buf remote_ref_name = GIT_BUF_INIT; size_t i, j; + git_refspec *fetch_spec; push_spec *push_spec; git_reference *remote_ref; push_status *status; @@ -191,7 +191,8 @@ int git_push_update_tips(git_push *push) continue; /* Find the corresponding remote ref */ - if (!git_refspec_src_matches(fetch_spec, status->ref)) + fetch_spec = git_remote__matching_refspec(push->remote, status->ref); + if (!fetch_spec) continue; if ((error = git_refspec_transform_r(&remote_ref_name, fetch_spec, status->ref)) < 0) diff --git a/src/refspec.c b/src/refspec.c index a51b0cfab..0ed02f48b 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -25,6 +25,7 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) assert(refspec && input); memset(refspec, 0x0, sizeof(git_refspec)); + refspec->push = !is_fetch; lhs = input; if (*lhs == '+') { diff --git a/src/refspec.h b/src/refspec.h index a7a4dd834..339d10eb2 100644 --- a/src/refspec.h +++ b/src/refspec.h @@ -11,10 +11,10 @@ #include "buffer.h" struct git_refspec { - struct git_refspec *next; char *src; char *dst; unsigned int force :1, + push : 1, pattern :1, matching :1; }; diff --git a/src/remote.c b/src/remote.c index 56853834b..1c4b22bb9 100644 --- a/src/remote.c +++ b/src/remote.c @@ -19,15 +19,34 @@ #include -static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var, bool is_fetch) +static int add_refspec(git_remote *remote, const char *string, bool is_fetch) { - int error; - const char *val; + char *name_dup; + git_refspec *spec; - if ((error = git_config_get_string(&val, cfg, var)) < 0) - return error; + spec = git__calloc(1, sizeof(git_refspec)); + GITERR_CHECK_ALLOC(spec); - return git_refspec__parse(refspec, val, is_fetch); + name_dup = git__strdup(string); + if (!name_dup) + goto on_error; + + if (git_refspec__parse(spec, string, is_fetch) < 0) + goto on_error; + + spec->push = !is_fetch; + if (git_vector_insert(&remote->refspec_strings, name_dup) < 0) + goto on_error; + + if (git_vector_insert(&remote->refspecs, spec) < 0) + goto on_error; + + return 0; + +on_error: + git__free(spec); + git__free(name_dup); + return -1; } static int download_tags_value(git_remote *remote, git_config *cfg) @@ -99,7 +118,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n } if (fetch != NULL) { - if (git_refspec__parse(&remote->fetch, fetch, true) < 0) + if (add_refspec(remote, fetch, true) < 0) goto on_error; } @@ -186,6 +205,18 @@ int git_remote_create_inmemory(git_remote **out, git_repository *repo, const cha return 0; } +struct refspec_cb_data { + git_remote *remote; + int fetch; +}; + +static int refspec_cb(const git_config_entry *entry, void *payload) +{ + const struct refspec_cb_data *data = (struct refspec_cb_data *)payload; + + return add_refspec(data->remote, entry->value, data->fetch); +} + int git_remote_load(git_remote **out, git_repository *repo, const char *name) { git_remote *remote; @@ -193,6 +224,8 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) const char *val; int error = 0; git_config *config; + struct refspec_cb_data data; + assert(out && repo && name); @@ -211,7 +244,9 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) remote->name = git__strdup(name); GITERR_CHECK_ALLOC(remote->name); - if (git_vector_init(&remote->refs, 32, NULL) < 0) { + if ((git_vector_init(&remote->refs, 32, NULL) < 0) || + (git_vector_init(&remote->refspecs, 2, NULL)) || + (git_vector_init(&remote->refspec_strings, 2, NULL))) { error = -1; goto cleanup; } @@ -262,7 +297,9 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) goto cleanup; } - error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf), true); + data.remote = remote; + data.fetch = true; + error = git_config_get_multivar(config, git_buf_cstr(&buf), NULL, refspec_cb, &data); if (error == GIT_ENOTFOUND) error = 0; @@ -277,7 +314,8 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) goto cleanup; } - error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf), false); + data.fetch = false; + error = git_config_get_multivar(config, git_buf_cstr(&buf), NULL, refspec_cb, &data); if (error == GIT_ENOTFOUND) error = 0; @@ -300,36 +338,46 @@ cleanup: return error; } -static int update_config_refspec( - git_config *config, - const char *remote_name, - const git_refspec *refspec, - int git_direction) +static int update_config_refspec(const git_remote *remote, git_config *config, int direction) { - git_buf name = GIT_BUF_INIT, value = GIT_BUF_INIT; + git_buf name = GIT_BUF_INIT; + int push; + const char *dir; + size_t i; int error = -1; - if (refspec->src == NULL || refspec->dst == NULL) - return 0; + push = direction == GIT_DIRECTION_PUSH; + dir = push ? "push" : "fetch"; - if (git_buf_printf( - &name, - "remote.%s.%s", - remote_name, - git_direction == GIT_DIRECTION_FETCH ? "fetch" : "push") < 0) + if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0) + return -1; + + /* Clear out the existing config */ + do { + error = git_config_delete_entry(config, git_buf_cstr(&name)); + } while (!error); + + if (error != GIT_ENOTFOUND) + return error; + + for (i = 0; i < remote->refspec_strings.length; i++) { + git_refspec *spec = git_vector_get(&remote->refspecs, i); + const char *str = git_vector_get(&remote->refspec_strings, i); + assert(spec && str); + + if (spec->push != push) + continue; + + if ((error = git_config_set_multivar(config, git_buf_cstr(&name), "", str)) < 0) { goto cleanup; + } + } - if (git_refspec__serialize(&value, refspec) < 0) - goto cleanup; - - error = git_config_set_string( - config, - git_buf_cstr(&name), - git_buf_cstr(&value)); + giterr_clear(); + error = 0; cleanup: git_buf_free(&name); - git_buf_free(&value); return error; } @@ -383,19 +431,11 @@ int git_remote_save(const git_remote *remote) } } - if (update_config_refspec( - config, - remote->name, - &remote->fetch, - GIT_DIRECTION_FETCH) < 0) - goto on_error; + if (update_config_refspec(remote, config, GIT_DIRECTION_FETCH) < 0) + goto on_error; - if (update_config_refspec( - config, - remote->name, - &remote->push, - GIT_DIRECTION_PUSH) < 0) - goto on_error; + if (update_config_refspec(remote, config, GIT_DIRECTION_PUSH) < 0) + goto on_error; /* * What action to take depends on the old and new values. This @@ -482,49 +522,6 @@ int git_remote_set_pushurl(git_remote *remote, const char* url) return 0; } -int git_remote_set_fetchspec(git_remote *remote, const char *spec) -{ - git_refspec refspec; - - assert(remote && spec); - - if (git_refspec__parse(&refspec, spec, true) < 0) - return -1; - - git_refspec__free(&remote->fetch); - memcpy(&remote->fetch, &refspec, sizeof(git_refspec)); - - return 0; -} - -const git_refspec *git_remote_fetchspec(const git_remote *remote) -{ - assert(remote); - return &remote->fetch; -} - -int git_remote_set_pushspec(git_remote *remote, const char *spec) -{ - git_refspec refspec; - - assert(remote && spec); - - if (git_refspec__parse(&refspec, spec, false) < 0) - return -1; - - git_refspec__free(&remote->push); - remote->push.src = refspec.src; - remote->push.dst = refspec.dst; - - return 0; -} - -const git_refspec *git_remote_pushspec(const git_remote *remote) -{ - assert(remote); - return &remote->push; -} - const char* git_remote__urlfordirection(git_remote *remote, int direction) { assert(remote); @@ -687,21 +684,21 @@ static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *upda return 0; } -static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_vector *update_heads, git_reference *ref) +static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vector *update_heads, git_reference *ref) { git_reference *resolved_ref = NULL; git_reference *tracking_ref = NULL; git_buf remote_name = GIT_BUF_INIT; int error = 0; - assert(out && remote && ref); + assert(out && spec && ref); *out = NULL; if ((error = git_reference_resolve(&resolved_ref, ref)) < 0 || (!git_reference_is_branch(resolved_ref)) || (error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 || - (error = git_refspec_transform_l(&remote_name, &remote->fetch, git_reference_name(tracking_ref))) < 0) { + (error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) { /* Not an error if HEAD is orphaned or no tracking branch */ if (error == GIT_ENOTFOUND) error = 0; @@ -718,9 +715,8 @@ cleanup: return error; } -static int git_remote_write_fetchhead(git_remote *remote, git_vector *update_heads) +static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads) { - struct git_refspec *spec; git_reference *head_ref = NULL; git_fetchhead_ref *fetchhead_ref; git_remote_head *remote_ref, *merge_remote_ref; @@ -735,8 +731,6 @@ static int git_remote_write_fetchhead(git_remote *remote, git_vector *update_hea if (update_heads->length == 0) return 0; - spec = &remote->fetch; - if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0) return -1; @@ -746,7 +740,7 @@ static int git_remote_write_fetchhead(git_remote *remote, git_vector *update_hea /* Determine what to merge: if refspec was a wildcard, just use HEAD */ if (git_refspec_is_wildcard(spec)) { if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 || - (error = remote_head_for_ref(&merge_remote_ref, remote, update_heads, head_ref)) < 0) + (error = remote_head_for_ref(&merge_remote_ref, spec, update_heads, head_ref)) < 0) goto cleanup; } else { /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */ @@ -786,7 +780,7 @@ cleanup: return error; } -int git_remote_update_tips(git_remote *remote) +static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vector *refs) { int error = 0, autotag; unsigned int i = 0; @@ -795,14 +789,11 @@ int git_remote_update_tips(git_remote *remote) git_odb *odb; git_remote_head *head; git_reference *ref; - struct git_refspec *spec; git_refspec tagspec; - git_vector refs, update_heads; + git_vector update_heads; assert(remote); - spec = &remote->fetch; - if (git_repository_odb__weakptr(&odb, remote->repo) < 0) return -1; @@ -810,16 +801,12 @@ int git_remote_update_tips(git_remote *remote) return -1; /* Make a copy of the transport's refs */ - if (git_vector_init(&refs, 16, NULL) < 0 || - git_vector_init(&update_heads, 16, NULL) < 0) + if (git_vector_init(&update_heads, 16, NULL) < 0) return -1; - if (git_remote_ls(remote, update_tips_callback, &refs) < 0) - goto on_error; - /* Let's go find HEAD, if it exists. Check only the first ref in the vector. */ - if (refs.length > 0) { - head = (git_remote_head *)refs.contents[0]; + if (refs->length > 0) { + head = git_vector_get(refs, 0); if (!strcmp(head->name, GIT_HEAD_FILE)) { if (git_reference_create(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0) @@ -830,8 +817,8 @@ int git_remote_update_tips(git_remote *remote) } } - for (; i < refs.length; ++i) { - head = (git_remote_head *)refs.contents[i]; + for (; i < refs->length; ++i) { + head = git_vector_get(refs, i); autotag = 0; /* Ignore malformed ref names (which also saves us from tag^{} */ @@ -886,17 +873,15 @@ int git_remote_update_tips(git_remote *remote) } if (git_remote_update_fetchhead(remote) && - (error = git_remote_write_fetchhead(remote, &update_heads)) < 0) + (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0) goto on_error; - git_vector_free(&refs); git_vector_free(&update_heads); git_refspec__free(&tagspec); git_buf_free(&refname); return 0; on_error: - git_vector_free(&refs); git_vector_free(&update_heads); git_refspec__free(&tagspec); git_buf_free(&refname); @@ -904,6 +889,34 @@ on_error: } +int git_remote_update_tips(git_remote *remote) +{ + git_refspec *spec; + git_vector refs; + size_t i; + + if (git_vector_init(&refs, 16, NULL) < 0) + return -1; + + if (git_remote_ls(remote, update_tips_callback, &refs) < 0) + goto on_error; + + git_vector_foreach(&remote->refspecs, i, spec) { + if (spec->push) + continue; + + if (update_tips_for_spec(remote, spec, &refs) < 0) + goto on_error; + } + + git_vector_free(&refs); + return 0; + +on_error: + git_vector_free(&refs); + return -1; +} + int git_remote_connected(git_remote *remote) { assert(remote); @@ -933,6 +946,10 @@ void git_remote_disconnect(git_remote *remote) void git_remote_free(git_remote *remote) { + git_refspec *spec; + char *str; + size_t i; + if (remote == NULL) return; @@ -945,8 +962,16 @@ void git_remote_free(git_remote *remote) git_vector_free(&remote->refs); - git_refspec__free(&remote->fetch); - git_refspec__free(&remote->push); + git_vector_foreach(&remote->refspecs, i, spec) { + git_refspec__free(spec); + git__free(spec); + } + git_vector_free(&remote->refspecs); + + git_vector_foreach(&remote->refspec_strings, i, str) + git__free(str); + git_vector_free(&remote->refspec_strings); + git__free(remote->url); git__free(remote->pushurl); git__free(remote->name); @@ -1237,58 +1262,61 @@ static int rename_fetch_refspecs( void *payload) { git_config *config; - const git_refspec *fetch_refspec; - git_buf dst_prefix = GIT_BUF_INIT, serialized = GIT_BUF_INIT; - const char* pos; + git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT; + const char *refspec; + size_t i; int error = -1; - fetch_refspec = git_remote_fetchspec(remote); - - /* Is there a refspec to deal with? */ - if (fetch_refspec->src == NULL && - fetch_refspec->dst == NULL) - return 0; - - if (git_refspec__serialize(&serialized, fetch_refspec) < 0) + if (git_buf_printf(&base, "+refs/heads/*:refs/remotes/%s/*", remote->name) < 0) goto cleanup; - /* Is it an in-memory remote? */ - if (!remote->name) { - error = (callback(git_buf_cstr(&serialized), payload) < 0) ? GIT_EUSER : 0; - goto cleanup; - } + git_vector_foreach(&remote->refspec_strings, i, refspec) { + git_refspec *spec = git_vector_get(&remote->refspecs, i); + assert(spec); - if (git_buf_printf(&dst_prefix, ":refs/remotes/%s/", remote->name) < 0) - goto cleanup; + if (spec->push) + continue; - pos = strstr(git_buf_cstr(&serialized), git_buf_cstr(&dst_prefix)); + /* Every refspec is a problem refspec for an in-memory remote */ + if (!remote->name) { + if (callback(refspec, payload) < 0) { + error = GIT_EUSER; + goto cleanup; + } - /* Does the dst part of the refspec follow the extected standard format? */ - if (!pos) { - error = (callback(git_buf_cstr(&serialized), payload) < 0) ? GIT_EUSER : 0; - goto cleanup; - } + continue; + } - if (git_buf_splice( - &serialized, - pos - git_buf_cstr(&serialized) + strlen(":refs/remotes/"), - strlen(remote->name), new_name, - strlen(new_name)) < 0) + /* Does the dst part of the refspec follow the extected standard format? */ + if (strcmp(git_buf_cstr(&base), refspec)) { + if (callback(refspec, payload) < 0) { + error = GIT_EUSER; + goto cleanup; + } + + continue; + } + + /* If we do want to move it to the new section */ + if (git_buf_printf(&val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0) goto cleanup; - git_refspec__free(&remote->fetch); + if (git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) + goto cleanup; - if (git_refspec__parse(&remote->fetch, git_buf_cstr(&serialized), true) < 0) - goto cleanup; + if (git_repository_config__weakptr(&config, remote->repo) < 0) + goto cleanup; - if (git_repository_config__weakptr(&config, remote->repo) < 0) - goto cleanup; + if (git_config_set_string(config, git_buf_cstr(&var), git_buf_cstr(&val)) < 0) + goto cleanup; + } - error = update_config_refspec(config, new_name, &remote->fetch, GIT_DIRECTION_FETCH); + error = 0; cleanup: - git_buf_free(&serialized); - git_buf_free(&dst_prefix); + git_buf_free(&base); + git_buf_free(&var); + git_buf_free(&val); return error; } @@ -1389,3 +1417,62 @@ int git_remote_is_valid_name( giterr_clear(); return error == 0; } + +git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname) +{ + git_refspec *spec; + size_t i; + + git_vector_foreach(&remote->refspecs, i, spec) { + if (spec->push) + continue; + + if (git_refspec_src_matches(spec, refname)) + return spec; + } + + return NULL; +} + +git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname) +{ + git_refspec *spec; + size_t i; + + git_vector_foreach(&remote->refspecs, i, spec) { + if (spec->push) + continue; + + if (git_refspec_dst_matches(spec, refname)) + return spec; + } + + return NULL; +} + +void git_remote_clear_refspecs(git_remote *remote) +{ + git_refspec *spec; + char *str; + size_t i; + + git_vector_foreach(&remote->refspecs, i, spec) { + git_refspec__free(spec); + } + git_vector_clear(&remote->refspecs); + + git_vector_foreach(&remote->refspec_strings, i, str) { + git__free(str); + } + git_vector_clear(&remote->refspec_strings); +} + +int git_remote_add_fetchspec(git_remote *remote, const char *refspec) +{ + return add_refspec(remote, refspec, true); +} + +int git_remote_add_pushspec(git_remote *remote, const char *refspec) +{ + return add_refspec(remote, refspec, false); +} diff --git a/src/remote.h b/src/remote.h index 4c1a18aa7..b1f33f0e7 100644 --- a/src/remote.h +++ b/src/remote.h @@ -20,8 +20,8 @@ struct git_remote { char *url; char *pushurl; git_vector refs; - struct git_refspec fetch; - struct git_refspec push; + git_vector refspecs; + git_vector refspec_strings; git_cred_acquire_cb cred_acquire_cb; void *cred_acquire_payload; git_transport *transport; @@ -37,4 +37,7 @@ struct git_remote { const char* git_remote__urlfordirection(struct git_remote *remote, int direction); int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url); +git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname); +git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname); + #endif diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c index c4b482234..02066e07d 100644 --- a/tests-clar/clone/nonetwork.c +++ b/tests-clar/clone/nonetwork.c @@ -2,6 +2,7 @@ #include "git2/clone.h" #include "repository.h" +#include "remote.h" #define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository" @@ -148,7 +149,7 @@ void test_clone_nonetwork__custom_fetch_spec(void) cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); cl_git_pass(git_remote_load(&g_remote, g_repo, "origin")); - actual_fs = git_remote_fetchspec(g_remote); + actual_fs = git_vector_get(&g_remote->refspecs, 0); cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs)); cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs)); @@ -164,7 +165,7 @@ void test_clone_nonetwork__custom_push_spec(void) cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); cl_git_pass(git_remote_load(&g_remote, g_repo, "origin")); - actual_fs = git_remote_pushspec(g_remote); + actual_fs = git_vector_get(&g_remote->refspecs, g_remote->refspecs.length - 1); cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs)); cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs)); } diff --git a/tests-clar/network/remote/remotes.c b/tests-clar/network/remote/remotes.c index a5ff7415f..92c9d8ac1 100644 --- a/tests-clar/network/remote/remotes.c +++ b/tests-clar/network/remote/remotes.c @@ -13,7 +13,7 @@ void test_network_remote_remotes__initialize(void) cl_git_pass(git_remote_load(&_remote, _repo, "test")); - _refspec = git_remote_fetchspec(_remote); + _refspec = git_vector_get(&_remote->refspecs, 0); cl_assert(_refspec != NULL); } @@ -109,20 +109,41 @@ void test_network_remote_remotes__refspec_parsing(void) cl_assert_equal_s(git_refspec_dst(_refspec), "refs/remotes/test/*"); } -void test_network_remote_remotes__set_fetchspec(void) +void test_network_remote_remotes__add_fetchspec(void) { - cl_git_pass(git_remote_set_fetchspec(_remote, "refs/*:refs/*")); - _refspec = git_remote_fetchspec(_remote); + size_t size; + + size = _remote->refspecs.length; + cl_assert_equal_i(size, _remote->refspec_strings.length); + + cl_git_pass(git_remote_add_fetchspec(_remote, "refs/*:refs/*")); + + size++; + cl_assert_equal_i(size, _remote->refspec_strings.length); + cl_assert_equal_i(size, _remote->refspecs.length); + + _refspec = git_vector_get(&_remote->refspecs, size-1); cl_assert_equal_s(git_refspec_src(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*"); + cl_assert_equal_i(_refspec->push, false); } -void test_network_remote_remotes__set_pushspec(void) +void test_network_remote_remotes__add_pushspec(void) { - cl_git_pass(git_remote_set_pushspec(_remote, "refs/*:refs/*")); - _refspec = git_remote_pushspec(_remote); + size_t size; + + size = _remote->refspecs.length; + + cl_git_pass(git_remote_add_pushspec(_remote, "refs/*:refs/*")); + size++; + cl_assert_equal_i(size, _remote->refspec_strings.length); + cl_assert_equal_i(size, _remote->refspecs.length); + + _refspec = git_vector_get(&_remote->refspecs, size-1); cl_assert_equal_s(git_refspec_src(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*"); + + cl_assert_equal_i(_refspec->push, true); } void test_network_remote_remotes__save(void) @@ -132,8 +153,18 @@ void test_network_remote_remotes__save(void) /* Set up the remote and save it to config */ cl_git_pass(git_remote_create(&_remote, _repo, "upstream", "git://github.com/libgit2/libgit2")); - cl_git_pass(git_remote_set_fetchspec(_remote, "refs/heads/*:refs/remotes/upstream/*")); - cl_git_pass(git_remote_set_pushspec(_remote, "refs/heads/*:refs/heads/*")); + git_remote_clear_refspecs(_remote); + cl_assert_equal_i(0, _remote->refspecs.length); + cl_assert_equal_i(0, _remote->refspec_strings.length); + + cl_git_pass(git_remote_add_fetchspec(_remote, "refs/heads/*:refs/remotes/upstream/*")); + cl_assert_equal_i(1, _remote->refspecs.length); + cl_assert_equal_i(1, _remote->refspec_strings.length); + + cl_git_pass(git_remote_add_pushspec(_remote, "refs/heads/*:refs/heads/*")); + cl_assert_equal_i(2, _remote->refspecs.length); + cl_assert_equal_i(2, _remote->refspec_strings.length); + cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/libgit2_push")); cl_git_pass(git_remote_save(_remote)); git_remote_free(_remote); @@ -142,13 +173,14 @@ void test_network_remote_remotes__save(void) /* Load it from config and make sure everything matches */ cl_git_pass(git_remote_load(&_remote, _repo, "upstream")); - _refspec = git_remote_fetchspec(_remote); + _refspec = git_vector_get(&_remote->refspecs, 0); cl_assert(_refspec != NULL); cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*"); cl_assert_equal_s(git_refspec_dst(_refspec), "refs/remotes/upstream/*"); cl_assert_equal_i(0, git_refspec_force(_refspec)); - _refspec = git_remote_pushspec(_remote); + cl_assert(_refspec != git_vector_get(&_remote->refspecs, 1)); + _refspec = git_vector_get(&_remote->refspecs, 1); cl_assert(_refspec != NULL); cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*"); cl_assert_equal_s(git_refspec_dst(_refspec), "refs/heads/*"); @@ -265,7 +297,7 @@ void test_network_remote_remotes__add(void) _remote = NULL; cl_git_pass(git_remote_load(&_remote, _repo, "addtest")); - _refspec = git_remote_fetchspec(_remote); + _refspec = git_vector_get(&_remote->refspecs, 0); cl_assert_equal_s("refs/heads/*", git_refspec_src(_refspec)); cl_assert(git_refspec_force(_refspec) == 1); cl_assert_equal_s("refs/remotes/addtest/*", git_refspec_dst(_refspec)); diff --git a/tests-clar/online/fetchhead.c b/tests-clar/online/fetchhead.c index a8a5bb918..3cbdc7e93 100644 --- a/tests-clar/online/fetchhead.c +++ b/tests-clar/online/fetchhead.c @@ -42,8 +42,10 @@ static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fet cl_git_pass(git_remote_load(&remote, g_repo, "origin")); git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO); - if(fetchspec != NULL) - git_remote_set_fetchspec(remote, fetchspec); + if(fetchspec != NULL) { + git_remote_clear_refspecs(remote); + git_remote_add_fetchspec(remote, fetchspec); + } cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); cl_git_pass(git_remote_download(remote, NULL, NULL)); diff --git a/tests-clar/online/push.c b/tests-clar/online/push.c index 907d6d29f..5dc7974c7 100644 --- a/tests-clar/online/push.c +++ b/tests-clar/online/push.c @@ -160,7 +160,7 @@ static int tracking_branch_list_cb(const char *branch_name, git_branch_t branch_ */ static void verify_tracking_branches(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len) { - git_refspec *fetch_spec = &remote->fetch; + git_refspec *fetch_spec; size_t i, j; git_buf msg = GIT_BUF_INIT; git_buf ref_name = GIT_BUF_INIT; @@ -179,7 +179,8 @@ static void verify_tracking_branches(git_remote *remote, expected_ref expected_r /* Convert remote reference name into tracking branch name. * If the spec is not under refs/heads/, then skip. */ - if (!git_refspec_src_matches(fetch_spec, expected_refs[i].name)) + fetch_spec = git_remote__matching_refspec(remote, expected_refs[i].name); + if (!fetch_spec) continue; cl_git_pass(git_refspec_transform_r(&ref_name, fetch_spec, expected_refs[i].name)); diff --git a/tests-clar/refs/branches/remote.c b/tests-clar/refs/branches/remote.c index 2beef3724..475fa54a8 100644 --- a/tests-clar/refs/branches/remote.c +++ b/tests-clar/refs/branches/remote.c @@ -69,7 +69,8 @@ void test_refs_branches_remote__ambiguous_remote_returns_error(void) cl_git_pass(git_remote_create(&remote, g_repo, "addtest", "http://github.com/libgit2/libgit2")); /* Update the remote fetch spec */ - cl_git_pass(git_remote_set_fetchspec(remote, "refs/heads/*:refs/remotes/test/*")); + git_remote_clear_refspecs(remote); + cl_git_pass(git_remote_add_fetchspec(remote, "refs/heads/*:refs/remotes/test/*")); cl_git_pass(git_remote_save(remote)); git_remote_free(remote); From bc6374eac485ab9ad9cfd7165b6d071c3dcb673a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 20 Apr 2013 18:49:11 +0200 Subject: [PATCH 028/384] remote: allow querying for refspecs Introduce git_remote_{fetch,push}_refspecs() to get a list of refspecs from the remote and rename the refspec-adding functions to a less silly name. Use this instead of the vector index hacks in the tests. --- include/git2/remote.h | 26 +++++++++- src/clone.c | 4 +- src/remote.c | 52 ++++++++++++++++++- tests-clar/network/remote/remotes.c | 79 ++++++++++++++++++++--------- tests-clar/online/fetchhead.c | 2 +- tests-clar/refs/branches/remote.c | 2 +- 6 files changed, 134 insertions(+), 31 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 7a161796f..5dcd93099 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -148,7 +148,18 @@ GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url); * @apram refspec the new fetch refspec * @return 0 or an error value */ -GIT_EXTERN(int) git_remote_add_fetchspec(git_remote *remote, const char *refspec); +GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec); + +/** + * Get the remote's list of fetch refspecs + * + * The memory is owned by the user and should be freed with + * `git_strarray_free`. + * + * @param array pointer to the array in which to store the strings + * @param remote the remote to query + */ +GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote); /** * Add a push refspec to the remote @@ -157,7 +168,18 @@ GIT_EXTERN(int) git_remote_add_fetchspec(git_remote *remote, const char *refspec * @param refspec the new push refspec * @return 0 or an error value */ -GIT_EXTERN(int) git_remote_add_pushspec(git_remote *remote, const char *refspec); +GIT_EXTERN(int) git_remote_add_push(git_remote *remote, const char *refspec); + +/** + * Get the remote's list of push refspecs + * + * The memory is owned by the user and should be freed with + * `git_strarray_free`. + * + * @param array pointer to the array in which to store the strings + * @param remote the remote to query + */ +GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, git_remote *remote); /** * Get the push refspec diff --git a/src/clone.c b/src/clone.c index cb4053736..8f10ca819 100644 --- a/src/clone.c +++ b/src/clone.c @@ -324,11 +324,11 @@ static int create_and_configure_origin( goto on_error; if (options->fetch_spec && - (error = git_remote_add_fetchspec(origin, options->fetch_spec)) < 0) + (error = git_remote_add_fetch(origin, options->fetch_spec)) < 0) goto on_error; if (options->push_spec && - (error = git_remote_add_pushspec(origin, options->push_spec)) < 0) + (error = git_remote_add_push(origin, options->push_spec)) < 0) goto on_error; if (options->pushurl && diff --git a/src/remote.c b/src/remote.c index 1c4b22bb9..8b7e66e53 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1467,12 +1467,60 @@ void git_remote_clear_refspecs(git_remote *remote) git_vector_clear(&remote->refspec_strings); } -int git_remote_add_fetchspec(git_remote *remote, const char *refspec) +int git_remote_add_fetch(git_remote *remote, const char *refspec) { return add_refspec(remote, refspec, true); } -int git_remote_add_pushspec(git_remote *remote, const char *refspec) +int git_remote_add_push(git_remote *remote, const char *refspec) { return add_refspec(remote, refspec, false); } + +static int copy_refspecs(git_strarray *array, git_remote *remote, int push) +{ + size_t i; + git_vector refspecs; + git_refspec *spec; + char *dup; + + if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0) + return -1; + + git_vector_foreach(&remote->refspecs, i, spec) { + if (spec->push != push) + continue; + + dup = git__strdup(git_vector_get(&remote->refspec_strings, i)); + if (!dup) { + goto on_error; + } + + if (git_vector_insert(&refspecs, dup) < 0) { + git__free(dup); + goto on_error; + } + } + + array->strings = (char **)refspecs.contents; + array->count = refspecs.length; + + return 0; + +on_error: + git_vector_foreach(&refspecs, i, dup) + git__free(dup); + git_vector_free(&refspecs); + + return -1; +} + +int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote) +{ + return copy_refspecs(array, remote, false); +} + +int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote) +{ + return copy_refspecs(array, remote, true); +} diff --git a/tests-clar/network/remote/remotes.c b/tests-clar/network/remote/remotes.c index 92c9d8ac1..7b6e6aa85 100644 --- a/tests-clar/network/remote/remotes.c +++ b/tests-clar/network/remote/remotes.c @@ -116,7 +116,7 @@ void test_network_remote_remotes__add_fetchspec(void) size = _remote->refspecs.length; cl_assert_equal_i(size, _remote->refspec_strings.length); - cl_git_pass(git_remote_add_fetchspec(_remote, "refs/*:refs/*")); + cl_git_pass(git_remote_add_fetch(_remote, "refs/*:refs/*")); size++; cl_assert_equal_i(size, _remote->refspec_strings.length); @@ -134,7 +134,7 @@ void test_network_remote_remotes__add_pushspec(void) size = _remote->refspecs.length; - cl_git_pass(git_remote_add_pushspec(_remote, "refs/*:refs/*")); + cl_git_pass(git_remote_add_push(_remote, "refs/*:refs/*")); size++; cl_assert_equal_i(size, _remote->refspec_strings.length); cl_assert_equal_i(size, _remote->refspecs.length); @@ -148,23 +148,19 @@ void test_network_remote_remotes__add_pushspec(void) void test_network_remote_remotes__save(void) { + git_strarray array; + const char *fetch_refspec = "refs/heads/*:refs/remotes/upstream/*"; + const char *push_refspec = "refs/heads/*:refs/heads/*"; + git_remote_free(_remote); _remote = NULL; /* Set up the remote and save it to config */ cl_git_pass(git_remote_create(&_remote, _repo, "upstream", "git://github.com/libgit2/libgit2")); git_remote_clear_refspecs(_remote); - cl_assert_equal_i(0, _remote->refspecs.length); - cl_assert_equal_i(0, _remote->refspec_strings.length); - - cl_git_pass(git_remote_add_fetchspec(_remote, "refs/heads/*:refs/remotes/upstream/*")); - cl_assert_equal_i(1, _remote->refspecs.length); - cl_assert_equal_i(1, _remote->refspec_strings.length); - - cl_git_pass(git_remote_add_pushspec(_remote, "refs/heads/*:refs/heads/*")); - cl_assert_equal_i(2, _remote->refspecs.length); - cl_assert_equal_i(2, _remote->refspec_strings.length); + cl_git_pass(git_remote_add_fetch(_remote, fetch_refspec)); + cl_git_pass(git_remote_add_push(_remote, push_refspec)); cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/libgit2_push")); cl_git_pass(git_remote_save(_remote)); git_remote_free(_remote); @@ -173,20 +169,17 @@ void test_network_remote_remotes__save(void) /* Load it from config and make sure everything matches */ cl_git_pass(git_remote_load(&_remote, _repo, "upstream")); - _refspec = git_vector_get(&_remote->refspecs, 0); - cl_assert(_refspec != NULL); - cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*"); - cl_assert_equal_s(git_refspec_dst(_refspec), "refs/remotes/upstream/*"); - cl_assert_equal_i(0, git_refspec_force(_refspec)); - - cl_assert(_refspec != git_vector_get(&_remote->refspecs, 1)); - _refspec = git_vector_get(&_remote->refspecs, 1); - cl_assert(_refspec != NULL); - cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*"); - cl_assert_equal_s(git_refspec_dst(_refspec), "refs/heads/*"); + cl_git_pass(git_remote_get_fetch_refspecs(&array, _remote)); + cl_assert_equal_i(1, array.count); + cl_assert_equal_s(fetch_refspec, array.strings[0]); + git_strarray_free(&array); + cl_git_pass(git_remote_get_push_refspecs(&array, _remote)); + cl_assert_equal_i(1, array.count); + cl_assert_equal_s(push_refspec, array.strings[0]); cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2"); cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/libgit2_push"); + git_strarray_free(&array); /* remove the pushurl again and see if we can save that too */ cl_git_pass(git_remote_set_pushurl(_remote, NULL)); @@ -418,3 +411,43 @@ void test_network_remote_remotes__cannot_create_a_remote_which_name_is_invalid(v assert_cannot_create_remote(".lock", GIT_EINVALIDSPEC); assert_cannot_create_remote("a.lock", GIT_EINVALIDSPEC); } + +static const char *fetch_refspecs[] = { + "+refs/heads/*:refs/remotes/origin/*", + "refs/tags/*:refs/tags/*", + "+refs/pull/*:refs/pull/*", +}; + +static const char *push_refspecs[] = { + "refs/heads/*:refs/heads/*", + "refs/tags/*:refs/tags/*", + "refs/notes/*:refs/notes/*", +}; + +void test_network_remote_remotes__query_refspecs(void) +{ + git_remote *remote; + git_strarray array; + int i; + + cl_git_pass(git_remote_create_inmemory(&remote, _repo, NULL, "git://github.com/libgit2/libgit2")); + + for (i = 0; i < 3; i++) { + cl_git_pass(git_remote_add_fetch(remote, fetch_refspecs[i])); + cl_git_pass(git_remote_add_push(remote, push_refspecs[i])); + } + + cl_git_pass(git_remote_get_fetch_refspecs(&array, remote)); + for (i = 0; i < 3; i++) { + cl_assert_equal_s(fetch_refspecs[i], array.strings[i]); + } + git_strarray_free(&array); + + cl_git_pass(git_remote_get_push_refspecs(&array, remote)); + for (i = 0; i < 3; i++) { + cl_assert_equal_s(push_refspecs[i], array.strings[i]); + } + git_strarray_free(&array); + + git_remote_free(remote); +} diff --git a/tests-clar/online/fetchhead.c b/tests-clar/online/fetchhead.c index 3cbdc7e93..e14ae0926 100644 --- a/tests-clar/online/fetchhead.c +++ b/tests-clar/online/fetchhead.c @@ -44,7 +44,7 @@ static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fet if(fetchspec != NULL) { git_remote_clear_refspecs(remote); - git_remote_add_fetchspec(remote, fetchspec); + git_remote_add_fetch(remote, fetchspec); } cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); diff --git a/tests-clar/refs/branches/remote.c b/tests-clar/refs/branches/remote.c index 475fa54a8..6043828b3 100644 --- a/tests-clar/refs/branches/remote.c +++ b/tests-clar/refs/branches/remote.c @@ -70,7 +70,7 @@ void test_refs_branches_remote__ambiguous_remote_returns_error(void) /* Update the remote fetch spec */ git_remote_clear_refspecs(remote); - cl_git_pass(git_remote_add_fetchspec(remote, "refs/heads/*:refs/remotes/test/*")); + cl_git_pass(git_remote_add_fetch(remote, "refs/heads/*:refs/remotes/test/*")); cl_git_pass(git_remote_save(remote)); git_remote_free(remote); From 1be680c4d0909ee5160292d7b56c4522c4bc309c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 20 Apr 2013 19:13:47 +0200 Subject: [PATCH 029/384] refspec: unify the string and parsed data It used to be separate as an attempt to make the querying easier, but it didn't work out that way, so put all the data together. Add git_refspec_string() as well to get the original string, which is now stored alongside the independent parts. --- include/git2/refspec.h | 8 ++++ src/refspec.c | 9 ++++ src/refspec.h | 1 + src/remote.c | 64 +++++++++-------------------- src/remote.h | 1 - tests-clar/network/remote/remotes.c | 6 +-- 6 files changed, 40 insertions(+), 49 deletions(-) diff --git a/include/git2/refspec.h b/include/git2/refspec.h index ec7830b7c..3e1b502ef 100644 --- a/include/git2/refspec.h +++ b/include/git2/refspec.h @@ -35,6 +35,14 @@ GIT_EXTERN(const char *) git_refspec_src(const git_refspec *refspec); */ GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec); +/** + * Get the refspec's string + * + * @param refspec the refspec + * @returns the refspec's original string + */ +GIT_EXTERN(const char *) git_refspec_string(const git_refspec *refspec); + /** * Get the force update setting * diff --git a/src/refspec.c b/src/refspec.c index 0ed02f48b..fbdea9d93 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -120,6 +120,9 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) } } + refspec->string = git__strdup(input); + GITERR_CHECK_ALLOC(refspec->string); + return 0; invalid: @@ -133,6 +136,7 @@ void git_refspec__free(git_refspec *refspec) git__free(refspec->src); git__free(refspec->dst); + git__free(refspec->string); } const char *git_refspec_src(const git_refspec *refspec) @@ -145,6 +149,11 @@ const char *git_refspec_dst(const git_refspec *refspec) return refspec == NULL ? NULL : refspec->dst; } +const char *git_refspec_string(const git_refspec *refspec) +{ + return refspec == NULL ? NULL : refspec->string; +} + int git_refspec_force(const git_refspec *refspec) { assert(refspec); diff --git a/src/refspec.h b/src/refspec.h index 339d10eb2..29f4d5354 100644 --- a/src/refspec.h +++ b/src/refspec.h @@ -11,6 +11,7 @@ #include "buffer.h" struct git_refspec { + char *string; char *src; char *dst; unsigned int force :1, diff --git a/src/remote.c b/src/remote.c index 8b7e66e53..ffce2b6e2 100644 --- a/src/remote.c +++ b/src/remote.c @@ -21,32 +21,24 @@ static int add_refspec(git_remote *remote, const char *string, bool is_fetch) { - char *name_dup; git_refspec *spec; spec = git__calloc(1, sizeof(git_refspec)); GITERR_CHECK_ALLOC(spec); - name_dup = git__strdup(string); - if (!name_dup) - goto on_error; - - if (git_refspec__parse(spec, string, is_fetch) < 0) - goto on_error; + if (git_refspec__parse(spec, string, is_fetch) < 0) { + git__free(spec); + return -1; + } spec->push = !is_fetch; - if (git_vector_insert(&remote->refspec_strings, name_dup) < 0) - goto on_error; - - if (git_vector_insert(&remote->refspecs, spec) < 0) - goto on_error; + if (git_vector_insert(&remote->refspecs, spec) < 0) { + git_refspec__free(spec); + git__free(spec); + return -1; + } return 0; - -on_error: - git__free(spec); - git__free(name_dup); - return -1; } static int download_tags_value(git_remote *remote, git_config *cfg) @@ -245,8 +237,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) GITERR_CHECK_ALLOC(remote->name); if ((git_vector_init(&remote->refs, 32, NULL) < 0) || - (git_vector_init(&remote->refspecs, 2, NULL)) || - (git_vector_init(&remote->refspec_strings, 2, NULL))) { + (git_vector_init(&remote->refspecs, 2, NULL))) { error = -1; goto cleanup; } @@ -360,15 +351,13 @@ static int update_config_refspec(const git_remote *remote, git_config *config, i if (error != GIT_ENOTFOUND) return error; - for (i = 0; i < remote->refspec_strings.length; i++) { + for (i = 0; i < remote->refspecs.length; i++) { git_refspec *spec = git_vector_get(&remote->refspecs, i); - const char *str = git_vector_get(&remote->refspec_strings, i); - assert(spec && str); if (spec->push != push) continue; - if ((error = git_config_set_multivar(config, git_buf_cstr(&name), "", str)) < 0) { + if ((error = git_config_set_multivar(config, git_buf_cstr(&name), "", spec->string)) < 0) { goto cleanup; } } @@ -947,7 +936,6 @@ void git_remote_disconnect(git_remote *remote) void git_remote_free(git_remote *remote) { git_refspec *spec; - char *str; size_t i; if (remote == NULL) @@ -968,10 +956,6 @@ void git_remote_free(git_remote *remote) } git_vector_free(&remote->refspecs); - git_vector_foreach(&remote->refspec_strings, i, str) - git__free(str); - git_vector_free(&remote->refspec_strings); - git__free(remote->url); git__free(remote->pushurl); git__free(remote->name); @@ -1263,23 +1247,20 @@ static int rename_fetch_refspecs( { git_config *config; git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT; - const char *refspec; + const git_refspec *spec; size_t i; int error = -1; if (git_buf_printf(&base, "+refs/heads/*:refs/remotes/%s/*", remote->name) < 0) goto cleanup; - git_vector_foreach(&remote->refspec_strings, i, refspec) { - git_refspec *spec = git_vector_get(&remote->refspecs, i); - assert(spec); - + git_vector_foreach(&remote->refspecs, i, spec) { if (spec->push) continue; /* Every refspec is a problem refspec for an in-memory remote */ if (!remote->name) { - if (callback(refspec, payload) < 0) { + if (callback(spec->string, payload) < 0) { error = GIT_EUSER; goto cleanup; } @@ -1288,8 +1269,8 @@ static int rename_fetch_refspecs( } /* Does the dst part of the refspec follow the extected standard format? */ - if (strcmp(git_buf_cstr(&base), refspec)) { - if (callback(refspec, payload) < 0) { + if (strcmp(git_buf_cstr(&base), spec->string)) { + if (callback(spec->string, payload) < 0) { error = GIT_EUSER; goto cleanup; } @@ -1453,18 +1434,13 @@ git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *re void git_remote_clear_refspecs(git_remote *remote) { git_refspec *spec; - char *str; size_t i; git_vector_foreach(&remote->refspecs, i, spec) { git_refspec__free(spec); + git__free(spec); } git_vector_clear(&remote->refspecs); - - git_vector_foreach(&remote->refspec_strings, i, str) { - git__free(str); - } - git_vector_clear(&remote->refspec_strings); } int git_remote_add_fetch(git_remote *remote, const char *refspec) @@ -1491,10 +1467,8 @@ static int copy_refspecs(git_strarray *array, git_remote *remote, int push) if (spec->push != push) continue; - dup = git__strdup(git_vector_get(&remote->refspec_strings, i)); - if (!dup) { + if ((dup = git__strdup(spec->string)) == NULL) goto on_error; - } if (git_vector_insert(&refspecs, dup) < 0) { git__free(dup); diff --git a/src/remote.h b/src/remote.h index b1f33f0e7..c9c26b77d 100644 --- a/src/remote.h +++ b/src/remote.h @@ -21,7 +21,6 @@ struct git_remote { char *pushurl; git_vector refs; git_vector refspecs; - git_vector refspec_strings; git_cred_acquire_cb cred_acquire_cb; void *cred_acquire_payload; git_transport *transport; diff --git a/tests-clar/network/remote/remotes.c b/tests-clar/network/remote/remotes.c index 7b6e6aa85..908e17d96 100644 --- a/tests-clar/network/remote/remotes.c +++ b/tests-clar/network/remote/remotes.c @@ -114,17 +114,17 @@ void test_network_remote_remotes__add_fetchspec(void) size_t size; size = _remote->refspecs.length; - cl_assert_equal_i(size, _remote->refspec_strings.length); + cl_assert_equal_i(size, _remote->refspecs.length); cl_git_pass(git_remote_add_fetch(_remote, "refs/*:refs/*")); size++; - cl_assert_equal_i(size, _remote->refspec_strings.length); cl_assert_equal_i(size, _remote->refspecs.length); _refspec = git_vector_get(&_remote->refspecs, size-1); cl_assert_equal_s(git_refspec_src(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*"); + cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*"); cl_assert_equal_i(_refspec->push, false); } @@ -136,12 +136,12 @@ void test_network_remote_remotes__add_pushspec(void) cl_git_pass(git_remote_add_push(_remote, "refs/*:refs/*")); size++; - cl_assert_equal_i(size, _remote->refspec_strings.length); cl_assert_equal_i(size, _remote->refspecs.length); _refspec = git_vector_get(&_remote->refspecs, size-1); cl_assert_equal_s(git_refspec_src(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*"); + cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*"); cl_assert_equal_i(_refspec->push, true); } From 6b24688758b721afece65702475f76d225d096be Mon Sep 17 00:00:00 2001 From: nulltoken Date: Sun, 21 Apr 2013 08:59:04 +0200 Subject: [PATCH 030/384] mailmap: Coalesce some identities --- .mailmap | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.mailmap b/.mailmap index a8b171908..3f5a087ea 100644 --- a/.mailmap +++ b/.mailmap @@ -5,3 +5,14 @@ Ben Straub Ben Straub Ben Straub Ben Straub Carlos Martín Nieto Carlos Martín Nieto +nulltoken +Scott J. Goldman +Martin Woodward +Peter Drahoš +Adam Roben +Adam Roben +Xavier L. +Xavier L. +Sascha Cunz +Authmillenon +Authmillenon From 83041c711cd7d9fccb7a1327e642ce33f0705370 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 19 Apr 2013 11:52:04 -0700 Subject: [PATCH 031/384] Move git_config_backend to include/git2/sys Moving backend implementor objects into include/git2/sys so the APIs can be isolated from the ones that normal libgit2 users would be likely to use. --- include/git2/config.h | 52 ++------------------------- include/git2/sys/config.h | 70 +++++++++++++++++++++++++++++++++++++ src/config.c | 1 + src/config_file.c | 1 + src/submodule.c | 1 + tests-clar/config/backend.c | 1 + 6 files changed, 76 insertions(+), 50 deletions(-) create mode 100644 include/git2/sys/config.h diff --git a/include/git2/config.h b/include/git2/config.h index 19d4cb78d..5a2f956fd 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -43,29 +43,6 @@ typedef struct { typedef int (*git_config_foreach_cb)(const git_config_entry *, void *); - -/** - * Generic backend that implements the interface to - * access a configuration file - */ -struct git_config_backend { - unsigned int version; - struct git_config *cfg; - - /* Open means open the file/database and parse if necessary */ - int (*open)(struct git_config_backend *, unsigned int level); - int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry); - int (*get_multivar)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload); - int (*set)(struct git_config_backend *, const char *key, const char *value); - int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value); - int (*del)(struct git_config_backend *, const char *key); - int (*foreach)(struct git_config_backend *, const char *, git_config_foreach_cb callback, void *payload); - int (*refresh)(struct git_config_backend *); - void (*free)(struct git_config_backend *); -}; -#define GIT_CONFIG_BACKEND_VERSION 1 -#define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION} - typedef enum { GIT_CVAR_FALSE = 0, GIT_CVAR_TRUE = 1, @@ -153,30 +130,6 @@ GIT_EXTERN(int) git_config_open_default(git_config **out); */ GIT_EXTERN(int) git_config_new(git_config **out); -/** - * Add a generic config file instance to an existing config - * - * Note that the configuration object will free the file - * automatically. - * - * Further queries on this config object will access each - * of the config file instances in order (instances with - * a higher priority level will be accessed first). - * - * @param cfg the configuration to add the file to - * @param file the configuration file (backend) to add - * @param level the priority level of the backend - * @param force if a config file already exists for the given - * priority level, replace it - * @return 0 on success, GIT_EEXISTS when adding more than one file - * for a given priority level (and force_replace set to 0), or error code - */ -GIT_EXTERN(int) git_config_add_backend( - git_config *cfg, - git_config_backend *file, - unsigned int level, - int force); - /** * Add an on-disk config file instance to an existing config * @@ -192,10 +145,9 @@ GIT_EXTERN(int) git_config_add_backend( * a higher priority level will be accessed first). * * @param cfg the configuration to add the file to - * @param path path to the configuration file (backend) to add + * @param path path to the configuration file to add * @param level the priority level of the backend - * @param force if a config file already exists for the given - * priority level, replace it + * @param force replace config file at the given priority level * @return 0 on success, GIT_EEXISTS when adding more than one file * for a given priority level (and force_replace set to 0), * GIT_ENOTFOUND when the file doesn't exist or error code diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h new file mode 100644 index 000000000..c9039e96e --- /dev/null +++ b/include/git2/sys/config.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_git_sys_config_h__ +#define INCLUDE_git_sys_config_h__ + +#include "git2/common.h" +#include "git2/types.h" +#include "git2/config.h" + +/** + * @file git2/sys/config.h + * @brief Git config backend routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Generic backend that implements the interface to + * access a configuration file + */ +struct git_config_backend { + unsigned int version; + struct git_config *cfg; + + /* Open means open the file/database and parse if necessary */ + int (*open)(struct git_config_backend *, unsigned int level); + int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry); + int (*get_multivar)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload); + int (*set)(struct git_config_backend *, const char *key, const char *value); + int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value); + int (*del)(struct git_config_backend *, const char *key); + int (*foreach)(struct git_config_backend *, const char *, git_config_foreach_cb callback, void *payload); + int (*refresh)(struct git_config_backend *); + void (*free)(struct git_config_backend *); +}; +#define GIT_CONFIG_BACKEND_VERSION 1 +#define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION} + +/** + * Add a generic config file instance to an existing config + * + * Note that the configuration object will free the file + * automatically. + * + * Further queries on this config object will access each + * of the config file instances in order (instances with + * a higher priority level will be accessed first). + * + * @param cfg the configuration to add the file to + * @param file the configuration file (backend) to add + * @param level the priority level of the backend + * @param force if a config file already exists for the given + * priority level, replace it + * @return 0 on success, GIT_EEXISTS when adding more than one file + * for a given priority level (and force_replace set to 0), or error code + */ +GIT_EXTERN(int) git_config_add_backend( + git_config *cfg, + git_config_backend *file, + unsigned int level, + int force); + +/** @} */ +GIT_END_DECL +#endif diff --git a/src/config.c b/src/config.c index 5379b0ec5..1283522ca 100644 --- a/src/config.c +++ b/src/config.c @@ -9,6 +9,7 @@ #include "fileops.h" #include "config.h" #include "git2/config.h" +#include "git2/sys/config.h" #include "vector.h" #include "buf_text.h" #include "config_file.h" diff --git a/src/config_file.c b/src/config_file.c index 8b51ab21b..a4ff8bb94 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -12,6 +12,7 @@ #include "buffer.h" #include "buf_text.h" #include "git2/config.h" +#include "git2/sys/config.h" #include "git2/types.h" #include "strmap.h" diff --git a/src/submodule.c b/src/submodule.c index 2fdaf2f77..0a22e3b13 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -7,6 +7,7 @@ #include "common.h" #include "git2/config.h" +#include "git2/sys/config.h" #include "git2/types.h" #include "git2/repository.h" #include "git2/index.h" diff --git a/tests-clar/config/backend.c b/tests-clar/config/backend.c index 28502a8ba..3fd6eb114 100644 --- a/tests-clar/config/backend.c +++ b/tests-clar/config/backend.c @@ -1,4 +1,5 @@ #include "clar_libgit2.h" +#include "git2/sys/config.h" void test_config_backend__checks_version(void) { From 83cc70d9fec5f81d07f9fc4133c9515527efb9af Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 19 Apr 2013 12:48:33 -0700 Subject: [PATCH 032/384] Move odb_backend implementors stuff into git2/sys This moves some of the odb_backend stuff that is related to the internals of an odb_backend implementation into include/git2/sys. Some of the stuff related to streaming I left in include/git2 because it seemed like it would be reasonably needed by a normal user who wanted to stream objects into and out of the ODB. Also, I added APIs for traversing the list of backends so that some of the tests would not need to access ODB internals. --- include/git2/indexer.h | 22 +----- include/git2/odb.h | 97 +++++++++++++++---------- include/git2/odb_backend.h | 123 +++++--------------------------- include/git2/sys/config.h | 5 +- include/git2/sys/odb_backend.h | 86 ++++++++++++++++++++++ include/git2/types.h | 20 ++++++ src/blob.c | 1 + src/odb.c | 22 ++++++ src/odb_loose.c | 2 +- src/odb_pack.c | 3 +- src/tag.c | 1 + src/transports/smart_protocol.c | 1 + tests-clar/object/raw/write.c | 3 +- tests-clar/odb/packed_one.c | 3 +- tests-clar/odb/sorting.c | 18 +++-- 15 files changed, 225 insertions(+), 182 deletions(-) create mode 100644 include/git2/sys/odb_backend.h diff --git a/include/git2/indexer.h b/include/git2/indexer.h index dfe6ae5aa..262dcd154 100644 --- a/include/git2/indexer.h +++ b/include/git2/indexer.h @@ -8,31 +8,11 @@ #define _INCLUDE_git_indexer_h__ #include "common.h" +#include "types.h" #include "oid.h" GIT_BEGIN_DECL -/** - * This is passed as the first argument to the callback to allow the - * user to see the progress. - */ -typedef struct git_transfer_progress { - unsigned int total_objects; - unsigned int indexed_objects; - unsigned int received_objects; - size_t received_bytes; -} git_transfer_progress; - - -/** - * Type for progress callbacks during indexing. Return a value less than zero - * to cancel the transfer. - * - * @param stats Structure containing information about the state of the transfer - * @param payload Payload provided by caller - */ -typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload); - typedef struct git_indexer_stream git_indexer_stream; /** diff --git a/include/git2/odb.h b/include/git2/odb.h index 8fd1a95be..b64436c4d 100644 --- a/include/git2/odb.h +++ b/include/git2/odb.h @@ -10,8 +10,6 @@ #include "common.h" #include "types.h" #include "oid.h" -#include "odb_backend.h" -#include "indexer.h" /** * @file git2/odb.h @@ -22,6 +20,11 @@ */ GIT_BEGIN_DECL +/** + * Function type for callbacks from git_odb_foreach. + */ +typedef int (*git_odb_foreach_cb)(const git_oid *id, void *payload); + /** * Create a new object database with no backends. * @@ -52,42 +55,6 @@ GIT_EXTERN(int) git_odb_new(git_odb **out); */ GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir); -/** - * Add a custom backend to an existing Object DB - * - * The backends are checked in relative ordering, based on the - * value of the `priority` parameter. - * - * Read for more information. - * - * @param odb database to add the backend to - * @param backend pointer to a git_odb_backend instance - * @param priority Value for ordering the backends queue - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority); - -/** - * Add a custom backend to an existing Object DB; this - * backend will work as an alternate. - * - * Alternate backends are always checked for objects *after* - * all the main backends have been exhausted. - * - * The backends are checked in relative ordering, based on the - * value of the `priority` parameter. - * - * Writing is disabled on alternate backends. - * - * Read for more information. - * - * @param odb database to add the backend to - * @param backend pointer to a git_odb_backend instance - * @param priority Value for ordering the backends queue - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority); - /** * Add an on-disk alternate to an existing Object DB. * @@ -406,6 +373,60 @@ GIT_EXTERN(size_t) git_odb_object_size(git_odb_object *object); */ GIT_EXTERN(git_otype) git_odb_object_type(git_odb_object *object); +/** + * Add a custom backend to an existing Object DB + * + * The backends are checked in relative ordering, based on the + * value of the `priority` parameter. + * + * Read for more information. + * + * @param odb database to add the backend to + * @param backend pointer to a git_odb_backend instance + * @param priority Value for ordering the backends queue + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority); + +/** + * Add a custom backend to an existing Object DB; this + * backend will work as an alternate. + * + * Alternate backends are always checked for objects *after* + * all the main backends have been exhausted. + * + * The backends are checked in relative ordering, based on the + * value of the `priority` parameter. + * + * Writing is disabled on alternate backends. + * + * Read for more information. + * + * @param odb database to add the backend to + * @param backend pointer to a git_odb_backend instance + * @param priority Value for ordering the backends queue + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority); + +/** + * Get the number of ODB backend objects + * + * @param odb object database + * @return number of backends in the ODB + */ +GIT_EXTERN(size_t) git_odb_num_backends(git_odb *odb); + +/** + * Lookup an ODB backend object by index + * + * @param out output pointer to ODB backend at pos + * @param odb object database + * @param pos index into object database backend list + * @return 0 on success; GIT_ENOTFOUND if pos is invalid; other errors < 0 + */ +GIT_EXTERN(int) git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos); + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h index dbc3981f6..d38005d15 100644 --- a/include/git2/odb_backend.h +++ b/include/git2/odb_backend.h @@ -7,106 +7,24 @@ #ifndef INCLUDE_git_odb_backend_h__ #define INCLUDE_git_odb_backend_h__ -#include "common.h" -#include "types.h" -#include "oid.h" -#include "indexer.h" +#include "git2/common.h" +#include "git2/types.h" /** * @file git2/backend.h * @brief Git custom backend functions - * @defgroup git_backend Git custom backend API + * @defgroup git_odb Git object database routines * @ingroup Git * @{ */ GIT_BEGIN_DECL -struct git_odb_stream; -struct git_odb_writepack; - /** - * Function type for callbacks from git_odb_foreach. + * Constructors for in-box ODB backends. */ -typedef int (*git_odb_foreach_cb)(const git_oid *id, void *payload); - -/** - * An instance for a custom backend - */ -struct git_odb_backend { - unsigned int version; - git_odb *odb; - - /* read and read_prefix each return to libgit2 a buffer which - * will be freed later. The buffer should be allocated using - * the function git_odb_backend_malloc to ensure that it can - * be safely freed later. */ - int (* read)( - void **, size_t *, git_otype *, - struct git_odb_backend *, - const git_oid *); - - /* To find a unique object given a prefix - * of its oid. - * The oid given must be so that the - * remaining (GIT_OID_HEXSZ - len)*4 bits - * are 0s. - */ - int (* read_prefix)( - git_oid *, - void **, size_t *, git_otype *, - struct git_odb_backend *, - const git_oid *, - size_t); - - int (* read_header)( - size_t *, git_otype *, - struct git_odb_backend *, - const git_oid *); - - /* The writer may assume that the object - * has already been hashed and is passed - * in the first parameter. - */ - int (* write)( - git_oid *, - struct git_odb_backend *, - const void *, - size_t, - git_otype); - - int (* writestream)( - struct git_odb_stream **, - struct git_odb_backend *, - size_t, - git_otype); - - int (* readstream)( - struct git_odb_stream **, - struct git_odb_backend *, - const git_oid *); - - int (* exists)( - struct git_odb_backend *, - const git_oid *); - - int (* refresh)(struct git_odb_backend *); - - int (* foreach)( - struct git_odb_backend *, - git_odb_foreach_cb cb, - void *payload); - - int (* writepack)( - struct git_odb_writepack **, - struct git_odb_backend *, - git_transfer_progress_callback progress_cb, - void *progress_payload); - - void (* free)(struct git_odb_backend *); -}; - -#define GIT_ODB_BACKEND_VERSION 1 -#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION} +GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir); +GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync); +GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file); /** Streaming mode */ enum { @@ -117,33 +35,24 @@ enum { /** A stream to read/write from a backend */ struct git_odb_stream { - struct git_odb_backend *backend; + git_odb_backend *backend; unsigned int mode; - int (*read)(struct git_odb_stream *stream, char *buffer, size_t len); - int (*write)(struct git_odb_stream *stream, const char *buffer, size_t len); - int (*finalize_write)(git_oid *oid_p, struct git_odb_stream *stream); - void (*free)(struct git_odb_stream *stream); + int (*read)(git_odb_stream *stream, char *buffer, size_t len); + int (*write)(git_odb_stream *stream, const char *buffer, size_t len); + int (*finalize_write)(git_oid *oid_p, git_odb_stream *stream); + void (*free)(git_odb_stream *stream); }; /** A stream to write a pack file to the ODB */ struct git_odb_writepack { - struct git_odb_backend *backend; + git_odb_backend *backend; - int (*add)(struct git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats); - int (*commit)(struct git_odb_writepack *writepack, git_transfer_progress *stats); - void (*free)(struct git_odb_writepack *writepack); + int (*add)(git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats); + int (*commit)(git_odb_writepack *writepack, git_transfer_progress *stats); + void (*free)(git_odb_writepack *writepack); }; -GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len); - -/** - * Constructors for in-box ODB backends. - */ -GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir); -GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync); -GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file); - GIT_END_DECL #endif diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index c9039e96e..1c9deba7c 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -4,8 +4,8 @@ * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ -#ifndef INCLUDE_git_sys_config_h__ -#define INCLUDE_git_sys_config_h__ +#ifndef INCLUDE_sys_git_config_backend_h__ +#define INCLUDE_sys_git_config_backend_h__ #include "git2/common.h" #include "git2/types.h" @@ -14,6 +14,7 @@ /** * @file git2/sys/config.h * @brief Git config backend routines + * @defgroup git_backend Git custom backend APIs * @ingroup Git * @{ */ diff --git a/include/git2/sys/odb_backend.h b/include/git2/sys/odb_backend.h new file mode 100644 index 000000000..3cd2734c0 --- /dev/null +++ b/include/git2/sys/odb_backend.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_sys_git_odb_backend_h__ +#define INCLUDE_sys_git_odb_backend_h__ + +#include "git2/common.h" +#include "git2/types.h" +#include "git2/oid.h" +#include "git2/odb.h" + +/** + * @file git2/sys/backend.h + * @brief Git custom backend implementors functions + * @defgroup git_backend Git custom backend APIs + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * An instance for a custom backend + */ +struct git_odb_backend { + unsigned int version; + git_odb *odb; + + /* read and read_prefix each return to libgit2 a buffer which + * will be freed later. The buffer should be allocated using + * the function git_odb_backend_malloc to ensure that it can + * be safely freed later. */ + int (* read)( + void **, size_t *, git_otype *, git_odb_backend *, const git_oid *); + + /* To find a unique object given a prefix + * of its oid. + * The oid given must be so that the + * remaining (GIT_OID_HEXSZ - len)*4 bits + * are 0s. + */ + int (* read_prefix)( + git_oid *, void **, size_t *, git_otype *, + git_odb_backend *, const git_oid *, size_t); + + int (* read_header)( + size_t *, git_otype *, git_odb_backend *, const git_oid *); + + /* The writer may assume that the object + * has already been hashed and is passed + * in the first parameter. + */ + int (* write)( + git_oid *, git_odb_backend *, const void *, size_t, git_otype); + + int (* writestream)( + git_odb_stream **, git_odb_backend *, size_t, git_otype); + + int (* readstream)( + git_odb_stream **, git_odb_backend *, const git_oid *); + + int (* exists)( + git_odb_backend *, const git_oid *); + + int (* refresh)(git_odb_backend *); + + int (* foreach)( + git_odb_backend *, git_odb_foreach_cb cb, void *payload); + + int (* writepack)( + git_odb_writepack **, git_odb_backend *, + git_transfer_progress_callback progress_cb, void *progress_payload); + + void (* free)(git_odb_backend *); +}; + +#define GIT_ODB_BACKEND_VERSION 1 +#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION} + +GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len); + +GIT_END_DECL + +#endif diff --git a/include/git2/types.h b/include/git2/types.h index bc15050ce..aca9ed927 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -196,6 +196,26 @@ typedef struct git_push git_push; typedef struct git_remote_head git_remote_head; typedef struct git_remote_callbacks git_remote_callbacks; +/** + * This is passed as the first argument to the callback to allow the + * user to see the progress. + */ +typedef struct git_transfer_progress { + unsigned int total_objects; + unsigned int indexed_objects; + unsigned int received_objects; + size_t received_bytes; +} git_transfer_progress; + +/** + * Type for progress callbacks during indexing. Return a value less than zero + * to cancel the transfer. + * + * @param stats Structure containing information about the state of the transfer + * @param payload Payload provided by caller + */ +typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload); + /** @} */ GIT_END_DECL diff --git a/src/blob.c b/src/blob.c index c0514fc13..11e1f4d77 100644 --- a/src/blob.c +++ b/src/blob.c @@ -8,6 +8,7 @@ #include "git2/common.h" #include "git2/object.h" #include "git2/repository.h" +#include "git2/odb_backend.h" #include "common.h" #include "blob.h" diff --git a/src/odb.c b/src/odb.c index c98df247c..ecdaf7ac2 100644 --- a/src/odb.c +++ b/src/odb.c @@ -8,6 +8,7 @@ #include "common.h" #include #include "git2/object.h" +#include "git2/sys/odb_backend.h" #include "fileops.h" #include "hash.h" #include "odb.h" @@ -403,6 +404,27 @@ int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority) return add_backend_internal(odb, backend, priority, 1); } +size_t git_odb_num_backends(git_odb *odb) +{ + assert(odb); + return odb->backends.length; +} + +int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos) +{ + backend_internal *internal; + + assert(odb && odb); + internal = git_vector_get(&odb->backends, pos); + + if (internal && internal->backend) { + *out = internal->backend; + return 0; + } + + return GIT_ENOTFOUND; +} + static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates, int alternate_depth) { git_odb_backend *loose, *packed; diff --git a/src/odb_loose.c b/src/odb_loose.c index 68083f7fd..e78172cf6 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -8,7 +8,7 @@ #include "common.h" #include #include "git2/object.h" -#include "git2/oid.h" +#include "git2/sys/odb_backend.h" #include "fileops.h" #include "hash.h" #include "odb.h" diff --git a/src/odb_pack.c b/src/odb_pack.c index 7240a4ac7..773e14974 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -8,7 +8,8 @@ #include "common.h" #include #include "git2/repository.h" -#include "git2/oid.h" +#include "git2/indexer.h" +#include "git2/sys/odb_backend.h" #include "fileops.h" #include "hash.h" #include "odb.h" diff --git a/src/tag.c b/src/tag.c index 735ba7e1d..c82decbe3 100644 --- a/src/tag.c +++ b/src/tag.c @@ -13,6 +13,7 @@ #include "git2/object.h" #include "git2/repository.h" #include "git2/signature.h" +#include "git2/odb_backend.h" void git_tag__free(git_tag *tag) { diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 8acedeb49..90851980c 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -5,6 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ #include "git2.h" +#include "git2/odb_backend.h" #include "smart.h" #include "refs.h" diff --git a/tests-clar/object/raw/write.c b/tests-clar/object/raw/write.c index 1b28d0df7..60aa31f6a 100644 --- a/tests-clar/object/raw/write.c +++ b/tests-clar/object/raw/write.c @@ -1,5 +1,6 @@ - #include "clar_libgit2.h" +#include "git2/odb_backend.h" + #include "fileops.h" #include "odb.h" diff --git a/tests-clar/odb/packed_one.c b/tests-clar/odb/packed_one.c index e9d246c23..4f9bde9ed 100644 --- a/tests-clar/odb/packed_one.c +++ b/tests-clar/odb/packed_one.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" -#include "odb.h" +#include "git2/odb_backend.h" + #include "pack_data_one.h" #include "pack.h" diff --git a/tests-clar/odb/sorting.c b/tests-clar/odb/sorting.c index b4f9e44bc..147a160c8 100644 --- a/tests-clar/odb/sorting.c +++ b/tests-clar/odb/sorting.c @@ -1,13 +1,12 @@ #include "clar_libgit2.h" -#include "git2/odb_backend.h" -#include "odb.h" +#include "git2/sys/odb_backend.h" typedef struct { git_odb_backend base; - int position; + size_t position; } fake_backend; -static git_odb_backend *new_backend(int position) +static git_odb_backend *new_backend(size_t position) { fake_backend *b; @@ -22,14 +21,13 @@ static git_odb_backend *new_backend(int position) static void check_backend_sorting(git_odb *odb) { - unsigned int i; - - for (i = 0; i < odb->backends.length; ++i) { - fake_backend *internal = - *((fake_backend **)git_vector_get(&odb->backends, i)); + size_t i, max_i = git_odb_num_backends(odb); + fake_backend *internal; + for (i = 0; i < max_i; ++i) { + cl_git_pass(git_odb_get_backend((git_odb_backend **)&internal, odb, i)); cl_assert(internal != NULL); - cl_assert(internal->position == (int)i); + cl_assert_equal_sz(i, internal->position); } } From 7cc3c9202722c53bf46e5a8f8d0d7180423a7632 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 29 Jan 2013 07:48:36 -0600 Subject: [PATCH 033/384] Added git_repository_new function --- include/git2/repository.h | 8 ++++++++ src/repository.c | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/include/git2/repository.h b/include/git2/repository.h index ed837b359..f55ab798f 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -136,6 +136,14 @@ GIT_EXTERN(int) git_repository_open_ext( */ GIT_EXTERN(int) git_repository_open_bare(git_repository **out, const char *bare_path); +/** + * Create a new repository with neither backends nor config object + * + * Note that this is only useful if you wish to associate the repository + * with a non-filesystem-backed object database and config store. + */ +GIT_EXTERN(int) git_repository_new(git_repository **out); + /** * Free a previously allocated repository * diff --git a/src/repository.c b/src/repository.c index 64ab2f4db..72b5a079a 100644 --- a/src/repository.c +++ b/src/repository.c @@ -129,6 +129,12 @@ static git_repository *repository_alloc(void) return repo; } +int git_repository_new(git_repository **out) +{ + *out = repository_alloc(); + return 0; +} + static int load_config_data(git_repository *repo) { int is_bare; From 1384b688d0bb5cd784c453fffef69d27e3db44ca Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 19 Apr 2013 13:00:12 -0700 Subject: [PATCH 034/384] Move some low-level repo fns to include/git2/sys --- include/git2/repository.h | 70 ----------------------- include/git2/sys/repository.h | 92 +++++++++++++++++++++++++++++++ src/repository.c | 1 + tests-clar/diff/patch.c | 2 + tests-clar/repo/setters.c | 2 + tests-clar/status/worktree_init.c | 6 +- 6 files changed, 101 insertions(+), 72 deletions(-) create mode 100644 include/git2/sys/repository.h diff --git a/include/git2/repository.h b/include/git2/repository.h index f55ab798f..08024cd89 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -136,14 +136,6 @@ GIT_EXTERN(int) git_repository_open_ext( */ GIT_EXTERN(int) git_repository_open_bare(git_repository **out, const char *bare_path); -/** - * Create a new repository with neither backends nor config object - * - * Note that this is only useful if you wish to associate the repository - * with a non-filesystem-backed object database and config store. - */ -GIT_EXTERN(int) git_repository_new(git_repository **out); - /** * Free a previously allocated repository * @@ -408,21 +400,6 @@ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo); */ GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo); -/** - * Set the configuration file for this repository - * - * This configuration file will be used for all configuration - * queries involving this repository. - * - * The repository will keep a reference to the config file; - * the user must still free the config after setting it - * to the repository, or it will leak. - * - * @param repo A repository object - * @param config A Config object - */ -GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *config); - /** * Get the Object Database for this repository. * @@ -439,21 +416,6 @@ GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *con */ GIT_EXTERN(int) git_repository_odb(git_odb **out, git_repository *repo); -/** - * Set the Object Database for this repository - * - * The ODB will be used for all object-related operations - * involving this repository. - * - * The repository will keep a reference to the ODB; the user - * must still free the ODB object after setting it to the - * repository, or it will leak. - * - * @param repo A repository object - * @param odb An ODB object - */ -GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb); - /** * Get the Reference Database Backend for this repository. * @@ -470,23 +432,6 @@ GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb); */ GIT_EXTERN(int) git_repository_refdb(git_refdb **out, git_repository *repo); -/** - * Set the Reference Database Backend for this repository - * - * The refdb will be used for all reference related operations - * involving this repository. - * - * The repository will keep a reference to the refdb; the user - * must still free the refdb object after setting it to the - * repository, or it will leak. - * - * @param repo A repository object - * @param refdb An refdb object - */ -GIT_EXTERN(void) git_repository_set_refdb( - git_repository *repo, - git_refdb *refdb); - /** * Get the Index file for this repository. * @@ -503,21 +448,6 @@ GIT_EXTERN(void) git_repository_set_refdb( */ GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo); -/** - * Set the index file for this repository - * - * This index will be used for all index-related operations - * involving this repository. - * - * The repository will keep a reference to the index file; - * the user must still free the index after setting it - * to the repository, or it will leak. - * - * @param repo A repository object - * @param index An index object - */ -GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index); - /** * Retrieve git's prepared message * diff --git a/include/git2/sys/repository.h b/include/git2/sys/repository.h new file mode 100644 index 000000000..34bc17516 --- /dev/null +++ b/include/git2/sys/repository.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_sys_git_repository_h__ +#define INCLUDE_sys_git_repository_h__ + +/** + * @file git2/sys/repository.h + * @brief Git repository custom implementation routines + * @defgroup git_backend Git custom backend APIs + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Create a new repository with neither backends nor config object + * + * Note that this is only useful if you wish to associate the repository + * with a non-filesystem-backed object database and config store. + * + * @param out The blank repository + * @return 0 on success, or an error code + */ +GIT_EXTERN(int) git_repository_new(git_repository **out); + +/** + * Set the configuration file for this repository + * + * This configuration file will be used for all configuration + * queries involving this repository. + * + * The repository will keep a reference to the config file; + * the user must still free the config after setting it + * to the repository, or it will leak. + * + * @param repo A repository object + * @param config A Config object + */ +GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *config); + +/** + * Set the Object Database for this repository + * + * The ODB will be used for all object-related operations + * involving this repository. + * + * The repository will keep a reference to the ODB; the user + * must still free the ODB object after setting it to the + * repository, or it will leak. + * + * @param repo A repository object + * @param odb An ODB object + */ +GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb); + +/** + * Set the Reference Database Backend for this repository + * + * The refdb will be used for all reference related operations + * involving this repository. + * + * The repository will keep a reference to the refdb; the user + * must still free the refdb object after setting it to the + * repository, or it will leak. + * + * @param repo A repository object + * @param refdb An refdb object + */ +GIT_EXTERN(void) git_repository_set_refdb(git_repository *repo, git_refdb *refdb); + +/** + * Set the index file for this repository + * + * This index will be used for all index-related operations + * involving this repository. + * + * The repository will keep a reference to the index file; + * the user must still free the index after setting it + * to the repository, or it will leak. + * + * @param repo A repository object + * @param index An index object + */ +GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index); + +/** @} */ +GIT_END_DECL +#endif diff --git a/src/repository.c b/src/repository.c index 72b5a079a..a16f69da4 100644 --- a/src/repository.c +++ b/src/repository.c @@ -9,6 +9,7 @@ #include "git2/object.h" #include "git2/refdb.h" +#include "git2/sys/repository.h" #include "common.h" #include "repository.h" diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c index 4d17da468..d41f3f12d 100644 --- a/tests-clar/diff/patch.c +++ b/tests-clar/diff/patch.c @@ -1,4 +1,6 @@ #include "clar_libgit2.h" +#include "git2/sys/repository.h" + #include "diff_helpers.h" #include "repository.h" #include "buf_text.h" diff --git a/tests-clar/repo/setters.c b/tests-clar/repo/setters.c index 7e482dee1..063d76c8d 100644 --- a/tests-clar/repo/setters.c +++ b/tests-clar/repo/setters.c @@ -1,4 +1,6 @@ #include "clar_libgit2.h" +#include "git2/sys/repository.h" + #include "buffer.h" #include "posix.h" #include "util.h" diff --git a/tests-clar/status/worktree_init.c b/tests-clar/status/worktree_init.c index b67107aec..296c27c86 100644 --- a/tests-clar/status/worktree_init.c +++ b/tests-clar/status/worktree_init.c @@ -1,4 +1,6 @@ #include "clar_libgit2.h" +#include "git2/sys/repository.h" + #include "fileops.h" #include "ignore.h" #include "status_helpers.h" @@ -321,10 +323,10 @@ void test_status_worktree_init__new_staged_file_must_handle_crlf(void) cl_set_cleanup(&cleanup_new_repo, "getting_started"); cl_git_pass(git_repository_init(&repo, "getting_started", 0)); - // Ensure that repo has core.autocrlf=true + /* Ensure that repo has core.autocrlf=true */ cl_repo_set_bool(repo, "core.autocrlf", true); - cl_git_mkfile("getting_started/testfile.txt", "content\r\n"); // Content with CRLF + cl_git_mkfile("getting_started/testfile.txt", "content\r\n"); /* Content with CRLF */ cl_git_pass(git_repository_index(&index, repo)); cl_git_pass(git_index_add_bypath(index, "testfile.txt")); From 9255039898bf4c625f678f390c8075c11d10cad0 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 29 Jan 2013 09:53:23 -0600 Subject: [PATCH 035/384] Added git_commit_create_oid --- include/git2/commit.h | 20 +++++++++++++++++ src/commit.c | 50 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/include/git2/commit.h b/include/git2/commit.h index 764053eaa..e7ef51816 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -287,6 +287,26 @@ GIT_EXTERN(int) git_commit_create_v( int parent_count, ...); +/** + * Create a new commit in the repository, as with `git_commit_create`, + * using `git_oid` instances as parameters instead of `git_object`. + * + * See documentation for `git_commit_create` for information about the + * parameters, as the meaning is identical excepting that `tree` and + * `parents` now take `git_oid`. + */ +GIT_EXTERN(int) git_commit_create_oid( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_oid *tree, + int parent_count, + const git_oid *parents[]); + /** @} */ GIT_END_DECL #endif diff --git a/src/commit.c b/src/commit.c index c7b83ed43..e6bfd95ce 100644 --- a/src/commit.c +++ b/src/commit.c @@ -76,7 +76,7 @@ int git_commit_create_v( return res; } -int git_commit_create( +int git_commit_create_oid( git_oid *oid, git_repository *repo, const char *update_ref, @@ -84,22 +84,18 @@ int git_commit_create( const git_signature *committer, const char *message_encoding, const char *message, - const git_tree *tree, + const git_oid *tree, int parent_count, - const git_commit *parents[]) + const git_oid *parents[]) { git_buf commit = GIT_BUF_INIT; int i; git_odb *odb; - assert(git_object_owner((const git_object *)tree) == repo); + git_oid__writebuf(&commit, "tree ", tree); - git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree)); - - for (i = 0; i < parent_count; ++i) { - assert(git_object_owner((const git_object *)parents[i]) == repo); - git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i])); - } + for (i = 0; i < parent_count; ++i) + git_oid__writebuf(&commit, "parent ", parents[i]); git_signature__writebuf(&commit, "author ", author); git_signature__writebuf(&commit, "committer ", committer); @@ -131,6 +127,40 @@ on_error: return -1; } +int git_commit_create( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree, + int parent_count, + const git_commit *parents[]) +{ + int retval, i; + const git_oid **parent_oids; + + assert(git_object_owner((const git_object *)tree) == repo); + + parent_oids = git__malloc(parent_count * sizeof(git_oid *)); + GITERR_CHECK_ALLOC(parent_oids); + + for (i = 0; i < parent_count; ++i) { + assert(git_object_owner((const git_object *)parents[i]) == repo); + parent_oids[i] = git_object_id((const git_object *)parents[i]); + } + + retval = git_commit_create_oid(oid, repo, update_ref, author, committer, + message_encoding, message, + git_object_id((const git_object *)tree), + parent_count, parent_oids); + + git__free((void *)parent_oids); + return retval; +} + int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len) { const char *buffer = data; From 9233b3de4ea264a8ae846c784acc70c505022d8b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 19 Apr 2013 13:17:29 -0700 Subject: [PATCH 036/384] Move git_commit_create_from_oids into sys/commit.h Actually this renames git_commit_create_oid to git_commit_create_from_oids and moves the API declaration to include/git2/sys/commit.h since it is a dangerous API for general use (because it doesn't check that the OID list items actually refer to real objects). --- include/git2/commit.h | 108 +++++++++++++++----------------------- include/git2/sys/commit.h | 45 ++++++++++++++++ src/commit.c | 81 ++++++++++++++-------------- 3 files changed, 129 insertions(+), 105 deletions(-) create mode 100644 include/git2/sys/commit.h diff --git a/include/git2/commit.h b/include/git2/commit.h index e7ef51816..0f7601252 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -201,14 +201,12 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor( unsigned int n); /** - * Create a new commit in the repository using `git_object` - * instances as parameters. + * Create new commit in the repository from a list of `git_object` pointers * - * The message will not be cleaned up. This can be achieved - * through `git_message_prettify()`. + * The message will not be cleaned up automatically. You can do that with + * the `git_message_prettify()` function. * - * @param id Pointer where to store the OID of the - * newly created commit + * @param id Pointer in which to store the OID of the newly created commit * * @param repo Repository where to store the commit * @@ -219,93 +217,69 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor( * make it point to this commit. If the reference doesn't * exist yet, it will be created. * - * @param author Signature representing the author and the authory - * time of this commit + * @param author Signature with author and author time of commit * - * @param committer Signature representing the committer and the - * commit time of this commit + * @param committer Signature with committer and * commit time of commit * * @param message_encoding The encoding for the message in the - * commit, represented with a standard encoding name. - * E.g. "UTF-8". If NULL, no encoding header is written and - * UTF-8 is assumed. + * commit, represented with a standard encoding name. + * E.g. "UTF-8". If NULL, no encoding header is written and + * UTF-8 is assumed. * * @param message Full message for this commit * * @param tree An instance of a `git_tree` object that will - * be used as the tree for the commit. This tree object must - * also be owned by the given `repo`. + * be used as the tree for the commit. This tree object must + * also be owned by the given `repo`. * * @param parent_count Number of parents for this commit * * @param parents[] Array of `parent_count` pointers to `git_commit` - * objects that will be used as the parents for this commit. This - * array may be NULL if `parent_count` is 0 (root commit). All the - * given commits must be owned by the `repo`. + * objects that will be used as the parents for this commit. This + * array may be NULL if `parent_count` is 0 (root commit). All the + * given commits must be owned by the `repo`. * * @return 0 or an error code * The created commit will be written to the Object Database and * the given reference will be updated to point to it */ GIT_EXTERN(int) git_commit_create( - git_oid *id, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message_encoding, - const char *message, - const git_tree *tree, - int parent_count, - const git_commit *parents[]); + git_oid *id, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree, + int parent_count, + const git_commit *parents[]); /** - * Create a new commit in the repository using a variable - * argument list. + * Create new commit in the repository using a variable argument list. * - * The message will be cleaned up from excess whitespace - * it will be made sure that the last line ends with a '\n'. + * The message will be cleaned up from excess whitespace and it will be made + * sure that the last line ends with a '\n'. * - * The parents for the commit are specified as a variable - * list of pointers to `const git_commit *`. Note that this - * is a convenience method which may not be safe to export - * for certain languages or compilers + * The parents for the commit are specified as a variable list of pointers + * to `const git_commit *`. Note that this is a convenience method which may + * not be safe to export for certain languages or compilers * - * All other parameters remain the same + * All other parameters remain the same at `git_commit_create()`. * * @see git_commit_create */ GIT_EXTERN(int) git_commit_create_v( - git_oid *id, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message_encoding, - const char *message, - const git_tree *tree, - int parent_count, - ...); - -/** - * Create a new commit in the repository, as with `git_commit_create`, - * using `git_oid` instances as parameters instead of `git_object`. - * - * See documentation for `git_commit_create` for information about the - * parameters, as the meaning is identical excepting that `tree` and - * `parents` now take `git_oid`. - */ -GIT_EXTERN(int) git_commit_create_oid( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message_encoding, - const char *message, - const git_oid *tree, - int parent_count, - const git_oid *parents[]); + git_oid *id, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree, + int parent_count, + ...); /** @} */ GIT_END_DECL diff --git a/include/git2/sys/commit.h b/include/git2/sys/commit.h new file mode 100644 index 000000000..096e028c5 --- /dev/null +++ b/include/git2/sys/commit.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_sys_git_commit_h__ +#define INCLUDE_sys_git_commit_h__ + +#include "git2/common.h" +#include "git2/types.h" +#include "git2/oid.h" + +/** + * @file git2/sys/commit.h + * @brief Low-level Git commit creation + * @defgroup git_backend Git custom backend APIs + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Create new commit in the repository from a list of `git_oid` values + * + * See documentation for `git_commit_create()` for information about the + * parameters, as the meaning is identical excepting that `tree` and + * `parents` now take `git_oid`. This is a dangerous API in that the + * `parents` list of `git_oid`s in not checked for validity. + */ +GIT_EXTERN(int) git_commit_create_from_oids( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_oid *tree, + int parent_count, + const git_oid *parents[]); + +/** @} */ +GIT_END_DECL +#endif diff --git a/src/commit.c b/src/commit.c index e6bfd95ce..dd416920d 100644 --- a/src/commit.c +++ b/src/commit.c @@ -9,6 +9,7 @@ #include "git2/object.h" #include "git2/repository.h" #include "git2/signature.h" +#include "git2/sys/commit.h" #include "common.h" #include "odb.h" @@ -44,16 +45,16 @@ void git_commit__free(git_commit *commit) } int git_commit_create_v( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message_encoding, - const char *message, - const git_tree *tree, - int parent_count, - ...) + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree, + int parent_count, + ...) { va_list ap; int i, res; @@ -76,22 +77,25 @@ int git_commit_create_v( return res; } -int git_commit_create_oid( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message_encoding, - const char *message, - const git_oid *tree, - int parent_count, - const git_oid *parents[]) +int git_commit_create_from_oids( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_oid *tree, + int parent_count, + const git_oid *parents[]) { git_buf commit = GIT_BUF_INIT; int i; git_odb *odb; + assert(oid && repo && tree && parent_count >= 0); + assert(git_object_owner((const git_object *)tree) == repo); + git_oid__writebuf(&commit, "tree ", tree); for (i = 0; i < parent_count; ++i) @@ -128,21 +132,21 @@ on_error: } int git_commit_create( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message_encoding, - const char *message, - const git_tree *tree, - int parent_count, - const git_commit *parents[]) + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree, + int parent_count, + const git_commit *parents[]) { - int retval, i; + int retval, i; const git_oid **parent_oids; - assert(git_object_owner((const git_object *)tree) == repo); + assert(parent_count >= 0); parent_oids = git__malloc(parent_count * sizeof(git_oid *)); GITERR_CHECK_ALLOC(parent_oids); @@ -152,13 +156,14 @@ int git_commit_create( parent_oids[i] = git_object_id((const git_object *)parents[i]); } - retval = git_commit_create_oid(oid, repo, update_ref, author, committer, - message_encoding, message, - git_object_id((const git_object *)tree), - parent_count, parent_oids); + retval = git_commit_create_from_oids( + oid, repo, update_ref, author, committer, + message_encoding, message, + git_object_id((const git_object *)tree), parent_count, parent_oids); git__free((void *)parent_oids); - return retval; + + return retval; } int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len) From 4dcd87801972e1b880afa9cd0998842bae7af5b5 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 19 Apr 2013 17:17:44 -0700 Subject: [PATCH 037/384] Move refdb_backend to include/git2/sys This moves most of the refdb stuff over to the include/git2/sys directory, with some minor shifts in function organization. While I was making the necessary updates, I also removed the trailing whitespace in a few files that I modified just because I was there and it was bugging me. --- include/git2/refdb.h | 14 ------ include/git2/{ => sys}/refdb_backend.h | 18 ++++++-- src/refdb.c | 9 ++-- src/refdb_fs.c | 30 ++++++------- src/refs.c | 45 +++++++++---------- src/util.h | 2 + tests-clar/refdb/inmemory.c | 62 ++++++++++++++------------ tests-clar/refdb/testdb.c | 48 +++++++++----------- tests-clar/refdb/testdb.h | 5 +++ 9 files changed, 119 insertions(+), 114 deletions(-) rename include/git2/{ => sys}/refdb_backend.h (86%) diff --git a/include/git2/refdb.h b/include/git2/refdb.h index 0e3ec5eaf..9278b1116 100644 --- a/include/git2/refdb.h +++ b/include/git2/refdb.h @@ -81,20 +81,6 @@ GIT_EXTERN(int) git_refdb_compress(git_refdb *refdb); */ GIT_EXTERN(void) git_refdb_free(git_refdb *refdb); -/** - * Sets the custom backend to an existing reference DB - * - * Read for more information. - * - * @param refdb database to add the backend to - * @param backend pointer to a git_refdb_backend instance - * @param priority Value for ordering the backends queue - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_refdb_set_backend( - git_refdb *refdb, - git_refdb_backend *backend); - /** @} */ GIT_END_DECL diff --git a/include/git2/refdb_backend.h b/include/git2/sys/refdb_backend.h similarity index 86% rename from include/git2/refdb_backend.h rename to include/git2/sys/refdb_backend.h index 20eb6a9dd..dcdf7bcb8 100644 --- a/include/git2/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -7,9 +7,9 @@ #ifndef INCLUDE_git_refdb_backend_h__ #define INCLUDE_git_refdb_backend_h__ -#include "common.h" -#include "types.h" -#include "oid.h" +#include "git2/common.h" +#include "git2/types.h" +#include "git2/oid.h" /** * @file git2/refdb_backend.h @@ -103,6 +103,18 @@ GIT_EXTERN(int) git_refdb_backend_fs( struct git_refdb_backend **backend_out, git_repository *repo); +/** + * Sets the custom backend to an existing reference DB + * + * @param refdb database to add the backend to + * @param backend pointer to a git_refdb_backend instance + * @param priority Value for ordering the backends queue + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_refdb_set_backend( + git_refdb *refdb, + git_refdb_backend *backend); + GIT_END_DECL #endif diff --git a/src/refdb.c b/src/refdb.c index 2a0fd702c..0675be638 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -7,15 +7,16 @@ #include "common.h" #include "posix.h" + #include "git2/object.h" #include "git2/refs.h" #include "git2/refdb.h" +#include "git2/sys/refdb_backend.h" + #include "hash.h" #include "refdb.h" #include "refs.h" -#include "git2/refdb_backend.h" - int git_refdb_new(git_refdb **out, git_repository *repo) { git_refdb *db; @@ -74,11 +75,11 @@ int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend) int git_refdb_compress(git_refdb *db) { assert(db); - + if (db->backend->compress) { return db->backend->compress(db->backend); } - + return 0; } diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 56b2b6a99..4d5d6006d 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include GIT__USE_STRMAP; @@ -61,7 +61,7 @@ static int reference_read( /* Determine the full path of the file */ if (git_buf_joinpath(&path, repo_path, ref_name) < 0) return -1; - + result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, NULL, updated); git_buf_free(&path); @@ -174,7 +174,7 @@ static int packed_load(refdb_fs_backend *backend) ref_cache->packfile = git_strmap_alloc(); GITERR_CHECK_ALLOC(ref_cache->packfile); } - + result = reference_read(&packfile, &ref_cache->packfile_time, backend->path, GIT_PACKEDREFS_FILE, &updated); @@ -192,7 +192,7 @@ static int packed_load(refdb_fs_backend *backend) if (result < 0) return -1; - + if (!updated) return 0; @@ -433,7 +433,7 @@ static int loose_lookup( } else { if ((error = loose_parse_oid(&oid, &ref_file)) < 0) goto done; - + *out = git_reference__alloc(ref_name, &oid, NULL); } @@ -455,19 +455,19 @@ static int packed_map_entry( if (packed_load(backend) < 0) return -1; - + /* Look up on the packfile */ packfile_refs = backend->refcache.packfile; *pos = git_strmap_lookup_index(packfile_refs, ref_name); - + if (!git_strmap_valid_index(packfile_refs, *pos)) { giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref_name); return GIT_ENOTFOUND; } *entry = git_strmap_value_at(packfile_refs, *pos); - + return 0; } @@ -479,14 +479,14 @@ static int packed_lookup( struct packref *entry; khiter_t pos; int error = 0; - + if ((error = packed_map_entry(&entry, &pos, backend, ref_name)) < 0) return error; if ((*out = git_reference__alloc(ref_name, &entry->oid, &entry->peel)) == NULL) return -1; - + return 0; } @@ -582,7 +582,7 @@ static int refdb_fs_backend__foreach( git_buf refs_path = GIT_BUF_INIT; const char *ref_name; void *ref = NULL; - + GIT_UNUSED(ref); assert(_backend); @@ -590,7 +590,7 @@ static int refdb_fs_backend__foreach( if (packed_load(backend) < 0) return -1; - + /* list all the packed references first */ if (list_type & GIT_REF_OID) { git_strmap_foreach(backend->refcache.packfile, ref_name, ref, { @@ -924,7 +924,7 @@ static int refdb_fs_backend__delete( repo = backend->repo; /* If a loose reference exists, remove it from the filesystem */ - + if (git_buf_joinpath(&loose_path, repo->path_repository, ref->name) < 0) return -1; @@ -932,7 +932,7 @@ static int refdb_fs_backend__delete( error = p_unlink(loose_path.ptr); loose_deleted = 1; } - + git_buf_free(&loose_path); if (error != 0) @@ -946,7 +946,7 @@ static int refdb_fs_backend__delete( error = packed_write(backend); } - + if (pack_error == GIT_ENOTFOUND) error = loose_deleted ? 0 : GIT_ENOTFOUND; else diff --git a/src/refs.c b/src/refs.c index 5b5812aae..d9291e56f 100644 --- a/src/refs.c +++ b/src/refs.c @@ -19,7 +19,6 @@ #include #include #include -#include GIT__USE_STRMAP; @@ -255,10 +254,10 @@ int git_reference_lookup_resolved( max_nesting = MAX_NESTING_LEVEL; else if (max_nesting < 0) max_nesting = DEFAULT_NESTING_LEVEL; - + strncpy(scan_name, name, GIT_REFNAME_MAX); scan_type = GIT_REF_SYMBOLIC; - + if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) return -1; @@ -276,7 +275,7 @@ int git_reference_lookup_resolved( if ((error = git_refdb_lookup(&ref, refdb, scan_name)) < 0) return error; - + scan_type = ref->type; } @@ -354,7 +353,7 @@ static int reference__create( git_refdb *refdb; git_reference *ref = NULL; int error = 0; - + if (ref_out) *ref_out = NULL; @@ -369,7 +368,7 @@ static int reference__create( } else { ref = git_reference__alloc_symbolic(name, symbolic); } - + GITERR_CHECK_ALLOC(ref); ref->db = refdb; @@ -377,7 +376,7 @@ static int reference__create( git_reference_free(ref); return error; } - + if (ref_out == NULL) git_reference_free(ref); else @@ -397,17 +396,17 @@ int git_reference_create( int error = 0; assert(repo && name && oid); - + /* Sanity check the reference being created - target must exist. */ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) return error; - + if (!git_odb_exists(odb, oid)) { giterr_set(GITERR_REFERENCE, "Target OID for the reference doesn't exist on the repository"); return -1; } - + return reference__create(ref_out, repo, name, oid, NULL, force); } @@ -422,7 +421,7 @@ int git_reference_symbolic_create( int error = 0; assert(repo && name && target); - + if ((error = git_reference__normalize_name_lax( normalized, sizeof(normalized), target)) < 0) return error; @@ -436,7 +435,7 @@ int git_reference_set_target( const git_oid *id) { assert(out && ref && id); - + if (ref->type != GIT_REF_OID) { giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference"); return -1; @@ -451,13 +450,13 @@ int git_reference_symbolic_set_target( const char *target) { assert(out && ref && target); - + if (ref->type != GIT_REF_SYMBOLIC) { giterr_set(GITERR_REFERENCE, "Cannot set symbolic target on a direct reference"); return -1; } - + return git_reference_symbolic_create(out, ref->db->repo, ref->name, target, 1); } @@ -473,7 +472,7 @@ int git_reference_rename( git_reference *result = NULL; int error = 0; int reference_has_log; - + *out = NULL; normalization_flags = ref->type == GIT_REF_SYMBOLIC ? @@ -488,7 +487,7 @@ int git_reference_rename( * Create the new reference. */ if (ref->type == GIT_REF_OID) { - result = git_reference__alloc(new_name, &ref->target.oid, &ref->peel); + result = git_reference__alloc(new_name, &ref->target.oid, &ref->peel); } else if (ref->type == GIT_REF_SYMBOLIC) { result = git_reference__alloc_symbolic(new_name, ref->target.symbolic); } else { @@ -509,11 +508,11 @@ int git_reference_rename( /* Now delete the old ref and save the new one. */ if ((error = git_refdb_delete(ref->db, ref)) < 0) goto on_error; - + /* Save the new reference. */ if ((error = git_refdb_write(ref->db, result)) < 0) goto rollback; - + /* Update HEAD it was poiting to the reference being renamed. */ if (should_head_be_updated && (error = git_repository_set_head(ref->db->repo, new_name)) < 0) { giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference"); @@ -547,7 +546,7 @@ int git_reference_resolve(git_reference **ref_out, const git_reference *ref) switch (git_reference_type(ref)) { case GIT_REF_OID: return git_reference_lookup(ref_out, ref->db->repo, ref->name); - + case GIT_REF_SYMBOLIC: return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1); @@ -846,7 +845,7 @@ static int reference__update_terminal( if (nesting > MAX_NESTING_LEVEL) return GIT_ENOTFOUND; - + error = git_reference_lookup(&ref, repo, ref_name); /* If we haven't found the reference at all, create a new reference. */ @@ -854,10 +853,10 @@ static int reference__update_terminal( giterr_clear(); return git_reference_create(NULL, repo, ref_name, oid, 0); } - + if (error < 0) return error; - + /* If the ref is a symbolic reference, follow its target. */ if (git_reference_type(ref) == GIT_REF_SYMBOLIC) { error = reference__update_terminal(repo, git_reference_symbolic_target(ref), oid, @@ -867,7 +866,7 @@ static int reference__update_terminal( git_reference_free(ref); error = git_reference_create(NULL, repo, ref_name, oid, 1); } - + return error; } diff --git a/src/util.h b/src/util.h index c0f271997..af3ef0b46 100644 --- a/src/util.h +++ b/src/util.h @@ -7,6 +7,8 @@ #ifndef INCLUDE_util_h__ #define INCLUDE_util_h__ +#include "common.h" + #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define bitsizeof(x) (CHAR_BIT * sizeof(x)) #define MSB(x, bits) ((x) & (~0ULL << (bitsizeof(x) - (bits)))) diff --git a/tests-clar/refdb/inmemory.c b/tests-clar/refdb/inmemory.c index ea76172cf..2c76d4192 100644 --- a/tests-clar/refdb/inmemory.c +++ b/tests-clar/refdb/inmemory.c @@ -1,12 +1,15 @@ #include "clar_libgit2.h" -#include "refdb.h" -#include "repository.h" + +#include "buffer.h" +#include "posix.h" +#include "path.h" +#include "refs.h" + #include "testdb.h" #define TEST_REPO_PATH "testrepo" static git_repository *repo; -static git_refdb_backend *refdb_backend; int unlink_ref(void *payload, git_buf *file) { @@ -26,7 +29,7 @@ int ref_file_foreach(git_repository *repo, int (* cb)(void *payload, git_buf *fi const char *repo_path; git_buf repo_refs_dir = GIT_BUF_INIT; int error = 0; - + repo_path = git_repository_path(repo); git_buf_joinpath(&repo_refs_dir, repo_path, "HEAD"); @@ -38,7 +41,7 @@ int ref_file_foreach(git_repository *repo, int (* cb)(void *payload, git_buf *fi git_buf_joinpath(&repo_refs_dir, git_buf_cstr(&repo_refs_dir), "heads"); if (git_path_direach(&repo_refs_dir, cb, NULL) != 0) return -1; - + git_buf_joinpath(&repo_refs_dir, repo_path, "packed-refs"); if (git_path_exists(git_buf_cstr(&repo_refs_dir)) && cb(NULL, &repo_refs_dir) < 0) @@ -51,16 +54,17 @@ int ref_file_foreach(git_repository *repo, int (* cb)(void *payload, git_buf *fi void test_refdb_inmemory__initialize(void) { - git_refdb *refdb; - git_buf repo_refs_dir = GIT_BUF_INIT; + git_refdb *refdb; + git_refdb_backend *refdb_backend; repo = cl_git_sandbox_init(TEST_REPO_PATH); cl_git_pass(git_repository_refdb(&refdb, repo)); cl_git_pass(refdb_backend_test(&refdb_backend, repo)); cl_git_pass(git_refdb_set_backend(refdb, refdb_backend)); - + git_refdb_free(refdb); + ref_file_foreach(repo, unlink_ref); git_buf_free(&repo_refs_dir); @@ -76,10 +80,10 @@ void test_refdb_inmemory__doesnt_write_ref_file(void) { git_reference *ref; git_oid oid; - + cl_git_pass(git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd")); cl_git_pass(git_reference_create(&ref, repo, GIT_REFS_HEADS_DIR "test1", &oid, 0)); - + ref_file_foreach(repo, empty); git_reference_free(ref); @@ -89,10 +93,10 @@ void test_refdb_inmemory__read(void) { git_reference *write1, *write2, *write3, *read1, *read2, *read3; git_oid oid1, oid2, oid3; - + cl_git_pass(git_oid_fromstr(&oid1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); cl_git_pass(git_reference_create(&write1, repo, GIT_REFS_HEADS_DIR "test1", &oid1, 0)); - + cl_git_pass(git_oid_fromstr(&oid2, "e90810b8df3e80c413d903f631643c716887138d")); cl_git_pass(git_reference_create(&write2, repo, GIT_REFS_HEADS_DIR "test2", &oid2, 0)); @@ -139,7 +143,7 @@ int foreach_test(const char *ref_name, void *payload) cl_assert(git_oid_cmp(&expected, git_reference_target(ref)) == 0); ++(*i); - + git_reference_free(ref); return 0; @@ -150,19 +154,19 @@ void test_refdb_inmemory__foreach(void) git_reference *write1, *write2, *write3; git_oid oid1, oid2, oid3; size_t i = 0; - + cl_git_pass(git_oid_fromstr(&oid1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); cl_git_pass(git_reference_create(&write1, repo, GIT_REFS_HEADS_DIR "test1", &oid1, 0)); - + cl_git_pass(git_oid_fromstr(&oid2, "e90810b8df3e80c413d903f631643c716887138d")); cl_git_pass(git_reference_create(&write2, repo, GIT_REFS_HEADS_DIR "test2", &oid2, 0)); - + cl_git_pass(git_oid_fromstr(&oid3, "763d71aadf09a7951596c9746c024e7eece7c7af")); cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0)); - + cl_git_pass(git_reference_foreach(repo, GIT_REF_LISTALL, foreach_test, &i)); cl_assert_equal_i(3, (int)i); - + git_reference_free(write1); git_reference_free(write2); git_reference_free(write3); @@ -175,14 +179,14 @@ int delete_test(const char *ref_name, void *payload) size_t *i = payload; cl_git_pass(git_reference_lookup(&ref, repo, ref_name)); - - cl_git_pass(git_oid_fromstr(&expected, "e90810b8df3e80c413d903f631643c716887138d")); + + cl_git_pass(git_oid_fromstr(&expected, "e90810b8df3e80c413d903f631643c716887138d")); cl_assert(git_oid_cmp(&expected, git_reference_target(ref)) == 0); - + ++(*i); - + git_reference_free(ref); - + return 0; } @@ -191,22 +195,22 @@ void test_refdb_inmemory__delete(void) git_reference *write1, *write2, *write3; git_oid oid1, oid2, oid3; size_t i = 0; - + cl_git_pass(git_oid_fromstr(&oid1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); cl_git_pass(git_reference_create(&write1, repo, GIT_REFS_HEADS_DIR "test1", &oid1, 0)); - + cl_git_pass(git_oid_fromstr(&oid2, "e90810b8df3e80c413d903f631643c716887138d")); cl_git_pass(git_reference_create(&write2, repo, GIT_REFS_HEADS_DIR "test2", &oid2, 0)); - + cl_git_pass(git_oid_fromstr(&oid3, "763d71aadf09a7951596c9746c024e7eece7c7af")); cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0)); - + git_reference_delete(write1); git_reference_free(write1); - + git_reference_delete(write3); git_reference_free(write3); - + cl_git_pass(git_reference_foreach(repo, GIT_REF_LISTALL, delete_test, &i)); cl_assert_equal_i(1, (int)i); diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index 4bca39878..627254e44 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -1,14 +1,10 @@ -#include "common.h" #include "vector.h" #include "util.h" -#include -#include -#include -#include +#include "testdb.h" typedef struct refdb_test_backend { git_refdb_backend parent; - + git_repository *repo; git_vector refs; } refdb_test_backend; @@ -16,7 +12,7 @@ typedef struct refdb_test_backend { typedef struct refdb_test_entry { char *name; git_ref_t type; - + union { git_oid oid; char *symbolic; @@ -37,19 +33,19 @@ static int refdb_test_backend__exists( refdb_test_backend *backend; refdb_test_entry *entry; size_t i; - + assert(_backend); backend = (refdb_test_backend *)_backend; - + *exists = 0; - + git_vector_foreach(&backend->refs, i, entry) { if (strcmp(entry->name, ref_name) == 0) { *exists = 1; break; } } - + return 0; } @@ -59,18 +55,18 @@ static int refdb_test_backend__write( { refdb_test_backend *backend; refdb_test_entry *entry; - + assert(_backend); backend = (refdb_test_backend *)_backend; entry = git__calloc(1, sizeof(refdb_test_entry)); GITERR_CHECK_ALLOC(entry); - + entry->name = git__strdup(git_reference_name(ref)); GITERR_CHECK_ALLOC(entry->name); - + entry->type = git_reference_type(ref); - + if (entry->type == GIT_REF_OID) git_oid_cpy(&entry->target.oid, git_reference_target(ref)); else { @@ -79,7 +75,7 @@ static int refdb_test_backend__write( } git_vector_insert(&backend->refs, entry); - + return 0; } @@ -94,7 +90,7 @@ static int refdb_test_backend__lookup( assert(_backend); backend = (refdb_test_backend *)_backend; - + git_vector_foreach(&backend->refs, i, entry) { if (strcmp(entry->name, ref_name) == 0) { @@ -108,7 +104,7 @@ static int refdb_test_backend__lookup( if (*out == NULL) return -1; - + return 0; } } @@ -125,21 +121,21 @@ static int refdb_test_backend__foreach( refdb_test_backend *backend; refdb_test_entry *entry; size_t i; - + assert(_backend); backend = (refdb_test_backend *)_backend; git_vector_foreach(&backend->refs, i, entry) { if (entry->type == GIT_REF_OID && (list_flags & GIT_REF_OID) == 0) continue; - + if (entry->type == GIT_REF_SYMBOLIC && (list_flags & GIT_REF_SYMBOLIC) == 0) continue; - + if (callback(entry->name, payload) != 0) return GIT_EUSER; } - + return 0; } @@ -147,7 +143,7 @@ static void refdb_test_entry_free(refdb_test_entry *entry) { if (entry->type == GIT_REF_SYMBOLIC) git__free(entry->target.symbolic); - + git__free(entry->name); git__free(entry); } @@ -178,14 +174,14 @@ static void refdb_test_backend__free(git_refdb_backend *_backend) refdb_test_backend *backend; refdb_test_entry *entry; size_t i; - + assert(_backend); backend = (refdb_test_backend *)_backend; git_vector_foreach(&backend->refs, i, entry) refdb_test_entry_free(entry); - git_vector_free(&backend->refs); + git_vector_free(&backend->refs); git__free(backend); } @@ -197,7 +193,7 @@ int refdb_backend_test( backend = git__calloc(1, sizeof(refdb_test_backend)); GITERR_CHECK_ALLOC(backend); - + git_vector_init(&backend->refs, 0, ref_name_cmp); backend->repo = repo; diff --git a/tests-clar/refdb/testdb.h b/tests-clar/refdb/testdb.h index e38abd967..49d1cb1d0 100644 --- a/tests-clar/refdb/testdb.h +++ b/tests-clar/refdb/testdb.h @@ -1,3 +1,8 @@ +#include +#include +#include +#include + int refdb_backend_test( git_refdb_backend **backend_out, git_repository *repo); From 21ca045100337bcb2905a20a72d42721d18871f9 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Sun, 21 Apr 2013 12:52:17 -0700 Subject: [PATCH 038/384] Move git_reference__alloc to include/git2/sys Create a new include/git2/sys/refs.h and move the reference alloc functions there. Also fix some documentation issues and some minor code cleanups. --- include/git2/refdb.h | 19 ---------------- include/git2/sys/refdb_backend.h | 36 +++++++++++++++++++----------- include/git2/sys/refs.h | 38 ++++++++++++++++++++++++++++++++ src/refdb.c | 28 ++++++++++------------- src/refdb.h | 2 +- src/refdb_fs.c | 1 + src/refs.c | 4 ++-- tests-clar/refdb/inmemory.c | 1 - tests-clar/refdb/testdb.h | 1 + 9 files changed, 78 insertions(+), 52 deletions(-) create mode 100644 include/git2/sys/refs.h diff --git a/include/git2/refdb.h b/include/git2/refdb.h index 9278b1116..a315876ae 100644 --- a/include/git2/refdb.h +++ b/include/git2/refdb.h @@ -21,25 +21,6 @@ */ GIT_BEGIN_DECL -/** - * Create a new reference. Either an oid or a symbolic target must be - * specified. - * - * @param refdb the reference database to associate with this reference - * @param name the reference name - * @param oid the object id for a direct reference - * @param symbolic the target for a symbolic reference - * @return the created git_reference or NULL on error - */ -GIT_EXTERN(git_reference *) git_reference__alloc( - const char *name, - const git_oid *oid, - const git_oid *peel); - -GIT_EXTERN(git_reference *) git_reference__alloc_symbolic( - const char *name, - const char *target); - /** * Create a new reference database with no backends. * diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index dcdf7bcb8..d5f599fec 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -4,8 +4,8 @@ * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ -#ifndef INCLUDE_git_refdb_backend_h__ -#define INCLUDE_git_refdb_backend_h__ +#ifndef INCLUDE_sys_git_refdb_backend_h__ +#define INCLUDE_sys_git_refdb_backend_h__ #include "git2/common.h" #include "git2/types.h" @@ -30,7 +30,7 @@ struct git_refdb_backend { */ int (*exists)( int *exists, - struct git_refdb_backend *backend, + git_refdb_backend *backend, const char *ref_name); /** @@ -39,7 +39,7 @@ struct git_refdb_backend { */ int (*lookup)( git_reference **out, - struct git_refdb_backend *backend, + git_refdb_backend *backend, const char *ref_name); /** @@ -47,7 +47,7 @@ struct git_refdb_backend { * provide this function. */ int (*foreach)( - struct git_refdb_backend *backend, + git_refdb_backend *backend, unsigned int list_flags, git_reference_foreach_cb callback, void *payload); @@ -59,7 +59,7 @@ struct git_refdb_backend { * against the glob. */ int (*foreach_glob)( - struct git_refdb_backend *backend, + git_refdb_backend *backend, const char *glob, unsigned int list_flags, git_reference_foreach_cb callback, @@ -69,13 +69,13 @@ struct git_refdb_backend { * Writes the given reference to the refdb. A refdb implementation * must provide this function. */ - int (*write)(struct git_refdb_backend *backend, const git_reference *ref); + int (*write)(git_refdb_backend *backend, const git_reference *ref); /** * Deletes the given reference from the refdb. A refdb implementation * must provide this function. */ - int (*delete)(struct git_refdb_backend *backend, const git_reference *ref); + int (*delete)(git_refdb_backend *backend, const git_reference *ref); /** * Suggests that the given refdb compress or optimize its references. @@ -84,31 +84,41 @@ struct git_refdb_backend { * implementation may provide this function; if it is not provided, * nothing will be done. */ - int (*compress)(struct git_refdb_backend *backend); + int (*compress)(git_refdb_backend *backend); /** * Frees any resources held by the refdb. A refdb implementation may * provide this function; if it is not provided, nothing will be done. */ - void (*free)(struct git_refdb_backend *backend); + void (*free)(git_refdb_backend *backend); }; #define GIT_ODB_BACKEND_VERSION 1 #define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION} /** - * Constructors for default refdb backends. + * Constructors for default filesystem-based refdb backend + * + * Under normal usage, this is called for you when the repository is + * opened / created, but you can use this to explicitly construct a + * filesystem refdb backend for a repository. + * + * @param backend_out Output pointer to the git_refdb_backend object + * @param repo Git repository to access + * @return 0 on success, <0 error code on failure */ GIT_EXTERN(int) git_refdb_backend_fs( - struct git_refdb_backend **backend_out, + git_refdb_backend **backend_out, git_repository *repo); /** * Sets the custom backend to an existing reference DB * + * The `git_refdb` will take ownership of the `git_refdb_backend` so you + * should NOT free it after calling this function. + * * @param refdb database to add the backend to * @param backend pointer to a git_refdb_backend instance - * @param priority Value for ordering the backends queue * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_refdb_set_backend( diff --git a/include/git2/sys/refs.h b/include/git2/sys/refs.h new file mode 100644 index 000000000..85963258c --- /dev/null +++ b/include/git2/sys/refs.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_sys_git_refdb_h__ +#define INCLUDE_sys_git_refdb_h__ + +#include "git2/common.h" +#include "git2/types.h" +#include "git2/oid.h" + +/** + * Create a new direct reference from an OID. + * + * @param name the reference name + * @param oid the object id for a direct reference + * @param symbolic the target for a symbolic reference + * @return the created git_reference or NULL on error + */ +GIT_EXTERN(git_reference *) git_reference__alloc( + const char *name, + const git_oid *oid, + const git_oid *peel); + +/** + * Create a new symbolic reference. + * + * @param name the reference name + * @param symbolic the target for a symbolic reference + * @return the created git_reference or NULL on error + */ +GIT_EXTERN(git_reference *) git_reference__alloc_symbolic( + const char *name, + const char *target); + +#endif diff --git a/src/refdb.c b/src/refdb.c index 0675be638..33a1934d1 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -58,15 +58,19 @@ int git_refdb_open(git_refdb **out, git_repository *repo) return 0; } -int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend) +static void refdb_free_backend(git_refdb *db) { if (db->backend) { - if(db->backend->free) + if (db->backend->free) db->backend->free(db->backend); else git__free(db->backend); } +} +int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend) +{ + refdb_free_backend(db); db->backend = backend; return 0; @@ -76,22 +80,15 @@ int git_refdb_compress(git_refdb *db) { assert(db); - if (db->backend->compress) { + if (db->backend->compress) return db->backend->compress(db->backend); - } return 0; } static void refdb_free(git_refdb *db) { - if (db->backend) { - if(db->backend->free) - db->backend->free(db->backend); - else - git__free(db->backend); - } - + refdb_free_backend(db); git__free(db); } @@ -115,14 +112,13 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name) git_reference *ref; int error; - assert(db && db->backend && ref_name); + assert(db && db->backend && out && ref_name); - *out = NULL; - - if ((error = db->backend->lookup(&ref, db->backend, ref_name)) == 0) - { + if (!(error = db->backend->lookup(&ref, db->backend, ref_name))) { ref->db = db; *out = ref; + } else { + *out = NULL; } return error; diff --git a/src/refdb.h b/src/refdb.h index 0969711b9..047113ac8 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -41,6 +41,6 @@ int git_refdb_foreach_glob( int git_refdb_write(git_refdb *refdb, const git_reference *ref); -int git_refdb_delete(struct git_refdb *refdb, const git_reference *ref); +int git_refdb_delete(git_refdb *refdb, const git_reference *ref); #endif diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 4d5d6006d..443871005 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -19,6 +19,7 @@ #include #include #include +#include GIT__USE_STRMAP; diff --git a/src/refs.c b/src/refs.c index d9291e56f..9c6684a5a 100644 --- a/src/refs.c +++ b/src/refs.c @@ -19,6 +19,7 @@ #include #include #include +#include GIT__USE_STRMAP; @@ -44,8 +45,7 @@ static git_reference *alloc_ref(const char *name) } git_reference *git_reference__alloc_symbolic( - const char *name, - const char *target) + const char *name, const char *target) { git_reference *ref; diff --git a/tests-clar/refdb/inmemory.c b/tests-clar/refdb/inmemory.c index 2c76d4192..243b5bb37 100644 --- a/tests-clar/refdb/inmemory.c +++ b/tests-clar/refdb/inmemory.c @@ -63,7 +63,6 @@ void test_refdb_inmemory__initialize(void) cl_git_pass(git_repository_refdb(&refdb, repo)); cl_git_pass(refdb_backend_test(&refdb_backend, repo)); cl_git_pass(git_refdb_set_backend(refdb, refdb_backend)); - git_refdb_free(refdb); ref_file_foreach(repo, unlink_ref); diff --git a/tests-clar/refdb/testdb.h b/tests-clar/refdb/testdb.h index 49d1cb1d0..a0d1bbc48 100644 --- a/tests-clar/refdb/testdb.h +++ b/tests-clar/refdb/testdb.h @@ -1,6 +1,7 @@ #include #include #include +#include #include int refdb_backend_test( From 0d4a5b13afc6c516212b194b238ab08d3362a041 Mon Sep 17 00:00:00 2001 From: Jasper Lievisse Adriaanse Date: Mon, 22 Apr 2013 00:13:35 +0200 Subject: [PATCH 039/384] Add missing prototype for p_realpath(). --- src/unix/posix.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/unix/posix.h b/src/unix/posix.h index f4886c5d1..9c9f837b9 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -21,6 +21,8 @@ /* The OpenBSD realpath function behaves differently */ #if !defined(__OpenBSD__) # define p_realpath(p, po) realpath(p, po) +#else +char *p_realpath(const char *, char *); #endif #define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a) From 4ef2c79cb6dc81e17b68ccf7c270bcc7e4f85bfb Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 22 Apr 2013 16:37:40 +0200 Subject: [PATCH 040/384] odb: Disable inode checks for Win32 --- src/odb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/odb.c b/src/odb.c index 1c7969fcb..8249c5adc 100644 --- a/src/odb.c +++ b/src/odb.c @@ -415,6 +415,9 @@ static int add_default_backends( struct stat st; git_odb_backend *loose, *packed; + /* TODO: inodes are not really relevant on Win32, so we need to find + * a cross-platform workaround for this */ +#ifndef GIT_WIN32 if (p_stat(objects_dir, &st) < 0) { giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir); return -1; @@ -425,6 +428,7 @@ static int add_default_backends( if (backend->disk_inode == st.st_ino) return 0; } +#endif /* add the loose object backend */ if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 || From 5df184241a6cfe88ac5ebcee9a3ad175abfca0cd Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 1 Apr 2013 19:38:23 +0200 Subject: [PATCH 041/384] lol this worked first try wtf --- src/cache.c | 187 ++++++++++++++++++++++++++--------------- src/cache.h | 33 ++++---- src/object.c | 54 +++++++----- src/odb.c | 36 ++++---- src/odb.h | 4 +- src/oidmap.h | 6 +- src/repository.c | 2 +- src/util.c | 8 -- tests-clar/core/opts.c | 11 --- 9 files changed, 193 insertions(+), 148 deletions(-) diff --git a/src/cache.c b/src/cache.c index e7f333577..6eb18dae5 100644 --- a/src/cache.c +++ b/src/cache.c @@ -11,100 +11,147 @@ #include "thread-utils.h" #include "util.h" #include "cache.h" +#include "odb.h" +#include "object.h" #include "git2/oid.h" -int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr) +GIT__USE_OIDMAP + +int git_cache_init(git_cache *cache) { - if (size < 8) - size = 8; - size = git__size_t_powerof2(size); - - cache->size_mask = size - 1; - cache->lru_count = 0; - cache->free_obj = free_ptr; - + cache->map = git_oidmap_alloc(); git_mutex_init(&cache->lock); - - cache->nodes = git__malloc(size * sizeof(git_cached_obj *)); - GITERR_CHECK_ALLOC(cache->nodes); - - memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *)); return 0; } void git_cache_free(git_cache *cache) { - size_t i; - - for (i = 0; i < (cache->size_mask + 1); ++i) { - if (cache->nodes[i] != NULL) - git_cached_obj_decref(cache->nodes[i], cache->free_obj); - } - + git_oidmap_free(cache->map); git_mutex_free(&cache->lock); - git__free(cache->nodes); } -void *git_cache_get(git_cache *cache, const git_oid *oid) +static bool cache_should_store(git_cached_obj *entry) { - uint32_t hash; - git_cached_obj *node = NULL, *result = NULL; - - memcpy(&hash, oid->id, sizeof(hash)); - - if (git_mutex_lock(&cache->lock)) { - giterr_set(GITERR_THREAD, "unable to lock cache mutex"); - return NULL; - } - - { - node = cache->nodes[hash & cache->size_mask]; - - if (node != NULL && git_oid_cmp(&node->oid, oid) == 0) { - git_cached_obj_incref(node); - result = node; - } - } - git_mutex_unlock(&cache->lock); - - return result; + return true; } -void *git_cache_try_store(git_cache *cache, void *_entry) +static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags) { - git_cached_obj *entry = _entry; - uint32_t hash; + khiter_t pos; + git_cached_obj *entry = NULL; - memcpy(&hash, &entry->oid, sizeof(uint32_t)); - - if (git_mutex_lock(&cache->lock)) { - giterr_set(GITERR_THREAD, "unable to lock cache mutex"); + if (git_mutex_lock(&cache->lock) < 0) return NULL; - } - { - git_cached_obj *node = cache->nodes[hash & cache->size_mask]; + pos = kh_get(oid, cache->map, oid); + if (pos != kh_end(cache->map)) { + entry = kh_val(cache->map, pos); - /* increase the refcount on this object, because - * the cache now owns it */ - git_cached_obj_incref(entry); - - if (node == NULL) { - cache->nodes[hash & cache->size_mask] = entry; - } else if (git_oid_cmp(&node->oid, &entry->oid) == 0) { - git_cached_obj_decref(entry, cache->free_obj); - entry = node; + if (flags && entry->flags != flags) { + entry = NULL; } else { - git_cached_obj_decref(node, cache->free_obj); - cache->nodes[hash & cache->size_mask] = entry; + git_cached_obj_incref(entry); } - - /* increase the refcount again, because we are - * returning it to the user */ - git_cached_obj_incref(entry); - } + git_mutex_unlock(&cache->lock); return entry; } + +static void *cache_store(git_cache *cache, git_cached_obj *entry) +{ + khiter_t pos; + + git_cached_obj_incref(entry); + + if (!cache_should_store(entry)) + return entry; + + if (git_mutex_lock(&cache->lock) < 0) + return entry; + + pos = kh_get(oid, cache->map, &entry->oid); + + /* not found */ + if (pos == kh_end(cache->map)) { + int rval; + + pos = kh_put(oid, cache->map, &entry->oid, &rval); + if (rval >= 0) { + kh_key(cache->map, pos) = &entry->oid; + kh_val(cache->map, pos) = entry; + git_cached_obj_incref(entry); + } + } + /* found */ + else { + git_cached_obj *stored_entry = kh_val(cache->map, pos); + + if (stored_entry->flags == entry->flags) { + git_cached_obj_decref(entry); + git_cached_obj_incref(stored_entry); + entry = stored_entry; + } else if (stored_entry->flags == GIT_CACHE_STORE_RAW && + entry->flags == GIT_CACHE_STORE_PARSED) { + git_cached_obj_decref(stored_entry); + git_cached_obj_incref(entry); + + kh_key(cache->map, pos) = &entry->oid; + kh_val(cache->map, pos) = entry; + } else { + /* NO OP */ + } + } + + git_mutex_unlock(&cache->lock); + return entry; +} + +void *git_cache_store_raw(git_cache *cache, git_odb_object *entry) +{ + entry->cached.flags = GIT_CACHE_STORE_RAW; + return cache_store(cache, (git_cached_obj *)entry); +} + +void *git_cache_store_parsed(git_cache *cache, git_object *entry) +{ + entry->cached.flags = GIT_CACHE_STORE_PARSED; + return cache_store(cache, (git_cached_obj *)entry); +} + +git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid) +{ + return cache_get(cache, oid, GIT_CACHE_STORE_RAW); +} + +git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid) +{ + return cache_get(cache, oid, GIT_CACHE_STORE_PARSED); +} + +void *git_cache_get_any(git_cache *cache, const git_oid *oid) +{ + return cache_get(cache, oid, GIT_CACHE_STORE_ANY); +} + +void git_cached_obj_decref(void *_obj) +{ + git_cached_obj *obj = _obj; + + if (git_atomic_dec(&obj->refcount) == 0) { + switch (obj->flags) { + case GIT_CACHE_STORE_RAW: + git_odb_object__free(_obj); + break; + + case GIT_CACHE_STORE_PARSED: + git_object__free(_obj); + break; + + default: + git__free(_obj); + break; + } + } +} diff --git a/src/cache.h b/src/cache.h index 7034ec268..f30af9c3e 100644 --- a/src/cache.h +++ b/src/cache.h @@ -12,30 +12,35 @@ #include "git2/odb.h" #include "thread-utils.h" +#include "oidmap.h" -#define GIT_DEFAULT_CACHE_SIZE 128 - -typedef void (*git_cached_obj_freeptr)(void *); +enum { + GIT_CACHE_STORE_ANY = 0, + GIT_CACHE_STORE_RAW = 1, + GIT_CACHE_STORE_PARSED = 2 +}; typedef struct { git_oid oid; git_atomic refcount; + uint32_t flags; } git_cached_obj; typedef struct { - git_cached_obj **nodes; + git_oidmap *map; git_mutex lock; - unsigned int lru_count; - size_t size_mask; - git_cached_obj_freeptr free_obj; } git_cache; -int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr); +int git_cache_init(git_cache *cache); void git_cache_free(git_cache *cache); -void *git_cache_try_store(git_cache *cache, void *entry); -void *git_cache_get(git_cache *cache, const git_oid *oid); +void *git_cache_store_raw(git_cache *cache, git_odb_object *entry); +void *git_cache_store_parsed(git_cache *cache, git_object *entry); + +git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid); +git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid); +void *git_cache_get_any(git_cache *cache, const git_oid *oid); GIT_INLINE(void) git_cached_obj_incref(void *_obj) { @@ -43,12 +48,6 @@ GIT_INLINE(void) git_cached_obj_incref(void *_obj) git_atomic_inc(&obj->refcount); } -GIT_INLINE(void) git_cached_obj_decref(void *_obj, git_cached_obj_freeptr free_obj) -{ - git_cached_obj *obj = _obj; - - if (git_atomic_dec(&obj->refcount) == 0) - free_obj(obj); -} +void git_cached_obj_decref(void *_obj); #endif diff --git a/src/object.c b/src/object.c index 80fe51152..5542ebc8e 100644 --- a/src/object.c +++ b/src/object.c @@ -121,12 +121,13 @@ int git_object__from_odb_object( break; } - if (error < 0) + if (error < 0) { git_object__free(object); - else - *object_out = git_cache_try_store(&repo->objects, object); + return error; + } - return error; + *object_out = git_cache_store_parsed(&repo->objects, object); + return 0; } int git_object_lookup_prefix( @@ -154,27 +155,38 @@ int git_object_lookup_prefix( len = GIT_OID_HEXSZ; if (len == GIT_OID_HEXSZ) { + git_cached_obj *cached = NULL; + /* We want to match the full id : we can first look up in the cache, * since there is no need to check for non ambiguousity */ - object = git_cache_get(&repo->objects, id); - if (object != NULL) { - if (type != GIT_OBJ_ANY && type != object->type) { - git_object_free(object); - giterr_set(GITERR_INVALID, "The requested type does not match the type in ODB"); - return GIT_ENOTFOUND; + cached = git_cache_get_any(&repo->objects, id); + if (cached != NULL) { + if (cached->flags == GIT_CACHE_STORE_PARSED) { + object = (git_object *)cached; + + if (type != GIT_OBJ_ANY && type != object->type) { + git_object_free(object); + giterr_set(GITERR_INVALID, + "The requested type does not match the type in ODB"); + return GIT_ENOTFOUND; + } + + *object_out = object; + return 0; + } else if (cached->flags == GIT_CACHE_STORE_RAW) { + odb_obj = (git_odb_object *)cached; + } else { + assert(!"Wrong caching type in the global object cache"); } - - *object_out = object; - return 0; + } else { + /* Object was not found in the cache, let's explore the backends. + * We could just use git_odb_read_unique_short_oid, + * it is the same cost for packed and loose object backends, + * but it may be much more costly for sqlite and hiredis. + */ + error = git_odb_read(&odb_obj, odb, id); } - - /* Object was not found in the cache, let's explore the backends. - * We could just use git_odb_read_unique_short_oid, - * it is the same cost for packed and loose object backends, - * but it may be much more costly for sqlite and hiredis. - */ - error = git_odb_read(&odb_obj, odb, id); } else { git_oid short_oid; @@ -245,7 +257,7 @@ void git_object_free(git_object *object) if (object == NULL) return; - git_cached_obj_decref((git_cached_obj *)object, git_object__free); + git_cached_obj_decref(object); } const git_oid *git_object_id(const git_object *obj) diff --git a/src/odb.c b/src/odb.c index d6ea8c29a..821fbd70c 100644 --- a/src/odb.c +++ b/src/odb.c @@ -14,6 +14,7 @@ #include "odb.h" #include "delta-apply.h" #include "filter.h" +#include "repository.h" #include "git2/odb_backend.h" #include "git2/oid.h" @@ -34,7 +35,15 @@ typedef struct ino_t disk_inode; } backend_internal; -size_t git_odb__cache_size = GIT_DEFAULT_CACHE_SIZE; +static git_cache *odb_cache(git_odb *odb) +{ + if (odb->rc.owner != NULL) { + git_repository *owner = odb->rc.owner; + return &owner->objects; + } + + return &odb->own_cache; +} static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth); @@ -83,10 +92,8 @@ static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source) return object; } -static void free_odb_object(void *o) +void git_odb_object__free(git_odb_object *object) { - git_odb_object *object = (git_odb_object *)o; - if (object != NULL) { git__free(object->raw.data); git__free(object); @@ -118,7 +125,7 @@ void git_odb_object_free(git_odb_object *object) if (object == NULL) return; - git_cached_obj_decref((git_cached_obj *)object, &free_odb_object); + git_cached_obj_decref(object); } int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) @@ -355,9 +362,8 @@ int git_odb_new(git_odb **out) git_odb *db = git__calloc(1, sizeof(*db)); GITERR_CHECK_ALLOC(db); - if (git_cache_init(&db->cache, git_odb__cache_size, &free_odb_object) < 0 || - git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) - { + if (git_cache_init(&db->own_cache) < 0 || + git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) { git__free(db); return -1; } @@ -559,7 +565,7 @@ static void odb_free(git_odb *db) } git_vector_free(&db->backends); - git_cache_free(&db->cache); + git_cache_free(&db->own_cache); git__free(db); } @@ -580,7 +586,7 @@ int git_odb_exists(git_odb *db, const git_oid *id) assert(db && id); - if ((object = git_cache_get(&db->cache, id)) != NULL) { + if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { git_odb_object_free(object); return (int)true; } @@ -630,7 +636,7 @@ int git_odb__read_header_or_object( assert(db && id && out && len_p && type_p); - if ((object = git_cache_get(&db->cache, id)) != NULL) { + if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { *len_p = object->raw.len; *type_p = object->raw.type; *out = object; @@ -678,7 +684,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) return GIT_ENOTFOUND; } - *out = git_cache_get(&db->cache, id); + *out = git_cache_get_raw(odb_cache(db), id); if (*out != NULL) return 0; @@ -704,7 +710,7 @@ attempt_lookup: if (error && error != GIT_PASSTHROUGH) return error; - *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw)); + *out = git_cache_store_raw(odb_cache(db), new_odb_object(id, &raw)); return 0; } @@ -727,7 +733,7 @@ int git_odb_read_prefix( len = GIT_OID_HEXSZ; if (len == GIT_OID_HEXSZ) { - *out = git_cache_get(&db->cache, short_id); + *out = git_cache_get_raw(odb_cache(db), short_id); if (*out != NULL) return 0; } @@ -768,7 +774,7 @@ attempt_lookup: if (!found) return git_odb__error_notfound("no match for prefix", short_id); - *out = git_cache_try_store(&db->cache, new_odb_object(&found_full_oid, &raw)); + *out = git_cache_store_raw(odb_cache(db), new_odb_object(&found_full_oid, &raw)); return 0; } diff --git a/src/odb.h b/src/odb.h index 7c018cc50..9abf594e8 100644 --- a/src/odb.h +++ b/src/odb.h @@ -36,9 +36,11 @@ struct git_odb_object { struct git_odb { git_refcount rc; git_vector backends; - git_cache cache; + git_cache own_cache; }; +void git_odb_object__free(git_odb_object *object); + /* * Hash a git_rawobj internally. * The `git_rawobj` is supposed to be previously initialized diff --git a/src/oidmap.h b/src/oidmap.h index 40274cd19..dfa951af3 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -21,10 +21,8 @@ typedef khash_t(oid) git_oidmap; GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid) { - int i; - khint_t h = 0; - for (i = 0; i < 20; ++i) - h = (h << 5) - h + oid->id[i]; + khint_t h; + memcpy(&h, oid, sizeof(khint_t)); return h; } diff --git a/src/repository.c b/src/repository.c index a16f69da4..cda75b85f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -119,7 +119,7 @@ static git_repository *repository_alloc(void) memset(repo, 0x0, sizeof(git_repository)); - if (git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free) < 0) { + if (git_cache_init(&repo->objects) < 0) { git__free(repo); return NULL; } diff --git a/src/util.c b/src/util.c index 8e83d298e..c4a8c786d 100644 --- a/src/util.c +++ b/src/util.c @@ -93,14 +93,6 @@ int git_libgit2_opts(int key, ...) if ((error = config_level_to_futils_dir(va_arg(ap, int))) >= 0) error = git_futils_dirs_set(error, va_arg(ap, const char *)); break; - - case GIT_OPT_GET_ODB_CACHE_SIZE: - *(va_arg(ap, size_t *)) = git_odb__cache_size; - break; - - case GIT_OPT_SET_ODB_CACHE_SIZE: - git_odb__cache_size = va_arg(ap, size_t); - break; } va_end(ap); diff --git a/tests-clar/core/opts.c b/tests-clar/core/opts.c index 907339d51..3173c648b 100644 --- a/tests-clar/core/opts.c +++ b/tests-clar/core/opts.c @@ -16,15 +16,4 @@ void test_core_opts__readwrite(void) git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &new_val); cl_assert(new_val == old_val); - - git_libgit2_opts(GIT_OPT_GET_ODB_CACHE_SIZE, &old_val); - - cl_assert(old_val == GIT_DEFAULT_CACHE_SIZE); - - git_libgit2_opts(GIT_OPT_SET_ODB_CACHE_SIZE, (size_t)GIT_DEFAULT_CACHE_SIZE*2); - git_libgit2_opts(GIT_OPT_GET_ODB_CACHE_SIZE, &new_val); - - cl_assert(new_val == (GIT_DEFAULT_CACHE_SIZE*2)); - - git_libgit2_opts(GIT_OPT_GET_ODB_CACHE_SIZE, &old_val); } From 6b90e244de78640b751d0923c91c3868e12b8658 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 1 Apr 2013 19:53:49 +0200 Subject: [PATCH 042/384] Per-object filtering --- src/cache.c | 37 +++++++++++++++++++++++++++++++------ src/cache.h | 3 ++- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/cache.c b/src/cache.c index 6eb18dae5..a0925a188 100644 --- a/src/cache.c +++ b/src/cache.c @@ -17,8 +17,22 @@ GIT__USE_OIDMAP +bool git_cache__store_types[8] = { + false, /* GIT_OBJ__EXT1 */ + true, /* GIT_OBJ_COMMIT */ + true, /* GIT_OBJ_TREE */ + false, /* GIT_OBJ_BLOB */ + true, /* GIT_OBJ_TAG */ + false, /* GIT_OBJ__EXT2 */ + false, /* GIT_OBJ_OFS_DELTA */ + false /* GIT_OBJ_REF_DELTA */ +}; + +size_t git_cache__max_object_size = 4096; + int git_cache_init(git_cache *cache) { + cache->lru_count = 0; cache->map = git_oidmap_alloc(); git_mutex_init(&cache->lock); return 0; @@ -30,8 +44,14 @@ void git_cache_free(git_cache *cache) git_mutex_free(&cache->lock); } -static bool cache_should_store(git_cached_obj *entry) +static bool cache_should_store(git_otype object_type, size_t object_size) { + if (!git_cache__store_types[object_type]) + return false; + + if (object_size > git_cache__max_object_size) + return false; + return true; } @@ -63,11 +83,6 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) { khiter_t pos; - git_cached_obj_incref(entry); - - if (!cache_should_store(entry)) - return entry; - if (git_mutex_lock(&cache->lock) < 0) return entry; @@ -110,12 +125,22 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) void *git_cache_store_raw(git_cache *cache, git_odb_object *entry) { + git_cached_obj_incref(entry); + + if (!cache_should_store(entry->raw.type, entry->raw.len)) + return entry; + entry->cached.flags = GIT_CACHE_STORE_RAW; return cache_store(cache, (git_cached_obj *)entry); } void *git_cache_store_parsed(git_cache *cache, git_object *entry) { + git_cached_obj_incref(entry); + + if (!cache_should_store(entry->type, 0 /* TODO */)) + return entry; + entry->cached.flags = GIT_CACHE_STORE_PARSED; return cache_store(cache, (git_cached_obj *)entry); } diff --git a/src/cache.h b/src/cache.h index f30af9c3e..3461ff7d5 100644 --- a/src/cache.h +++ b/src/cache.h @@ -23,7 +23,8 @@ enum { typedef struct { git_oid oid; git_atomic refcount; - uint32_t flags; + uint16_t flags; + uint16_t lru; } git_cached_obj; typedef struct { From c4e91d4500bdd357fbf7378bc10648a482513fa6 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 3 Apr 2013 20:57:30 +0200 Subject: [PATCH 043/384] Random eviction --- src/cache.c | 19 ++++++++++++++++++- src/cache.h | 5 ++--- src/object.c | 1 + 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/cache.c b/src/cache.c index a0925a188..f49515ef3 100644 --- a/src/cache.c +++ b/src/cache.c @@ -32,7 +32,6 @@ size_t git_cache__max_object_size = 4096; int git_cache_init(git_cache *cache) { - cache->lru_count = 0; cache->map = git_oidmap_alloc(); git_mutex_init(&cache->lock); return 0; @@ -44,6 +43,24 @@ void git_cache_free(git_cache *cache) git_mutex_free(&cache->lock); } +static void cache_evict_entries(git_cache *cache, size_t evict) +{ + uint32_t seed = rand(); + + /* do not infinite loop if there's not enough entries to evict */ + if (evict > kh_size(cache->map)) + return; + + while (evict > 0) { + khiter_t pos = seed++ % kh_end(cache->map); + + if (kh_exist(cache->map, pos)) { + kh_del(oid, cache->map, pos); + evict--; + } + } +} + static bool cache_should_store(git_otype object_type, size_t object_size) { if (!git_cache__store_types[object_type]) diff --git a/src/cache.h b/src/cache.h index 3461ff7d5..f952830c5 100644 --- a/src/cache.h +++ b/src/cache.h @@ -23,14 +23,13 @@ enum { typedef struct { git_oid oid; git_atomic refcount; - uint16_t flags; - uint16_t lru; + uint32_t cache_size; + uint32_t flags; } git_cached_obj; typedef struct { git_oidmap *map; git_mutex lock; - unsigned int lru_count; } git_cache; int git_cache_init(git_cache *cache); diff --git a/src/object.c b/src/object.c index 5542ebc8e..54ceea33c 100644 --- a/src/object.c +++ b/src/object.c @@ -98,6 +98,7 @@ int git_object__from_odb_object( /* Initialize parent object */ git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid); + object->cached.cache_size = (uint32_t)odb_obj->raw.len; object->repo = repo; switch (type) { From 8842c75f172ed94be4ad11521d4083e97d740785 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 3 Apr 2013 22:30:07 +0200 Subject: [PATCH 044/384] What has science done. --- src/blob.c | 10 +++++----- src/cache.c | 4 ++-- src/cache.h | 5 +++-- src/checkout.c | 4 ++-- src/commit.c | 2 +- src/commit_list.c | 15 +++++++++------ src/object.c | 9 +++++---- src/odb.c | 21 ++++++++++++--------- src/odb.h | 2 +- src/tag.c | 4 ++-- src/tree.c | 4 +++- tests-clar/diff/workdir.c | 3 ++- tests-clar/object/raw/write.c | 8 +++++++- tests-clar/odb/loose.c | 7 ++++++- tests-clar/odb/packed.c | 8 ++++---- tests-clar/odb/packed_one.c | 4 ++-- 16 files changed, 66 insertions(+), 44 deletions(-) diff --git a/src/blob.c b/src/blob.c index 11e1f4d77..7dce4f7ee 100644 --- a/src/blob.c +++ b/src/blob.c @@ -18,19 +18,19 @@ const void *git_blob_rawcontent(const git_blob *blob) { assert(blob); - return blob->odb_object->raw.data; + return blob->odb_object->buffer; } git_off_t git_blob_rawsize(const git_blob *blob) { assert(blob); - return (git_off_t)blob->odb_object->raw.len; + return (git_off_t)blob->odb_object->cached.size; } int git_blob__getbuf(git_buf *buffer, git_blob *blob) { return git_buf_set( - buffer, blob->odb_object->raw.data, blob->odb_object->raw.len); + buffer, blob->odb_object->buffer, blob->odb_object->cached.size); } void git_blob__free(git_blob *blob) @@ -315,8 +315,8 @@ int git_blob_is_binary(git_blob *blob) assert(blob); - content.ptr = blob->odb_object->raw.data; - content.size = min(blob->odb_object->raw.len, 4000); + content.ptr = blob->odb_object->buffer; + content.size = min(blob->odb_object->cached.size, 4000); return git_buf_text_is_binary(&content); } diff --git a/src/cache.c b/src/cache.c index f49515ef3..316007bdf 100644 --- a/src/cache.c +++ b/src/cache.c @@ -144,7 +144,7 @@ void *git_cache_store_raw(git_cache *cache, git_odb_object *entry) { git_cached_obj_incref(entry); - if (!cache_should_store(entry->raw.type, entry->raw.len)) + if (!cache_should_store(entry->cached.type, entry->cached.size)) return entry; entry->cached.flags = GIT_CACHE_STORE_RAW; @@ -155,7 +155,7 @@ void *git_cache_store_parsed(git_cache *cache, git_object *entry) { git_cached_obj_incref(entry); - if (!cache_should_store(entry->type, 0 /* TODO */)) + if (!cache_should_store(entry->cached.type, entry->cached.size)) return entry; entry->cached.flags = GIT_CACHE_STORE_PARSED; diff --git a/src/cache.h b/src/cache.h index f952830c5..3633f62a7 100644 --- a/src/cache.h +++ b/src/cache.h @@ -22,9 +22,10 @@ enum { typedef struct { git_oid oid; - git_atomic refcount; - uint32_t cache_size; + int32_t type; + size_t size; uint32_t flags; + git_atomic refcount; } git_cached_obj; typedef struct { diff --git a/src/checkout.c b/src/checkout.c index 81dc5e3da..bb2f90606 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -710,8 +710,8 @@ static int blob_content_to_file( git_vector filters = GIT_VECTOR_INIT; /* Create a fake git_buf from the blob raw data... */ - filtered.ptr = blob->odb_object->raw.data; - filtered.size = blob->odb_object->raw.len; + filtered.ptr = blob->odb_object->buffer; + filtered.size = blob->odb_object->cached.size; /* ... and make sure it doesn't get unexpectedly freed */ dont_free_filtered = true; diff --git a/src/commit.c b/src/commit.c index dd416920d..2057364b5 100644 --- a/src/commit.c +++ b/src/commit.c @@ -244,7 +244,7 @@ bad_buffer: int git_commit__parse(git_commit *commit, git_odb_object *obj) { assert(commit); - return git_commit__parse_buffer(commit, obj->raw.data, obj->raw.len); + return git_commit__parse_buffer(commit, obj->buffer, obj->cached.size); } #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \ diff --git a/src/commit_list.c b/src/commit_list.c index 603dd754a..baabbbafb 100644 --- a/src/commit_list.c +++ b/src/commit_list.c @@ -100,12 +100,15 @@ git_commit_list_node *git_commit_list_pop(git_commit_list **stack) return item; } -static int commit_quick_parse(git_revwalk *walk, git_commit_list_node *commit, git_rawobj *raw) +static int commit_quick_parse( + git_revwalk *walk, + git_commit_list_node *commit, + uint8_t *buffer, + size_t buffer_len) { const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1; - unsigned char *buffer = raw->data; - unsigned char *buffer_end = buffer + raw->len; - unsigned char *parents_start, *committer_start; + uint8_t *buffer_end = buffer + buffer_len; + uint8_t *parents_start, *committer_start; int i, parents = 0; int commit_time; @@ -182,11 +185,11 @@ int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit) if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0) return error; - if (obj->raw.type != GIT_OBJ_COMMIT) { + if (obj->cached.type != GIT_OBJ_COMMIT) { giterr_set(GITERR_INVALID, "Object is no commit object"); error = -1; } else - error = commit_quick_parse(walk, commit, &obj->raw); + error = commit_quick_parse(walk, commit, obj->buffer, obj->cached.size); git_odb_object_free(obj); return error; diff --git a/src/object.c b/src/object.c index 54ceea33c..2667fcaf1 100644 --- a/src/object.c +++ b/src/object.c @@ -86,19 +86,20 @@ int git_object__from_odb_object( int error; git_object *object = NULL; - if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) { - giterr_set(GITERR_INVALID, "The requested type does not match the type in the ODB"); + if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) { + giterr_set(GITERR_INVALID, + "The requested type does not match the type in the ODB"); return GIT_ENOTFOUND; } - type = odb_obj->raw.type; + type = odb_obj->cached.type; if ((error = create_object(&object, type)) < 0) return error; /* Initialize parent object */ git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid); - object->cached.cache_size = (uint32_t)odb_obj->raw.len; + object->cached.size = odb_obj->cached.size; object->repo = repo; switch (type) { diff --git a/src/odb.c b/src/odb.c index 821fbd70c..16a842aa8 100644 --- a/src/odb.c +++ b/src/odb.c @@ -65,6 +65,7 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj) if (!git_object_typeisloose(obj->type)) return -1; + if (!obj->data && obj->len != 0) return -1; @@ -87,7 +88,9 @@ static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source) memset(object, 0x0, sizeof(git_odb_object)); git_oid_cpy(&object->cached.oid, oid); - memcpy(&object->raw, source, sizeof(git_rawobj)); + object->cached.size = source->len; + object->cached.type = source->type; + object->buffer = source->data; return object; } @@ -95,7 +98,7 @@ static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source) void git_odb_object__free(git_odb_object *object) { if (object != NULL) { - git__free(object->raw.data); + git__free(object->buffer); git__free(object); } } @@ -107,17 +110,17 @@ const git_oid *git_odb_object_id(git_odb_object *object) const void *git_odb_object_data(git_odb_object *object) { - return object->raw.data; + return object->buffer; } size_t git_odb_object_size(git_odb_object *object) { - return object->raw.len; + return object->cached.size; } git_otype git_odb_object_type(git_odb_object *object) { - return object->raw.type; + return object->cached.type; } void git_odb_object_free(git_odb_object *object) @@ -637,8 +640,8 @@ int git_odb__read_header_or_object( assert(db && id && out && len_p && type_p); if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { - *len_p = object->raw.len; - *type_p = object->raw.type; + *len_p = object->cached.size; + *type_p = object->cached.type; *out = object; return 0; } @@ -663,8 +666,8 @@ int git_odb__read_header_or_object( if ((error = git_odb_read(&object, db, id)) < 0) return error; /* error already set - pass along */ - *len_p = object->raw.len; - *type_p = object->raw.type; + *len_p = object->cached.size; + *type_p = object->cached.type; *out = object; return 0; diff --git a/src/odb.h b/src/odb.h index 9abf594e8..22c6e1668 100644 --- a/src/odb.h +++ b/src/odb.h @@ -29,7 +29,7 @@ typedef struct { /* EXPORT */ struct git_odb_object { git_cached_obj cached; - git_rawobj raw; + void *buffer; }; /* EXPORT */ diff --git a/src/tag.c b/src/tag.c index c82decbe3..3095d1287 100644 --- a/src/tag.c +++ b/src/tag.c @@ -324,7 +324,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu if (git_odb_read(&target_obj, odb, &tag.target) < 0) goto on_error; - if (tag.type != target_obj->raw.type) { + if (tag.type != target_obj->cached.type) { giterr_set(GITERR_TAG, "The type for the given target is invalid"); goto on_error; } @@ -397,7 +397,7 @@ int git_tag_delete(git_repository *repo, const char *tag_name) int git_tag__parse(git_tag *tag, git_odb_object *obj) { assert(tag); - return git_tag__parse_buffer(tag, obj->raw.data, obj->raw.len); + return git_tag__parse_buffer(tag, obj->buffer, obj->cached.size); } typedef struct { diff --git a/src/tree.c b/src/tree.c index d2db84055..6ffb07c69 100644 --- a/src/tree.c +++ b/src/tree.c @@ -419,7 +419,9 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf int git_tree__parse(git_tree *tree, git_odb_object *obj) { assert(tree); - return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len); + return tree_parse_buffer(tree, + (char *)obj->buffer, + (char *)obj->buffer + obj->cached.size); } static size_t find_next_dir(const char *dirname, git_index *index, size_t start) diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c index 9d92d8d60..435bd4f2c 100644 --- a/tests-clar/diff/workdir.c +++ b/tests-clar/diff/workdir.c @@ -908,7 +908,6 @@ void test_diff_workdir__can_diff_empty_file(void) /* baseline - make sure there are no outstanding diffs */ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts)); - git_tree_free(tree); cl_assert_equal_i(2, (int)git_diff_num_deltas(diff)); git_diff_list_free(diff); @@ -935,6 +934,8 @@ void test_diff_workdir__can_diff_empty_file(void) cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 1)); git_diff_patch_free(patch); git_diff_list_free(diff); + + git_tree_free(tree); } void test_diff_workdir__to_index_issue_1397(void) diff --git a/tests-clar/object/raw/write.c b/tests-clar/object/raw/write.c index 60aa31f6a..9709c0302 100644 --- a/tests-clar/object/raw/write.c +++ b/tests-clar/object/raw/write.c @@ -63,6 +63,7 @@ void test_body(object_data *d, git_rawobj *o) git_odb *db; git_oid id1, id2; git_odb_object *obj; + git_rawobj tmp; make_odb_dir(); cl_git_pass(git_odb_open(&db, odb_dir)); @@ -73,7 +74,12 @@ void test_body(object_data *d, git_rawobj *o) check_object_files(d); cl_git_pass(git_odb_read(&obj, db, &id1)); - cmp_objects(&obj->raw, o); + + tmp.data = obj->buffer; + tmp.len = obj->cached.size; + tmp.type = obj->cached.type; + + cmp_objects(&tmp, o); git_odb_object_free(obj); git_odb_free(db); diff --git a/tests-clar/odb/loose.c b/tests-clar/odb/loose.c index f95dc28d4..9539bb24c 100644 --- a/tests-clar/odb/loose.c +++ b/tests-clar/odb/loose.c @@ -30,6 +30,7 @@ static void test_read_object(object_data *data) git_oid id; git_odb_object *obj; git_odb *odb; + git_rawobj tmp; write_object_files(data); @@ -37,7 +38,11 @@ static void test_read_object(object_data *data) cl_git_pass(git_oid_fromstr(&id, data->id)); cl_git_pass(git_odb_read(&obj, odb, &id)); - cmp_objects((git_rawobj *)&obj->raw, data); + tmp.data = obj->buffer; + tmp.len = obj->cached.size; + tmp.type = obj->cached.type; + + cmp_objects(&tmp, data); git_odb_object_free(obj); git_odb_free(odb); diff --git a/tests-clar/odb/packed.c b/tests-clar/odb/packed.c index 90e9f3abd..b4f549b58 100644 --- a/tests-clar/odb/packed.c +++ b/tests-clar/odb/packed.c @@ -46,8 +46,8 @@ void test_odb_packed__read_header_0(void) cl_git_pass(git_odb_read(&obj, _odb, &id)); cl_git_pass(git_odb_read_header(&len, &type, _odb, &id)); - cl_assert(obj->raw.len == len); - cl_assert(obj->raw.type == type); + cl_assert(obj->cached.size == len); + cl_assert(obj->cached.type == type); git_odb_object_free(obj); } @@ -70,8 +70,8 @@ void test_odb_packed__read_header_1(void) cl_git_pass(git_odb_read(&obj, _odb, &id)); cl_git_pass(git_odb_read_header(&len, &type, _odb, &id)); - cl_assert(obj->raw.len == len); - cl_assert(obj->raw.type == type); + cl_assert(obj->cached.size == len); + cl_assert(obj->cached.type == type); git_odb_object_free(obj); } diff --git a/tests-clar/odb/packed_one.c b/tests-clar/odb/packed_one.c index 4f9bde9ed..0c6ed387b 100644 --- a/tests-clar/odb/packed_one.c +++ b/tests-clar/odb/packed_one.c @@ -52,8 +52,8 @@ void test_odb_packed_one__read_header_0(void) cl_git_pass(git_odb_read(&obj, _odb, &id)); cl_git_pass(git_odb_read_header(&len, &type, _odb, &id)); - cl_assert(obj->raw.len == len); - cl_assert(obj->raw.type == type); + cl_assert(obj->cached.size == len); + cl_assert(obj->cached.type == type); git_odb_object_free(obj); } From cf7850a4f70a1153ed640744750391d99000d546 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 3 Apr 2013 23:09:54 +0200 Subject: [PATCH 045/384] Duplicated type object --- src/cache.h | 4 ++-- src/object.c | 15 ++++++--------- src/object.h | 1 - 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/cache.h b/src/cache.h index 3633f62a7..d57f244dd 100644 --- a/src/cache.h +++ b/src/cache.h @@ -22,9 +22,9 @@ enum { typedef struct { git_oid oid; - int32_t type; + int16_t type; + uint16_t flags; size_t size; - uint32_t flags; git_atomic refcount; } git_cached_obj; diff --git a/src/object.c b/src/object.c index 2667fcaf1..80b765ef9 100644 --- a/src/object.c +++ b/src/object.c @@ -71,8 +71,6 @@ static int create_object(git_object **object_out, git_otype type) return -1; } - object->type = type; - *object_out = object; return 0; } @@ -92,17 +90,16 @@ int git_object__from_odb_object( return GIT_ENOTFOUND; } - type = odb_obj->cached.type; - - if ((error = create_object(&object, type)) < 0) + if ((error = create_object(&object, odb_obj->cached.type)) < 0) return error; /* Initialize parent object */ git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid); object->cached.size = odb_obj->cached.size; + object->cached.type = odb_obj->cached.type; object->repo = repo; - switch (type) { + switch (object->cached.type) { case GIT_OBJ_COMMIT: error = git_commit__parse((git_commit *)object, odb_obj); break; @@ -167,7 +164,7 @@ int git_object_lookup_prefix( if (cached->flags == GIT_CACHE_STORE_PARSED) { object = (git_object *)cached; - if (type != GIT_OBJ_ANY && type != object->type) { + if (type != GIT_OBJ_ANY && type != object->cached.type) { git_object_free(object); giterr_set(GITERR_INVALID, "The requested type does not match the type in ODB"); @@ -231,7 +228,7 @@ void git_object__free(void *_obj) assert(object); - switch (object->type) { + switch (object->cached.type) { case GIT_OBJ_COMMIT: git_commit__free((git_commit *)object); break; @@ -271,7 +268,7 @@ const git_oid *git_object_id(const git_object *obj) git_otype git_object_type(const git_object *obj) { assert(obj); - return obj->type; + return obj->cached.type; } git_repository *git_object_owner(const git_object *obj) diff --git a/src/object.h b/src/object.h index c1e50593c..d187c55b7 100644 --- a/src/object.h +++ b/src/object.h @@ -11,7 +11,6 @@ struct git_object { git_cached_obj cached; git_repository *repo; - git_otype type; }; /* fully free the object; internal method, DO NOT EXPORT */ From 064236ca45067c9a7189e0d30790b8f3541b91ad Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 3 Apr 2013 23:39:42 +0200 Subject: [PATCH 046/384] Per-object max size --- src/cache.c | 56 ++++++++++++++++++++++++++--------------------------- src/cache.h | 1 + 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/cache.c b/src/cache.c index 316007bdf..c6e983597 100644 --- a/src/cache.c +++ b/src/cache.c @@ -17,21 +17,20 @@ GIT__USE_OIDMAP -bool git_cache__store_types[8] = { - false, /* GIT_OBJ__EXT1 */ - true, /* GIT_OBJ_COMMIT */ - true, /* GIT_OBJ_TREE */ - false, /* GIT_OBJ_BLOB */ - true, /* GIT_OBJ_TAG */ - false, /* GIT_OBJ__EXT2 */ - false, /* GIT_OBJ_OFS_DELTA */ - false /* GIT_OBJ_REF_DELTA */ +size_t git_cache__max_object_size[8] = { + 0, /* GIT_OBJ__EXT1 */ + 4096, /* GIT_OBJ_COMMIT */ + 4096, /* GIT_OBJ_TREE */ + 0, /* GIT_OBJ_BLOB */ + 4096, /* GIT_OBJ_TAG */ + 0, /* GIT_OBJ__EXT2 */ + 0, /* GIT_OBJ_OFS_DELTA */ + 0 /* GIT_OBJ_REF_DELTA */ }; -size_t git_cache__max_object_size = 4096; - int git_cache_init(git_cache *cache) { + cache->used_memory = 0; cache->map = git_oidmap_alloc(); git_mutex_init(&cache->lock); return 0; @@ -43,30 +42,35 @@ void git_cache_free(git_cache *cache) git_mutex_free(&cache->lock); } -static void cache_evict_entries(git_cache *cache, size_t evict) +/* Call with lock, yo */ +static void cache_evict_entries(git_cache *cache, size_t evict_count) { uint32_t seed = rand(); /* do not infinite loop if there's not enough entries to evict */ - if (evict > kh_size(cache->map)) + if (evict_count > kh_size(cache->map)) return; - while (evict > 0) { + while (evict_count > 0) { khiter_t pos = seed++ % kh_end(cache->map); if (kh_exist(cache->map, pos)) { + git_cached_obj *evict = kh_val(cache->map, pos); + + evict_count--; + cache->used_memory -= evict->size; + git_cached_obj_decref(evict); + kh_del(oid, cache->map, pos); - evict--; } } } static bool cache_should_store(git_otype object_type, size_t object_size) { - if (!git_cache__store_types[object_type]) - return false; + size_t max_size = git_cache__max_object_size[object_type]; - if (object_size > git_cache__max_object_size) + if (max_size == 0 || object_size > max_size) return false; return true; @@ -100,6 +104,11 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) { khiter_t pos; + git_cached_obj_incref(entry); + + if (!cache_should_store(entry->type, entry->size)) + return entry; + if (git_mutex_lock(&cache->lock) < 0) return entry; @@ -114,6 +123,7 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) kh_key(cache->map, pos) = &entry->oid; kh_val(cache->map, pos) = entry; git_cached_obj_incref(entry); + cache->used_memory += entry->size; } } /* found */ @@ -142,22 +152,12 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) void *git_cache_store_raw(git_cache *cache, git_odb_object *entry) { - git_cached_obj_incref(entry); - - if (!cache_should_store(entry->cached.type, entry->cached.size)) - return entry; - entry->cached.flags = GIT_CACHE_STORE_RAW; return cache_store(cache, (git_cached_obj *)entry); } void *git_cache_store_parsed(git_cache *cache, git_object *entry) { - git_cached_obj_incref(entry); - - if (!cache_should_store(entry->cached.type, entry->cached.size)) - return entry; - entry->cached.flags = GIT_CACHE_STORE_PARSED; return cache_store(cache, (git_cached_obj *)entry); } diff --git a/src/cache.h b/src/cache.h index d57f244dd..65a6e5766 100644 --- a/src/cache.h +++ b/src/cache.h @@ -31,6 +31,7 @@ typedef struct { typedef struct { git_oidmap *map; git_mutex lock; + size_t used_memory; } git_cache; int git_cache_init(git_cache *cache); From d9d423e4215ac3cc17def7b1a353d03031b811f8 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 3 Apr 2013 23:53:32 +0200 Subject: [PATCH 047/384] Some stats --- src/cache.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/cache.c b/src/cache.c index c6e983597..f3ab8a684 100644 --- a/src/cache.c +++ b/src/cache.c @@ -28,6 +28,27 @@ size_t git_cache__max_object_size[8] = { 0 /* GIT_OBJ_REF_DELTA */ }; +void git_cache_dump_stats(git_cache *cache) +{ + git_cached_obj *object; + + if (kh_size(cache->map) == 0) + return; + + printf("Cache %p: %d items cached, %d bytes\n", + cache, kh_size(cache->map), (int)cache->used_memory); + + kh_foreach_value(cache->map, object, { + char oid_str[9]; + printf(" %s%c %s (%d)\n", + git_object_type2string(object->type), + object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ', + git_oid_tostr(oid_str, sizeof(oid_str), &object->oid), + (int)object->size + ); + }); +} + int git_cache_init(git_cache *cache) { cache->used_memory = 0; From e16e268457ab4ab8a4edc8153deca2c8c22dc757 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 4 Apr 2013 02:09:32 +0200 Subject: [PATCH 048/384] No longer needed --- include/git2/common.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index 5318e66b7..b8c3e42ce 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -131,8 +131,6 @@ enum { GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, GIT_OPT_GET_SEARCH_PATH, GIT_OPT_SET_SEARCH_PATH, - GIT_OPT_GET_ODB_CACHE_SIZE, - GIT_OPT_SET_ODB_CACHE_SIZE, }; /** @@ -169,15 +167,6 @@ enum { * - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL, * or GIT_CONFIG_LEVEL_XDG. * - * opts(GIT_OPT_GET_ODB_CACHE_SIZE): - * Get the size of the libgit2 odb cache. - * - * opts(GIT_OPT_SET_ODB_CACHE_SIZE): - * Set the size of the of the libgit2 odb cache. This needs - * to be done before git_repository_open is called, since - * git_repository_open initializes the odb layer. Defaults - * to 128. - * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure From e183e375b83044d7852b8253553c4f782d73c140 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 5 Apr 2013 22:38:14 +0200 Subject: [PATCH 049/384] Clear the cache when there are too many items to expire --- src/cache.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/cache.c b/src/cache.c index f3ab8a684..9f3290f01 100644 --- a/src/cache.c +++ b/src/cache.c @@ -63,14 +63,33 @@ void git_cache_free(git_cache *cache) git_mutex_free(&cache->lock); } +void git_cache_clear(git_cache *cache) +{ + git_cached_obj *evict = NULL; + + if (git_mutex_lock(&cache->lock) < 0) + return; + + kh_foreach_value(cache->map, evict, { + git_cached_obj_decref(evict); + }); + + kh_clear(oid, cache->map); + cache->used_memory = 0; + + git_mutex_unlock(&cache->lock); +} + /* Call with lock, yo */ static void cache_evict_entries(git_cache *cache, size_t evict_count) { uint32_t seed = rand(); /* do not infinite loop if there's not enough entries to evict */ - if (evict_count > kh_size(cache->map)) + if (evict_count > kh_size(cache->map)) { + git_cache_clear(cache); return; + } while (evict_count > 0) { khiter_t pos = seed++ % kh_end(cache->map); From ee12272d170d6a9d60f13d6de6129f56bfb2fbf6 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 5 Apr 2013 22:48:39 +0200 Subject: [PATCH 050/384] Global option setters --- include/git2/common.h | 2 ++ src/cache.c | 10 ++++------ src/cache.h | 3 +++ src/util.c | 11 +++++++++++ 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index b8c3e42ce..80d83d345 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -131,6 +131,8 @@ enum { GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, GIT_OPT_GET_SEARCH_PATH, GIT_OPT_SET_SEARCH_PATH, + GIT_OPT_SET_CACHE_LIMIT, + GIT_OPT_ENABLE_CACHING }; /** diff --git a/src/cache.c b/src/cache.c index 9f3290f01..f8cddeaca 100644 --- a/src/cache.c +++ b/src/cache.c @@ -17,6 +17,8 @@ GIT__USE_OIDMAP +bool git_cache__enabled = true; + size_t git_cache__max_object_size[8] = { 0, /* GIT_OBJ__EXT1 */ 4096, /* GIT_OBJ_COMMIT */ @@ -109,11 +111,7 @@ static void cache_evict_entries(git_cache *cache, size_t evict_count) static bool cache_should_store(git_otype object_type, size_t object_size) { size_t max_size = git_cache__max_object_size[object_type]; - - if (max_size == 0 || object_size > max_size) - return false; - - return true; + return git_cache__enabled && object_size < max_size; } static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags) @@ -121,7 +119,7 @@ static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags) khiter_t pos; git_cached_obj *entry = NULL; - if (git_mutex_lock(&cache->lock) < 0) + if (!git_cache__enabled || git_mutex_lock(&cache->lock) < 0) return NULL; pos = kh_get(oid, cache->map, oid); diff --git a/src/cache.h b/src/cache.h index 65a6e5766..8b2aa1f79 100644 --- a/src/cache.h +++ b/src/cache.h @@ -20,6 +20,9 @@ enum { GIT_CACHE_STORE_PARSED = 2 }; +extern bool git_cache__enabled; +extern size_t git_cache__max_object_size[8]; + typedef struct { git_oid oid; int16_t type; diff --git a/src/util.c b/src/util.c index c4a8c786d..0b5fbdc5a 100644 --- a/src/util.c +++ b/src/util.c @@ -11,6 +11,7 @@ #include #include "posix.h" #include "fileops.h" +#include "cache.h" #ifdef _MSC_VER # include @@ -93,6 +94,16 @@ int git_libgit2_opts(int key, ...) if ((error = config_level_to_futils_dir(va_arg(ap, int))) >= 0) error = git_futils_dirs_set(error, va_arg(ap, const char *)); break; + + case GIT_OPT_SET_CACHE_LIMIT: { + git_otype type = (git_otype)va_arg(ap, int); + git_cache__max_object_size[type] = va_arg(ap, size_t); + break; + } + + case GIT_OPT_ENABLE_CACHING: + git_cache__enabled = va_arg(ap, int); + break; } va_end(ap); From badd85a61354ef7b62c5f8e53d740738e5ef1e57 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 10 Apr 2013 17:10:17 -0700 Subject: [PATCH 051/384] Use git_odb_object_data/_size whereever possible This uses the odb object accessors so we can change the internals more easily... --- src/blob.c | 8 +++++--- src/checkout.c | 4 ++-- src/commit.c | 3 ++- src/commit_list.c | 13 ++++++++----- src/tag.c | 3 ++- src/tree.c | 14 +++++++++----- 6 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/blob.c b/src/blob.c index 7dce4f7ee..732b0f3de 100644 --- a/src/blob.c +++ b/src/blob.c @@ -18,19 +18,21 @@ const void *git_blob_rawcontent(const git_blob *blob) { assert(blob); - return blob->odb_object->buffer; + return git_odb_object_data(blob->odb_object); } git_off_t git_blob_rawsize(const git_blob *blob) { assert(blob); - return (git_off_t)blob->odb_object->cached.size; + return (git_off_t)git_odb_object_size(blob->odb_object); } int git_blob__getbuf(git_buf *buffer, git_blob *blob) { return git_buf_set( - buffer, blob->odb_object->buffer, blob->odb_object->cached.size); + buffer, + git_odb_object_data(blob->odb_object), + git_odb_object_size(blob->odb_object)); } void git_blob__free(git_blob *blob) diff --git a/src/checkout.c b/src/checkout.c index bb2f90606..62a73d6d0 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -710,8 +710,8 @@ static int blob_content_to_file( git_vector filters = GIT_VECTOR_INIT; /* Create a fake git_buf from the blob raw data... */ - filtered.ptr = blob->odb_object->buffer; - filtered.size = blob->odb_object->cached.size; + filtered.ptr = (void *)git_blob_rawcontent(blob); + filtered.size = (size_t)git_blob_rawsize(blob); /* ... and make sure it doesn't get unexpectedly freed */ dont_free_filtered = true; diff --git a/src/commit.c b/src/commit.c index 2057364b5..2cee44cd2 100644 --- a/src/commit.c +++ b/src/commit.c @@ -244,7 +244,8 @@ bad_buffer: int git_commit__parse(git_commit *commit, git_odb_object *obj) { assert(commit); - return git_commit__parse_buffer(commit, obj->buffer, obj->cached.size); + return git_commit__parse_buffer( + commit, git_odb_object_data(obj), git_odb_object_size(obj)); } #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \ diff --git a/src/commit_list.c b/src/commit_list.c index baabbbafb..bd5b5201a 100644 --- a/src/commit_list.c +++ b/src/commit_list.c @@ -103,12 +103,12 @@ git_commit_list_node *git_commit_list_pop(git_commit_list **stack) static int commit_quick_parse( git_revwalk *walk, git_commit_list_node *commit, - uint8_t *buffer, + const uint8_t *buffer, size_t buffer_len) { const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1; - uint8_t *buffer_end = buffer + buffer_len; - uint8_t *parents_start, *committer_start; + const uint8_t *buffer_end = buffer + buffer_len; + const uint8_t *parents_start, *committer_start; int i, parents = 0; int commit_time; @@ -127,7 +127,7 @@ static int commit_quick_parse( for (i = 0; i < parents; ++i) { git_oid oid; - if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < 0) + if (git_oid_fromstr(&oid, (const char *)buffer + strlen("parent ")) < 0) return -1; commit->parents[i] = git_revwalk__commit_lookup(walk, &oid); @@ -189,7 +189,10 @@ int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit) giterr_set(GITERR_INVALID, "Object is no commit object"); error = -1; } else - error = commit_quick_parse(walk, commit, obj->buffer, obj->cached.size); + error = commit_quick_parse( + walk, commit, + (const uint8_t *)git_odb_object_data(obj), + git_odb_object_size(obj)); git_odb_object_free(obj); return error; diff --git a/src/tag.c b/src/tag.c index 3095d1287..b76895d0c 100644 --- a/src/tag.c +++ b/src/tag.c @@ -397,7 +397,8 @@ int git_tag_delete(git_repository *repo, const char *tag_name) int git_tag__parse(git_tag *tag, git_odb_object *obj) { assert(tag); - return git_tag__parse_buffer(tag, obj->buffer, obj->cached.size); + return git_tag__parse_buffer( + tag, git_odb_object_data(obj), git_odb_object_size(obj)); } typedef struct { diff --git a/src/tree.c b/src/tree.c index 6ffb07c69..cc43b920c 100644 --- a/src/tree.c +++ b/src/tree.c @@ -371,7 +371,8 @@ static int tree_error(const char *str, const char *path) return -1; } -static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end) +static int tree_parse_buffer( + git_tree *tree, const char *buffer, const char *buffer_end) { if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0) return -1; @@ -418,10 +419,13 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf int git_tree__parse(git_tree *tree, git_odb_object *obj) { - assert(tree); - return tree_parse_buffer(tree, - (char *)obj->buffer, - (char *)obj->buffer + obj->cached.size); + const char *buf; + + assert(tree && obj); + + buf = (const char *)git_odb_object_data(obj); + + return tree_parse_buffer(tree, buf, buf + git_odb_object_size(obj)); } static size_t find_next_dir(const char *dirname, git_index *index, size_t start) From b12b72ea82776bbbd4296eeac1376055b0487edf Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 12 Apr 2013 12:44:51 -0700 Subject: [PATCH 052/384] Add range checking around cache opts Add a git_cache_set_max_object_size method that does more checking around setting the max object size. Also add a git_cache_size to read the number of objects currently in the cache. This makes it easier to write tests. --- src/cache.c | 23 +++++++++++++++++------ src/cache.h | 12 +++++++++--- src/util.c | 8 +++++--- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/cache.c b/src/cache.c index f8cddeaca..263f736fa 100644 --- a/src/cache.c +++ b/src/cache.c @@ -19,17 +19,28 @@ GIT__USE_OIDMAP bool git_cache__enabled = true; -size_t git_cache__max_object_size[8] = { - 0, /* GIT_OBJ__EXT1 */ +static size_t git_cache__max_object_size[8] = { + 0, /* GIT_OBJ__EXT1 */ 4096, /* GIT_OBJ_COMMIT */ 4096, /* GIT_OBJ_TREE */ - 0, /* GIT_OBJ_BLOB */ + 0, /* GIT_OBJ_BLOB */ 4096, /* GIT_OBJ_TAG */ - 0, /* GIT_OBJ__EXT2 */ - 0, /* GIT_OBJ_OFS_DELTA */ - 0 /* GIT_OBJ_REF_DELTA */ + 0, /* GIT_OBJ__EXT2 */ + 0, /* GIT_OBJ_OFS_DELTA */ + 0 /* GIT_OBJ_REF_DELTA */ }; +int git_cache_set_max_object_size(git_otype type, size_t size) +{ + if (type < 0 || (size_t)type >= ARRAY_SIZE(git_cache__max_object_size)) { + giterr_set(GITERR_INVALID, "type out of range"); + return -1; + } + + git_cache__max_object_size[type] = size; + return 0; +} + void git_cache_dump_stats(git_cache *cache) { git_cached_obj *object; diff --git a/src/cache.h b/src/cache.h index 8b2aa1f79..13b630e89 100644 --- a/src/cache.h +++ b/src/cache.h @@ -20,9 +20,6 @@ enum { GIT_CACHE_STORE_PARSED = 2 }; -extern bool git_cache__enabled; -extern size_t git_cache__max_object_size[8]; - typedef struct { git_oid oid; int16_t type; @@ -37,6 +34,10 @@ typedef struct { size_t used_memory; } git_cache; +extern bool git_cache__enabled; + +int git_cache_set_max_object_size(git_otype type, size_t size); + int git_cache_init(git_cache *cache); void git_cache_free(git_cache *cache); @@ -47,6 +48,11 @@ git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid); git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid); void *git_cache_get_any(git_cache *cache, const git_oid *oid); +GIT_INLINE(size_t) git_cache_size(git_cache *cache) +{ + return (size_t)kh_size(cache->map); +} + GIT_INLINE(void) git_cached_obj_incref(void *_obj) { git_cached_obj *obj = _obj; diff --git a/src/util.c b/src/util.c index 0b5fbdc5a..1ed5d5d16 100644 --- a/src/util.c +++ b/src/util.c @@ -95,14 +95,16 @@ int git_libgit2_opts(int key, ...) error = git_futils_dirs_set(error, va_arg(ap, const char *)); break; - case GIT_OPT_SET_CACHE_LIMIT: { + case GIT_OPT_SET_CACHE_LIMIT: + { git_otype type = (git_otype)va_arg(ap, int); - git_cache__max_object_size[type] = va_arg(ap, size_t); + size_t size = va_arg(ap, size_t); + error = git_cache_set_max_object_size(type, size); break; } case GIT_OPT_ENABLE_CACHING: - git_cache__enabled = va_arg(ap, int); + git_cache__enabled = (va_arg(ap, int) != 0); break; } From 24c70804e87523df99f4740ed2829976ec1a9c1b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 12 Apr 2013 12:59:38 -0700 Subject: [PATCH 053/384] Add mutex around mapping and unmapping pack files When I was writing threading tests for the new cache, the main error I kept running into was a pack file having it's content unmapped underneath the running thread. This adds a lock around the routines that map and unmap the pack data so that threads can effectively reload the data when they need it. This also required reworking the error handling paths in a couple places in the code which I tried to make consistent. --- src/pack.c | 67 +++++++++++++++++++++++++++++++++++------------------- src/pack.h | 1 + 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/pack.c b/src/pack.c index 75ac98186..1bffb4778 100644 --- a/src/pack.c +++ b/src/pack.c @@ -296,24 +296,34 @@ static int pack_index_check(const char *path, struct git_pack_file *p) static int pack_index_open(struct git_pack_file *p) { char *idx_name; - int error; - size_t name_len, offset; + int error = 0; + size_t name_len, base_len; + + if ((error = git_mutex_lock(&p->lock)) < 0) + return error; if (p->index_map.data) - return 0; + goto done; - idx_name = git__strdup(p->pack_name); - GITERR_CHECK_ALLOC(idx_name); + name_len = strlen(p->pack_name); + assert(name_len > strlen(".pack")); /* checked by git_pack_file alloc */ - name_len = strlen(idx_name); - offset = name_len - strlen(".pack"); - assert(offset < name_len); /* make sure no underflow */ + if ((idx_name = git__malloc(name_len)) == NULL) { + error = -1; + goto done; + } - strncpy(idx_name + offset, ".idx", name_len - offset); + base_len = name_len - strlen(".pack"); + memcpy(idx_name, p->pack_name, base_len); + memcpy(idx_name + base_len, ".idx", sizeof(".idx")); error = pack_index_check(idx_name, p); + git__free(idx_name); +done: + git_mutex_unlock(&p->lock); + return error; } @@ -389,7 +399,7 @@ int git_packfile_unpack_header( * the maximum deflated object size is 2^137, which is just * insane, so we know won't exceed what we have been given. */ -// base = pack_window_open(p, w_curs, *curpos, &left); +/* base = pack_window_open(p, w_curs, *curpos, &left); */ base = git_mwindow_open(mwf, w_curs, *curpos, 20, &left); if (base == NULL) return GIT_EBUFS; @@ -789,15 +799,23 @@ git_off_t get_delta_base( static struct git_pack_file *packfile_alloc(size_t extra) { struct git_pack_file *p = git__calloc(1, sizeof(*p) + extra); - if (p != NULL) - p->mwf.fd = -1; + if (!p) + return NULL; + + p->mwf.fd = -1; + git_mutex_init(&p->lock); + return p; } void git_packfile_free(struct git_pack_file *p) { - assert(p); + if (!p) + return; + + if (git_mutex_lock(&p->lock) < 0) + return; cache_free(&p->bases); @@ -810,6 +828,10 @@ void git_packfile_free(struct git_pack_file *p) pack_index_free(p); git__free(p->bad_object_sha1); + + git_mutex_unlock(&p->lock); + + git_mutex_free(&p->lock); git__free(p); } @@ -820,8 +842,6 @@ static int packfile_open(struct git_pack_file *p) git_oid sha1; unsigned char *idx_sha1; - assert(p->index_map.data); - if (!p->index_map.data && pack_index_open(p) < 0) return git_odb__error_notfound("failed to open packfile", NULL); @@ -888,7 +908,10 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path) size_t path_len; *pack_out = NULL; - path_len = strlen(path); + + if (!path || (path_len = strlen(path)) <= strlen(".idx")) + return git_odb__error_notfound("invalid packfile path", NULL); + p = packfile_alloc(path_len + 2); GITERR_CHECK_ALLOC(p); @@ -897,18 +920,13 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path) * the index looks sane. */ path_len -= strlen(".idx"); - if (path_len < 1) { - git__free(p); - return git_odb__error_notfound("invalid packfile path", NULL); - } - memcpy(p->pack_name, path, path_len); - strcpy(p->pack_name + path_len, ".keep"); + memcpy(p->pack_name + path_len, ".keep", sizeof(".keep")); if (git_path_exists(p->pack_name) == true) p->pack_keep = 1; - strcpy(p->pack_name + path_len, ".pack"); + memcpy(p->pack_name + path_len, ".pack", sizeof(".pack")); if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) { git__free(p); return git_odb__error_notfound("packfile not found", NULL); @@ -1039,7 +1057,6 @@ static int pack_entry_find_offset( if ((error = pack_index_open(p)) < 0) return error; - assert(p->index_map.data); index = p->index_map.data; @@ -1099,6 +1116,7 @@ static int pack_entry_find_offset( return git_odb__error_notfound("failed to find offset for pack entry", short_oid); if (found > 1) return git_odb__error_ambiguous("found multiple offsets for pack entry"); + *offset_out = nth_packed_object_offset(p, pos); git_oid_fromraw(found_oid, current); @@ -1110,6 +1128,7 @@ static int pack_entry_find_offset( printf("found lo=%d %s\n", lo, hex_sha1); } #endif + return 0; } diff --git a/src/pack.h b/src/pack.h index 8d7e33dfe..b734ac163 100644 --- a/src/pack.h +++ b/src/pack.h @@ -79,6 +79,7 @@ typedef struct { struct git_pack_file { git_mwindow_file mwf; git_map index_map; + git_mutex lock; uint32_t num_objects; uint32_t num_bad_objects; From 917f60c50bce09f789aeb927b45ba3bca5a23877 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 12 Apr 2013 13:04:08 -0700 Subject: [PATCH 054/384] Add tests for oidmap and new cache with threading This adds some basic tests for the oidmap just to make sure that collisions, etc. are dealt with correctly. This also adds some tests for the new caching that check if items are inserted (or not inserted) properly into the cache, and that the cache can hold up in a multithreaded environment without error. --- tests-clar/core/oidmap.c | 110 ++++++++++++++++++ tests-clar/object/cache.c | 232 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 342 insertions(+) create mode 100644 tests-clar/core/oidmap.c create mode 100644 tests-clar/object/cache.c diff --git a/tests-clar/core/oidmap.c b/tests-clar/core/oidmap.c new file mode 100644 index 000000000..ec4b5e775 --- /dev/null +++ b/tests-clar/core/oidmap.c @@ -0,0 +1,110 @@ +#include "clar_libgit2.h" +#include "oidmap.h" + +GIT__USE_OIDMAP; + +typedef struct { + git_oid oid; + size_t extra; +} oidmap_item; + +#define NITEMS 0x0fff + +void test_core_oidmap__basic(void) +{ + git_oidmap *map; + oidmap_item items[NITEMS]; + uint32_t i, j; + + for (i = 0; i < NITEMS; ++i) { + items[i].extra = i; + for (j = 0; j < GIT_OID_RAWSZ / 4; ++j) { + items[i].oid.id[j * 4 ] = (unsigned char)i; + items[i].oid.id[j * 4 + 1] = (unsigned char)(i >> 8); + items[i].oid.id[j * 4 + 2] = (unsigned char)(i >> 16); + items[i].oid.id[j * 4 + 3] = (unsigned char)(i >> 24); + } + } + + map = git_oidmap_alloc(); + cl_assert(map != NULL); + + for (i = 0; i < NITEMS; ++i) { + khiter_t pos; + int ret; + + pos = kh_get(oid, map, &items[i].oid); + cl_assert(pos == kh_end(map)); + + pos = kh_put(oid, map, &items[i].oid, &ret); + cl_assert(ret != 0); + + kh_val(map, pos) = &items[i]; + } + + + for (i = 0; i < NITEMS; ++i) { + khiter_t pos; + + pos = kh_get(oid, map, &items[i].oid); + cl_assert(pos != kh_end(map)); + + cl_assert_equal_p(kh_val(map, pos), &items[i]); + } + + git_oidmap_free(map); +} + +void test_core_oidmap__hash_collision(void) +{ + git_oidmap *map; + oidmap_item items[NITEMS]; + uint32_t i, j; + + for (i = 0; i < NITEMS; ++i) { + uint32_t segment = i / 8; + int modi = i - (segment * 8); + + items[i].extra = i; + + for (j = 0; j < GIT_OID_RAWSZ / 4; ++j) { + items[i].oid.id[j * 4 ] = (unsigned char)modi; + items[i].oid.id[j * 4 + 1] = (unsigned char)(modi >> 8); + items[i].oid.id[j * 4 + 2] = (unsigned char)(modi >> 16); + items[i].oid.id[j * 4 + 3] = (unsigned char)(modi >> 24); + } + + items[i].oid.id[ 8] = (unsigned char)i; + items[i].oid.id[ 9] = (unsigned char)(i >> 8); + items[i].oid.id[10] = (unsigned char)(i >> 16); + items[i].oid.id[11] = (unsigned char)(i >> 24); + } + + map = git_oidmap_alloc(); + cl_assert(map != NULL); + + for (i = 0; i < NITEMS; ++i) { + khiter_t pos; + int ret; + + pos = kh_get(oid, map, &items[i].oid); + cl_assert(pos == kh_end(map)); + + pos = kh_put(oid, map, &items[i].oid, &ret); + cl_assert(ret != 0); + + kh_val(map, pos) = &items[i]; + } + + + for (i = 0; i < NITEMS; ++i) { + khiter_t pos; + + pos = kh_get(oid, map, &items[i].oid); + cl_assert(pos != kh_end(map)); + + cl_assert_equal_p(kh_val(map, pos), &items[i]); + } + + git_oidmap_free(map); +} diff --git a/tests-clar/object/cache.c b/tests-clar/object/cache.c new file mode 100644 index 000000000..8121247c9 --- /dev/null +++ b/tests-clar/object/cache.c @@ -0,0 +1,232 @@ +#include "clar_libgit2.h" +#include "repository.h" + +static git_repository *g_repo; + +void test_object_cache__initialize(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); +} + +void test_object_cache__cleanup(void) +{ + git_repository_free(g_repo); + g_repo = NULL; + + git_libgit2_opts(GIT_OPT_SET_CACHE_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0); +} + +static struct { + git_otype type; + const char *sha; +} g_data[] = { + /* HEAD */ + { GIT_OBJ_BLOB, "a8233120f6ad708f843d861ce2b7228ec4e3dec6" }, /* README */ + { GIT_OBJ_BLOB, "3697d64be941a53d4ae8f6a271e4e3fa56b022cc" }, /* branch_file.txt */ + { GIT_OBJ_BLOB, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd" }, /* new.txt */ + + /* refs/heads/subtrees */ + { GIT_OBJ_BLOB, "1385f264afb75a56a5bec74243be9b367ba4ca08" }, /* README */ + { GIT_OBJ_TREE, "f1425cef211cc08caa31e7b545ffb232acb098c3" }, /* ab */ + { GIT_OBJ_BLOB, "d6c93164c249c8000205dd4ec5cbca1b516d487f" }, /* ab/4.txt */ + { GIT_OBJ_TREE, "9a03079b8a8ee85a0bee58bf9be3da8b62414ed4" }, /* ab/c */ + { GIT_OBJ_BLOB, "270b8ea76056d5cad83af921837702d3e3c2924d" }, /* ab/c/3.txt */ + { GIT_OBJ_TREE, "b6361fc6a97178d8fc8639fdeed71c775ab52593" }, /* ab/de */ + { GIT_OBJ_BLOB, "e7b4ad382349ff96dd8199000580b9b1e2042eb0" }, /* ab/de/2.txt */ + { GIT_OBJ_TREE, "3259a6bd5b57fb9c1281bb7ed3167b50f224cb54" }, /* ab/de/fgh */ + { GIT_OBJ_BLOB, "1f67fc4386b2d171e0d21be1c447e12660561f9b" }, /* ab/de/fgh/1.txt */ + { GIT_OBJ_BLOB, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057" }, /* branch_file.txt */ + { GIT_OBJ_BLOB, "fa49b077972391ad58037050f2a75f74e3671e92" }, /* new.txt */ + + /* refs/heads/chomped */ + { GIT_OBJ_BLOB, "0266163a49e280c4f5ed1e08facd36a2bd716bcf" }, /* readme.txt */ + + { 0, NULL }, + { 0, NULL } +}; + +void test_object_cache__cache_everything(void) +{ + int i, start; + git_oid oid; + git_odb_object *odb_obj; + git_object *obj; + git_odb *odb; + + git_libgit2_opts( + GIT_OPT_SET_CACHE_LIMIT, (int)GIT_OBJ_BLOB, (size_t)32767); + + cl_git_pass(git_repository_odb(&odb, g_repo)); + + start = (int)git_cache_size(&g_repo->objects); + + for (i = 0; g_data[i].sha != NULL; ++i) { + int count = (int)git_cache_size(&g_repo->objects); + + cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha)); + + /* alternate between loading raw and parsed objects */ + if ((i & 1) == 0) { + cl_git_pass(git_odb_read(&odb_obj, odb, &oid)); + cl_assert(g_data[i].type == git_odb_object_type(odb_obj)); + git_odb_object_free(odb_obj); + } else { + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + cl_assert(g_data[i].type == git_object_type(obj)); + git_object_free(obj); + } + + cl_assert_equal_i(count + 1, (int)git_cache_size(&g_repo->objects)); + } + + cl_assert_equal_i(i, git_cache_size(&g_repo->objects) - start); + + git_odb_free(odb); + + for (i = 0; g_data[i].sha != NULL; ++i) { + int count = (int)git_cache_size(&g_repo->objects); + + cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha)); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + cl_assert(g_data[i].type == git_object_type(obj)); + git_object_free(obj); + + cl_assert_equal_i(count, (int)git_cache_size(&g_repo->objects)); + } +} + +void test_object_cache__cache_no_blobs(void) +{ + int i, start, nonblobs = 0; + git_oid oid; + git_odb_object *odb_obj; + git_object *obj; + git_odb *odb; + + git_libgit2_opts(GIT_OPT_SET_CACHE_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0); + + cl_git_pass(git_repository_odb(&odb, g_repo)); + + start = (int)git_cache_size(&g_repo->objects); + + for (i = 0; g_data[i].sha != NULL; ++i) { + int count = (int)git_cache_size(&g_repo->objects); + + cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha)); + + /* alternate between loading raw and parsed objects */ + if ((i & 1) == 0) { + cl_git_pass(git_odb_read(&odb_obj, odb, &oid)); + cl_assert(g_data[i].type == git_odb_object_type(odb_obj)); + git_odb_object_free(odb_obj); + } else { + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + cl_assert(g_data[i].type == git_object_type(obj)); + git_object_free(obj); + } + + if (g_data[i].type == GIT_OBJ_BLOB) + cl_assert_equal_i(count, (int)git_cache_size(&g_repo->objects)); + else { + cl_assert_equal_i(count + 1, (int)git_cache_size(&g_repo->objects)); + nonblobs++; + } + } + + cl_assert_equal_i(nonblobs, git_cache_size(&g_repo->objects) - start); + + git_odb_free(odb); +} + +static void *cache_parsed(void *arg) +{ + int i; + git_oid oid; + git_object *obj; + + for (i = ((int *)arg)[1]; g_data[i].sha != NULL; i += 2) { + cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha)); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + cl_assert(g_data[i].type == git_object_type(obj)); + git_object_free(obj); + } + + for (i = 0; i < ((int *)arg)[1]; i += 2) { + cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha)); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + cl_assert(g_data[i].type == git_object_type(obj)); + git_object_free(obj); + } + + return arg; +} + +static void *cache_raw(void *arg) +{ + int i; + git_oid oid; + git_odb *odb; + git_odb_object *odb_obj; + + cl_git_pass(git_repository_odb(&odb, g_repo)); + + for (i = ((int *)arg)[1]; g_data[i].sha != NULL; i += 2) { + cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha)); + cl_git_pass(git_odb_read(&odb_obj, odb, &oid)); + cl_assert(g_data[i].type == git_odb_object_type(odb_obj)); + git_odb_object_free(odb_obj); + } + + for (i = 0; i < ((int *)arg)[1]; i += 2) { + cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha)); + cl_git_pass(git_odb_read(&odb_obj, odb, &oid)); + cl_assert(g_data[i].type == git_odb_object_type(odb_obj)); + git_odb_object_free(odb_obj); + } + + git_odb_free(odb); + + return arg; +} + +#define REPEAT 50 +#define THREADCOUNT 20 + +void test_object_cache__threadmania(void) +{ + int try, th, max_i; + git_thread t[THREADCOUNT]; + void *data; + void *(*fn)(void *); + + for (max_i = 0; g_data[max_i].sha != NULL; ++max_i) + /* count up */; + + for (try = 0; try < REPEAT; ++try) { + + for (th = 0; th < THREADCOUNT; ++th) { + data = git__malloc(2 * sizeof(int)); + + ((int *)data)[0] = th; + ((int *)data)[1] = th % max_i; + + fn = (th & 1) ? cache_parsed : cache_raw; + +#ifdef GIT_THREADS + git_thread_create(&t[th], NULL, fn, data); +#else + fn(data); + git__free(data); +#endif + } + +#ifdef GIT_THREADS + for (th = 0; th < THREADCOUNT; ++th) { + git_thread_join(t[th], &data); + cl_assert_equal_i(th, ((int *)data)[0]); + git__free(data); + } +#endif + + } +} From 786062639f05e361da977f3f1f6286141fa12fca Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 15 Apr 2013 00:05:44 -0700 Subject: [PATCH 055/384] Add callback to git_objects_table This adds create and free callback to the git_objects_table so that more of the creation and destruction of objects can be table driven instead of using switch statements. This also makes the semantics of certain object creation functions consistent so that we can make better use of function pointers. This also fixes a theoretical error case where an object allocation fails and we end up storing NULL into the cache. --- src/blob.c | 8 +-- src/blob.h | 4 +- src/cache.c | 14 ++-- src/cache.h | 12 ++-- src/commit.c | 16 ++--- src/commit.h | 5 +- src/object.c | 145 ++++++++++++++------------------------ src/odb.c | 31 +++++--- src/odb.h | 5 +- src/oidmap.h | 4 +- src/tag.c | 19 ++--- src/tag.h | 5 +- src/tree.c | 27 +++---- src/tree.h | 4 +- tests-clar/commit/parse.c | 13 ++-- 15 files changed, 128 insertions(+), 184 deletions(-) diff --git a/src/blob.c b/src/blob.c index 732b0f3de..501c13d1a 100644 --- a/src/blob.c +++ b/src/blob.c @@ -35,17 +35,17 @@ int git_blob__getbuf(git_buf *buffer, git_blob *blob) git_odb_object_size(blob->odb_object)); } -void git_blob__free(git_blob *blob) +void git_blob__free(void *blob) { - git_odb_object_free(blob->odb_object); + git_odb_object_free(((git_blob *)blob)->odb_object); git__free(blob); } -int git_blob__parse(git_blob *blob, git_odb_object *odb_obj) +int git_blob__from_odb_object(void *blob, git_odb_object *odb_obj) { assert(blob); git_cached_obj_incref((git_cached_obj *)odb_obj); - blob->odb_object = odb_obj; + ((git_blob *)blob)->odb_object = odb_obj; return 0; } diff --git a/src/blob.h b/src/blob.h index 524734b1f..4873505fd 100644 --- a/src/blob.h +++ b/src/blob.h @@ -17,8 +17,8 @@ struct git_blob { git_odb_object *odb_object; }; -void git_blob__free(git_blob *blob); -int git_blob__parse(git_blob *blob, git_odb_object *obj); +void git_blob__free(void *blob); +int git_blob__from_odb_object(void *blob, git_odb_object *obj); int git_blob__getbuf(git_buf *buffer, git_blob *blob); #endif diff --git a/src/cache.c b/src/cache.c index 263f736fa..c51be895e 100644 --- a/src/cache.c +++ b/src/cache.c @@ -70,12 +70,6 @@ int git_cache_init(git_cache *cache) return 0; } -void git_cache_free(git_cache *cache) -{ - git_oidmap_free(cache->map); - git_mutex_free(&cache->lock); -} - void git_cache_clear(git_cache *cache) { git_cached_obj *evict = NULL; @@ -93,6 +87,14 @@ void git_cache_clear(git_cache *cache) git_mutex_unlock(&cache->lock); } +void git_cache_free(git_cache *cache) +{ + git_cache_clear(cache); + + git_oidmap_free(cache->map); + git_mutex_free(&cache->lock); +} + /* Call with lock, yo */ static void cache_evict_entries(git_cache *cache, size_t evict_count) { diff --git a/src/cache.h b/src/cache.h index 13b630e89..e95d521fe 100644 --- a/src/cache.h +++ b/src/cache.h @@ -21,17 +21,17 @@ enum { }; typedef struct { - git_oid oid; - int16_t type; - uint16_t flags; - size_t size; + git_oid oid; + int16_t type; /* git_otype value */ + uint16_t flags; /* GIT_CACHE_STORE value */ + size_t size; git_atomic refcount; } git_cached_obj; typedef struct { git_oidmap *map; - git_mutex lock; - size_t used_memory; + git_mutex lock; + size_t used_memory; } git_cache; extern bool git_cache__enabled; diff --git a/src/commit.c b/src/commit.c index 2cee44cd2..3eca5b341 100644 --- a/src/commit.c +++ b/src/commit.c @@ -31,8 +31,10 @@ static void clear_parents(git_commit *commit) git_vector_clear(&commit->parent_ids); } -void git_commit__free(git_commit *commit) +void git_commit__free(void *_commit) { + git_commit *commit = _commit; + clear_parents(commit); git_vector_free(&commit->parent_ids); @@ -166,10 +168,9 @@ int git_commit_create( return retval; } -int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len) +int git_commit__parse(void *_commit, const char *buffer, const char *buffer_end) { - const char *buffer = data; - const char *buffer_end = (const char *)data + len; + git_commit *commit = _commit; git_oid parent_id; if (git_vector_init(&commit->parent_ids, 4, NULL) < 0) @@ -241,13 +242,6 @@ bad_buffer: return -1; } -int git_commit__parse(git_commit *commit, git_odb_object *obj) -{ - assert(commit); - return git_commit__parse_buffer( - commit, git_odb_object_data(obj), git_odb_object_size(obj)); -} - #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \ _rvalue git_commit_##_name(const git_commit *commit) \ {\ diff --git a/src/commit.h b/src/commit.h index 1ab164c0b..0c2c3ab5d 100644 --- a/src/commit.h +++ b/src/commit.h @@ -27,8 +27,7 @@ struct git_commit { char *message; }; -void git_commit__free(git_commit *c); -int git_commit__parse(git_commit *commit, git_odb_object *obj); +void git_commit__free(void *commit); +int git_commit__parse(void *commit, const char *buf, const char *bufend); -int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len); #endif diff --git a/src/object.c b/src/object.c index 80b765ef9..3698808c3 100644 --- a/src/object.c +++ b/src/object.c @@ -18,63 +18,39 @@ static const int OBJECT_BASE_SIZE = 4096; -static struct { +typedef struct { const char *str; /* type name string */ - int loose; /* valid loose object type flag */ size_t size; /* size in bytes of the object structure */ -} git_objects_table[] = { + + int (*from_odb)(void *self, git_odb_object *obj); + int (*parse)(void *self, const char *buf, const char *buf_end); + void (*free)(void *self); +} git_object_def; + +static git_object_def git_objects_table[] = { /* 0 = GIT_OBJ__EXT1 */ - { "", 0, 0}, + { "", 0, NULL, NULL, NULL }, /* 1 = GIT_OBJ_COMMIT */ - { "commit", 1, sizeof(struct git_commit)}, + { "commit", sizeof(git_commit), NULL, git_commit__parse, git_commit__free }, /* 2 = GIT_OBJ_TREE */ - { "tree", 1, sizeof(struct git_tree) }, + { "tree", sizeof(git_tree), NULL, git_tree__parse, git_tree__free }, /* 3 = GIT_OBJ_BLOB */ - { "blob", 1, sizeof(struct git_blob) }, + { "blob", sizeof(git_blob), git_blob__from_odb_object, NULL, git_blob__free }, /* 4 = GIT_OBJ_TAG */ - { "tag", 1, sizeof(struct git_tag) }, + { "tag", sizeof(git_tag), NULL, git_tag__parse, git_tag__free }, /* 5 = GIT_OBJ__EXT2 */ - { "", 0, 0 }, - + { "", 0, NULL, NULL, NULL }, /* 6 = GIT_OBJ_OFS_DELTA */ - { "OFS_DELTA", 0, 0 }, - + { "OFS_DELTA", 0, NULL, NULL, NULL }, /* 7 = GIT_OBJ_REF_DELTA */ - { "REF_DELTA", 0, 0 } + { "REF_DELTA", 0, NULL, NULL, NULL }, }; -static int create_object(git_object **object_out, git_otype type) -{ - git_object *object = NULL; - - assert(object_out); - - *object_out = NULL; - - switch (type) { - case GIT_OBJ_COMMIT: - case GIT_OBJ_TAG: - case GIT_OBJ_BLOB: - case GIT_OBJ_TREE: - object = git__malloc(git_object__size(type)); - GITERR_CHECK_ALLOC(object); - memset(object, 0x0, git_object__size(type)); - break; - - default: - giterr_set(GITERR_INVALID, "The given type is invalid"); - return -1; - } - - *object_out = object; - return 0; -} - int git_object__from_odb_object( git_object **object_out, git_repository *repo, @@ -82,46 +58,47 @@ int git_object__from_odb_object( git_otype type) { int error; + size_t object_size; + git_object_def *def; git_object *object = NULL; + assert(object_out); + *object_out = NULL; + + /* Validate type match */ if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) { giterr_set(GITERR_INVALID, "The requested type does not match the type in the ODB"); return GIT_ENOTFOUND; } - if ((error = create_object(&object, odb_obj->cached.type)) < 0) - return error; + if ((object_size = git_object__size(odb_obj->cached.type)) == 0) { + giterr_set(GITERR_INVALID, "The requested type is invalid"); + return GIT_ENOTFOUND; + } + + /* Allocate and initialize base object */ + object = git__calloc(1, object_size); + GITERR_CHECK_ALLOC(object); - /* Initialize parent object */ git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid); - object->cached.size = odb_obj->cached.size; object->cached.type = odb_obj->cached.type; + object->cached.size = odb_obj->cached.size; object->repo = repo; - switch (object->cached.type) { - case GIT_OBJ_COMMIT: - error = git_commit__parse((git_commit *)object, odb_obj); - break; + /* Parse raw object data */ + def = &git_objects_table[odb_obj->cached.type]; + assert(def->free && (def->from_odb || def->parse)); - case GIT_OBJ_TREE: - error = git_tree__parse((git_tree *)object, odb_obj); - break; - - case GIT_OBJ_TAG: - error = git_tag__parse((git_tag *)object, odb_obj); - break; - - case GIT_OBJ_BLOB: - error = git_blob__parse((git_blob *)object, odb_obj); - break; - - default: - break; + if (def->from_odb) { + error = def->from_odb(object, odb_obj); + } else { + const char *data = (const char *)git_odb_object_data(odb_obj); + error = def->parse(object, data, data + git_odb_object_size(odb_obj)); } if (error < 0) { - git_object__free(object); + def->free(object); return error; } @@ -129,6 +106,17 @@ int git_object__from_odb_object( return 0; } +void git_object__free(void *obj) +{ + git_otype type = ((git_object *)obj)->cached.type; + + if (type < 0 || ((size_t)type) >= ARRAY_SIZE(git_objects_table) || + !git_objects_table[type].free) + git__free(obj); + else + git_objects_table[type].free(obj); +} + int git_object_lookup_prefix( git_object **object_out, git_repository *repo, @@ -222,35 +210,6 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type); } -void git_object__free(void *_obj) -{ - git_object *object = (git_object *)_obj; - - assert(object); - - switch (object->cached.type) { - case GIT_OBJ_COMMIT: - git_commit__free((git_commit *)object); - break; - - case GIT_OBJ_TREE: - git_tree__free((git_tree *)object); - break; - - case GIT_OBJ_TAG: - git_tag__free((git_tag *)object); - break; - - case GIT_OBJ_BLOB: - git_blob__free((git_blob *)object); - break; - - default: - git__free(object); - break; - } -} - void git_object_free(git_object *object) { if (object == NULL) @@ -304,7 +263,7 @@ int git_object_typeisloose(git_otype type) if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table)) return 0; - return git_objects_table[type].loose; + return (git_objects_table[type].size > 0) ? 1 : 0; } size_t git_object__size(git_otype type) diff --git a/src/odb.c b/src/odb.c index 16a842aa8..53630dddc 100644 --- a/src/odb.c +++ b/src/odb.c @@ -82,23 +82,24 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj) } -static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source) +static git_odb_object *odb_object__alloc(const git_oid *oid, git_rawobj *source) { - git_odb_object *object = git__malloc(sizeof(git_odb_object)); - memset(object, 0x0, sizeof(git_odb_object)); + git_odb_object *object = git__calloc(1, sizeof(git_odb_object)); - git_oid_cpy(&object->cached.oid, oid); - object->cached.size = source->len; - object->cached.type = source->type; - object->buffer = source->data; + if (object != NULL) { + git_oid_cpy(&object->cached.oid, oid); + object->cached.type = source->type; + object->cached.size = source->len; + object->buffer = source->data; + } return object; } -void git_odb_object__free(git_odb_object *object) +void git_odb_object__free(void *object) { if (object != NULL) { - git__free(object->buffer); + git__free(((git_odb_object *)object)->buffer); git__free(object); } } @@ -679,6 +680,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) int error; bool refreshed = false; git_rawobj raw; + git_odb_object *object; assert(out && db && id); @@ -713,7 +715,10 @@ attempt_lookup: if (error && error != GIT_PASSTHROUGH) return error; - *out = git_cache_store_raw(odb_cache(db), new_odb_object(id, &raw)); + if ((object = odb_object__alloc(id, &raw)) == NULL) + return -1; + + *out = git_cache_store_raw(odb_cache(db), object); return 0; } @@ -726,6 +731,7 @@ int git_odb_read_prefix( git_rawobj raw; void *data = NULL; bool found = false, refreshed = false; + git_odb_object *object; assert(out && db); @@ -777,7 +783,10 @@ attempt_lookup: if (!found) return git_odb__error_notfound("no match for prefix", short_id); - *out = git_cache_store_raw(odb_cache(db), new_odb_object(&found_full_oid, &raw)); + if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) + return -1; + + *out = git_cache_store_raw(odb_cache(db), object); return 0; } diff --git a/src/odb.h b/src/odb.h index 22c6e1668..0d9f9e2ea 100644 --- a/src/odb.h +++ b/src/odb.h @@ -39,8 +39,6 @@ struct git_odb { git_cache own_cache; }; -void git_odb_object__free(git_odb_object *object); - /* * Hash a git_rawobj internally. * The `git_rawobj` is supposed to be previously initialized @@ -98,4 +96,7 @@ int git_odb__read_header_or_object( git_odb_object **out, size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id); +/* fully free the object; internal method, DO NOT EXPORT */ +void git_odb_object__free(void *object); + #endif diff --git a/src/oidmap.h b/src/oidmap.h index dfa951af3..a29c7cd35 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -19,7 +19,7 @@ __KHASH_TYPE(oid, const git_oid *, void *); typedef khash_t(oid) git_oidmap; -GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid) +GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid) { khint_t h; memcpy(&h, oid, sizeof(khint_t)); @@ -27,7 +27,7 @@ GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid) } #define GIT__USE_OIDMAP \ - __KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, hash_git_oid, git_oid_equal) + __KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal) #define git_oidmap_alloc() kh_init(oid) #define git_oidmap_free(h) kh_destroy(oid,h), h = NULL diff --git a/src/tag.c b/src/tag.c index b76895d0c..7dadc7e60 100644 --- a/src/tag.c +++ b/src/tag.c @@ -15,8 +15,9 @@ #include "git2/signature.h" #include "git2/odb_backend.h" -void git_tag__free(git_tag *tag) +void git_tag__free(void *_tag) { + git_tag *tag = _tag; git_signature_free(tag->tagger); git__free(tag->message); git__free(tag->tag_name); @@ -69,18 +70,17 @@ static int tag_error(const char *str) return -1; } -int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length) +int git_tag__parse(void *_tag, const char *buffer, const char *buffer_end) { static const char *tag_types[] = { NULL, "commit\n", "tree\n", "blob\n", "tag\n" }; + git_tag *tag = _tag; unsigned int i; size_t text_len; char *search; - const char *buffer_end = buffer + length; - if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0) return tag_error("Object field invalid"); @@ -317,7 +317,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu return -1; /* validate the buffer */ - if (git_tag__parse_buffer(&tag, buffer, strlen(buffer)) < 0) + if (git_tag__parse(&tag, buffer, buffer + strlen(buffer)) < 0) return -1; /* validate the target */ @@ -390,15 +390,8 @@ int git_tag_delete(git_repository *repo, const char *tag_name) if ((error = git_reference_delete(tag_ref)) == 0) git_reference_free(tag_ref); - - return error; -} -int git_tag__parse(git_tag *tag, git_odb_object *obj) -{ - assert(tag); - return git_tag__parse_buffer( - tag, git_odb_object_data(obj), git_odb_object_size(obj)); + return error; } typedef struct { diff --git a/src/tag.h b/src/tag.h index c8e421ee6..fb01a6f75 100644 --- a/src/tag.h +++ b/src/tag.h @@ -22,8 +22,7 @@ struct git_tag { char *message; }; -void git_tag__free(git_tag *tag); -int git_tag__parse(git_tag *tag, git_odb_object *obj); -int git_tag__parse_buffer(git_tag *tag, const char *data, size_t len); +void git_tag__free(void *tag); +int git_tag__parse(void *tag, const char *buf, const char *buf_end); #endif diff --git a/src/tree.c b/src/tree.c index cc43b920c..e66fa2370 100644 --- a/src/tree.c +++ b/src/tree.c @@ -219,15 +219,16 @@ git_tree_entry *git_tree_entry_dup(const git_tree_entry *entry) return copy; } -void git_tree__free(git_tree *tree) +void git_tree__free(void *tree) { + git_vector *entries = &((git_tree *)tree)->entries; size_t i; git_tree_entry *e; - git_vector_foreach(&tree->entries, i, e) + git_vector_foreach(entries, i, e) git_tree_entry_free(e); - git_vector_free(&tree->entries); + git_vector_free(entries); git__free(tree); } @@ -371,10 +372,11 @@ static int tree_error(const char *str, const char *path) return -1; } -static int tree_parse_buffer( - git_tree *tree, const char *buffer, const char *buffer_end) +int git_tree__parse(void *tree, const char *buffer, const char *buffer_end) { - if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0) + git_vector *tree_entries = &((git_tree *)tree)->entries; + + if (git_vector_init(tree_entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0) return -1; while (buffer < buffer_end) { @@ -397,7 +399,7 @@ static int tree_parse_buffer( entry = alloc_entry(buffer); GITERR_CHECK_ALLOC(entry); - if (git_vector_insert(&tree->entries, entry) < 0) { + if (git_vector_insert(tree_entries, entry) < 0) { git__free(entry); return -1; } @@ -417,17 +419,6 @@ static int tree_parse_buffer( return 0; } -int git_tree__parse(git_tree *tree, git_odb_object *obj) -{ - const char *buf; - - assert(tree && obj); - - buf = (const char *)git_odb_object_data(obj); - - return tree_parse_buffer(tree, buf, buf + git_odb_object_size(obj)); -} - static size_t find_next_dir(const char *dirname, git_index *index, size_t start) { size_t dirlen, i, entries = git_index_entrycount(index); diff --git a/src/tree.h b/src/tree.h index b77bfd961..cf47fb478 100644 --- a/src/tree.h +++ b/src/tree.h @@ -37,8 +37,8 @@ GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e) extern int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2); -void git_tree__free(git_tree *tree); -int git_tree__parse(git_tree *tree, git_odb_object *obj); +void git_tree__free(void *tree); +int git_tree__parse(void *tree, const char *buf, const char *buf_end); /** * Lookup the first position in the tree with a given prefix. diff --git a/tests-clar/commit/parse.c b/tests-clar/commit/parse.c index b99d27991..792b57626 100644 --- a/tests-clar/commit/parse.c +++ b/tests-clar/commit/parse.c @@ -269,6 +269,7 @@ void test_commit_parse__entire_commit(void) const int broken_commit_count = sizeof(failing_commit_cases) / sizeof(*failing_commit_cases); const int working_commit_count = sizeof(passing_commit_cases) / sizeof(*passing_commit_cases); int i; + const char *buf; for (i = 0; i < broken_commit_count; ++i) { git_commit *commit; @@ -276,9 +277,8 @@ void test_commit_parse__entire_commit(void) memset(commit, 0x0, sizeof(git_commit)); commit->object.repo = g_repo; - cl_git_fail(git_commit__parse_buffer( - commit, failing_commit_cases[i], strlen(failing_commit_cases[i])) - ); + buf = failing_commit_cases[i]; + cl_git_fail(git_commit__parse(commit, buf, buf + strlen(buf))); git_commit__free(commit); } @@ -290,11 +290,8 @@ void test_commit_parse__entire_commit(void) memset(commit, 0x0, sizeof(git_commit)); commit->object.repo = g_repo; - cl_git_pass(git_commit__parse_buffer( - commit, - passing_commit_cases[i], - strlen(passing_commit_cases[i])) - ); + buf = passing_commit_cases[i]; + cl_git_pass(git_commit__parse(commit, buf, buf + strlen(buf))); if (!i) cl_assert_equal_s("", git_commit_message(commit)); From 3f27127d15dfe69943d3c9ddf96d09a2300de3a9 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 16 Apr 2013 11:51:02 -0700 Subject: [PATCH 056/384] Simplify object table parse functions This unifies the object parse functions into one signature that takes an odb_object. --- src/blob.c | 2 +- src/blob.h | 2 +- src/commit.c | 4 +++- src/commit.h | 2 +- src/object.c | 37 ++++++++++++++----------------------- src/tag.c | 38 +++++++++++++++++++++++--------------- src/tag.h | 2 +- src/tree.c | 4 +++- src/tree.h | 2 +- tests-clar/commit/parse.c | 27 +++++++++++++++------------ 10 files changed, 63 insertions(+), 57 deletions(-) diff --git a/src/blob.c b/src/blob.c index 501c13d1a..a68c4cc3e 100644 --- a/src/blob.c +++ b/src/blob.c @@ -41,7 +41,7 @@ void git_blob__free(void *blob) git__free(blob); } -int git_blob__from_odb_object(void *blob, git_odb_object *odb_obj) +int git_blob__parse(void *blob, git_odb_object *odb_obj) { assert(blob); git_cached_obj_incref((git_cached_obj *)odb_obj); diff --git a/src/blob.h b/src/blob.h index 4873505fd..22e37cc3a 100644 --- a/src/blob.h +++ b/src/blob.h @@ -18,7 +18,7 @@ struct git_blob { }; void git_blob__free(void *blob); -int git_blob__from_odb_object(void *blob, git_odb_object *obj); +int git_blob__parse(void *blob, git_odb_object *obj); int git_blob__getbuf(git_buf *buffer, git_blob *blob); #endif diff --git a/src/commit.c b/src/commit.c index 3eca5b341..46c02c292 100644 --- a/src/commit.c +++ b/src/commit.c @@ -168,9 +168,11 @@ int git_commit_create( return retval; } -int git_commit__parse(void *_commit, const char *buffer, const char *buffer_end) +int git_commit__parse(void *_commit, git_odb_object *odb_obj) { git_commit *commit = _commit; + const char *buffer = git_odb_object_data(odb_obj); + const char *buffer_end = buffer + git_odb_object_size(odb_obj); git_oid parent_id; if (git_vector_init(&commit->parent_ids, 4, NULL) < 0) diff --git a/src/commit.h b/src/commit.h index 0c2c3ab5d..d0981b125 100644 --- a/src/commit.h +++ b/src/commit.h @@ -28,6 +28,6 @@ struct git_commit { }; void git_commit__free(void *commit); -int git_commit__parse(void *commit, const char *buf, const char *bufend); +int git_commit__parse(void *commit, git_odb_object *obj); #endif diff --git a/src/object.c b/src/object.c index 3698808c3..b87a07404 100644 --- a/src/object.c +++ b/src/object.c @@ -22,33 +22,32 @@ typedef struct { const char *str; /* type name string */ size_t size; /* size in bytes of the object structure */ - int (*from_odb)(void *self, git_odb_object *obj); - int (*parse)(void *self, const char *buf, const char *buf_end); + int (*parse)(void *self, git_odb_object *obj); void (*free)(void *self); } git_object_def; static git_object_def git_objects_table[] = { /* 0 = GIT_OBJ__EXT1 */ - { "", 0, NULL, NULL, NULL }, + { "", 0, NULL, NULL }, /* 1 = GIT_OBJ_COMMIT */ - { "commit", sizeof(git_commit), NULL, git_commit__parse, git_commit__free }, + { "commit", sizeof(git_commit), git_commit__parse, git_commit__free }, /* 2 = GIT_OBJ_TREE */ - { "tree", sizeof(git_tree), NULL, git_tree__parse, git_tree__free }, + { "tree", sizeof(git_tree), git_tree__parse, git_tree__free }, /* 3 = GIT_OBJ_BLOB */ - { "blob", sizeof(git_blob), git_blob__from_odb_object, NULL, git_blob__free }, + { "blob", sizeof(git_blob), git_blob__parse, git_blob__free }, /* 4 = GIT_OBJ_TAG */ - { "tag", sizeof(git_tag), NULL, git_tag__parse, git_tag__free }, + { "tag", sizeof(git_tag), git_tag__parse, git_tag__free }, /* 5 = GIT_OBJ__EXT2 */ - { "", 0, NULL, NULL, NULL }, + { "", 0, NULL, NULL }, /* 6 = GIT_OBJ_OFS_DELTA */ - { "OFS_DELTA", 0, NULL, NULL, NULL }, + { "OFS_DELTA", 0, NULL, NULL }, /* 7 = GIT_OBJ_REF_DELTA */ - { "REF_DELTA", 0, NULL, NULL, NULL }, + { "REF_DELTA", 0, NULL, NULL }, }; int git_object__from_odb_object( @@ -88,22 +87,14 @@ int git_object__from_odb_object( /* Parse raw object data */ def = &git_objects_table[odb_obj->cached.type]; - assert(def->free && (def->from_odb || def->parse)); + assert(def->free && def->parse); - if (def->from_odb) { - error = def->from_odb(object, odb_obj); - } else { - const char *data = (const char *)git_odb_object_data(odb_obj); - error = def->parse(object, data, data + git_odb_object_size(odb_obj)); - } - - if (error < 0) { + if ((error = def->parse(object, odb_obj)) < 0) def->free(object); - return error; - } + else + *object_out = git_cache_store_parsed(&repo->objects, object); - *object_out = git_cache_store_parsed(&repo->objects, object); - return 0; + return error; } void git_object__free(void *obj) diff --git a/src/tag.c b/src/tag.c index 7dadc7e60..b9a806cd1 100644 --- a/src/tag.c +++ b/src/tag.c @@ -70,13 +70,12 @@ static int tag_error(const char *str) return -1; } -int git_tag__parse(void *_tag, const char *buffer, const char *buffer_end) +static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) { static const char *tag_types[] = { NULL, "commit\n", "tree\n", "blob\n", "tag\n" }; - git_tag *tag = _tag; unsigned int i; size_t text_len; char *search; @@ -157,6 +156,15 @@ int git_tag__parse(void *_tag, const char *buffer, const char *buffer_end) return 0; } +int git_tag__parse(void *_tag, git_odb_object *odb_obj) +{ + git_tag *tag = _tag; + const char *buffer = git_odb_object_data(odb_obj); + const char *buffer_end = buffer + git_odb_object_size(odb_obj); + + return tag_parse(tag, buffer, buffer_end); +} + static int retrieve_tag_reference( git_reference **tag_reference_out, git_buf *ref_name_out, @@ -277,23 +285,23 @@ cleanup: } int git_tag_create( - git_oid *oid, - git_repository *repo, - const char *tag_name, - const git_object *target, - const git_signature *tagger, - const char *message, - int allow_ref_overwrite) + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message, + int allow_ref_overwrite) { return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1); } int git_tag_create_lightweight( - git_oid *oid, - git_repository *repo, - const char *tag_name, - const git_object *target, - int allow_ref_overwrite) + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + int allow_ref_overwrite) { return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0); } @@ -317,7 +325,7 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu return -1; /* validate the buffer */ - if (git_tag__parse(&tag, buffer, buffer + strlen(buffer)) < 0) + if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0) return -1; /* validate the target */ diff --git a/src/tag.h b/src/tag.h index fb01a6f75..d0cd393c7 100644 --- a/src/tag.h +++ b/src/tag.h @@ -23,6 +23,6 @@ struct git_tag { }; void git_tag__free(void *tag); -int git_tag__parse(void *tag, const char *buf, const char *buf_end); +int git_tag__parse(void *tag, git_odb_object *obj); #endif diff --git a/src/tree.c b/src/tree.c index e66fa2370..d6d4b77d3 100644 --- a/src/tree.c +++ b/src/tree.c @@ -372,8 +372,10 @@ static int tree_error(const char *str, const char *path) return -1; } -int git_tree__parse(void *tree, const char *buffer, const char *buffer_end) +int git_tree__parse(void *tree, git_odb_object *odb_obj) { + const char *buffer = git_odb_object_data(odb_obj); + const char *buffer_end = buffer + git_odb_object_size(odb_obj); git_vector *tree_entries = &((git_tree *)tree)->entries; if (git_vector_init(tree_entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0) diff --git a/src/tree.h b/src/tree.h index cf47fb478..7cb2dd36c 100644 --- a/src/tree.h +++ b/src/tree.h @@ -38,7 +38,7 @@ GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e) extern int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2); void git_tree__free(void *tree); -int git_tree__parse(void *tree, const char *buf, const char *buf_end); +int git_tree__parse(void *tree, git_odb_object *obj); /** * Lookup the first position in the tree with a given prefix. diff --git a/tests-clar/commit/parse.c b/tests-clar/commit/parse.c index 792b57626..ad2c746ca 100644 --- a/tests-clar/commit/parse.c +++ b/tests-clar/commit/parse.c @@ -266,32 +266,35 @@ a simple commit which works\n", void test_commit_parse__entire_commit(void) { - const int broken_commit_count = sizeof(failing_commit_cases) / sizeof(*failing_commit_cases); - const int working_commit_count = sizeof(passing_commit_cases) / sizeof(*passing_commit_cases); + const int failing_commit_count = ARRAY_SIZE(failing_commit_cases); + const int passing_commit_count = ARRAY_SIZE(passing_commit_cases); int i; - const char *buf; + git_commit *commit; + git_odb_object fake_odb_object; + memset(&fake_odb_object, 0, sizeof(fake_odb_object)); - for (i = 0; i < broken_commit_count; ++i) { - git_commit *commit; + for (i = 0; i < failing_commit_count; ++i) { commit = (git_commit*)git__malloc(sizeof(git_commit)); memset(commit, 0x0, sizeof(git_commit)); commit->object.repo = g_repo; - buf = failing_commit_cases[i]; - cl_git_fail(git_commit__parse(commit, buf, buf + strlen(buf))); + fake_odb_object.buffer = failing_commit_cases[i]; + fake_odb_object.cached.size = strlen(fake_odb_object.buffer); + + cl_git_fail(git_commit__parse(commit, &fake_odb_object)); git_commit__free(commit); } - for (i = 0; i < working_commit_count; ++i) { - git_commit *commit; - + for (i = 0; i < passing_commit_count; ++i) { commit = (git_commit*)git__malloc(sizeof(git_commit)); memset(commit, 0x0, sizeof(git_commit)); commit->object.repo = g_repo; - buf = passing_commit_cases[i]; - cl_git_pass(git_commit__parse(commit, buf, buf + strlen(buf))); + fake_odb_object.buffer = passing_commit_cases[i]; + fake_odb_object.cached.size = strlen(fake_odb_object.buffer); + + cl_git_pass(git_commit__parse(commit, &fake_odb_object)); if (!i) cl_assert_equal_s("", git_commit_message(commit)); From 116bbdf0446cd5335b73e691c3352f368eac9b8f Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 16 Apr 2013 12:08:21 -0700 Subject: [PATCH 057/384] clean up tree pointer casting --- src/tree.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tree.c b/src/tree.c index d6d4b77d3..58eb92f35 100644 --- a/src/tree.c +++ b/src/tree.c @@ -219,16 +219,16 @@ git_tree_entry *git_tree_entry_dup(const git_tree_entry *entry) return copy; } -void git_tree__free(void *tree) +void git_tree__free(void *_tree) { - git_vector *entries = &((git_tree *)tree)->entries; + git_tree *tree = _tree; size_t i; git_tree_entry *e; - git_vector_foreach(entries, i, e) + git_vector_foreach(&tree->entries, i, e) git_tree_entry_free(e); - git_vector_free(entries); + git_vector_free(&tree->entries); git__free(tree); } @@ -372,13 +372,13 @@ static int tree_error(const char *str, const char *path) return -1; } -int git_tree__parse(void *tree, git_odb_object *odb_obj) +int git_tree__parse(void *_tree, git_odb_object *odb_obj) { + git_tree *tree = _tree; const char *buffer = git_odb_object_data(odb_obj); const char *buffer_end = buffer + git_odb_object_size(odb_obj); - git_vector *tree_entries = &((git_tree *)tree)->entries; - if (git_vector_init(tree_entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0) + if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0) return -1; while (buffer < buffer_end) { @@ -401,7 +401,7 @@ int git_tree__parse(void *tree, git_odb_object *odb_obj) entry = alloc_entry(buffer); GITERR_CHECK_ALLOC(entry); - if (git_vector_insert(tree_entries, entry) < 0) { + if (git_vector_insert(&tree->entries, entry) < 0) { git__free(entry); return -1; } From 536078688549ac3d50483eecdec5a8169d921927 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 15 Apr 2013 00:09:03 -0700 Subject: [PATCH 058/384] Further threading fixes This builds on the earlier thread safety work to make it so that setting the odb, index, refdb, or config for a repository is done in a threadsafe manner with minimized locking time. This is done by adding a lock to the repository object and using it to guard the assignment of the above listed pointers. The lock is only held to assign the pointer value. This also contains some minor fixes to the other work with pack files to reduce the time that locks are being held to and fix an apparently memory leak. --- src/global.c | 6 ++ src/mwindow.c | 2 +- src/pack.c | 19 ++--- src/pack.h | 2 +- src/refdb_fs.c | 1 - src/repository.c | 214 ++++++++++++++++++++++++++++------------------- src/repository.h | 1 + src/util.h | 21 ++++- 8 files changed, 166 insertions(+), 100 deletions(-) diff --git a/src/global.c b/src/global.c index b7fd8e257..a0571d127 100644 --- a/src/global.c +++ b/src/global.c @@ -135,6 +135,12 @@ int git_threads_init(void) void git_threads_shutdown(void) { + if (_tls_init) { + void *ptr = pthread_getspecific(_tls_key); + pthread_setspecific(_tls_key, NULL); + git__free(ptr); + } + pthread_key_delete(_tls_key); _tls_init = 0; git_mutex_free(&git__mwindow_mutex); diff --git a/src/mwindow.c b/src/mwindow.c index b35503d46..7e5fcdfbc 100644 --- a/src/mwindow.c +++ b/src/mwindow.c @@ -162,7 +162,7 @@ static git_mwindow *new_window( git_mwindow *w; w = git__malloc(sizeof(*w)); - + if (w == NULL) return NULL; diff --git a/src/pack.c b/src/pack.c index 1bffb4778..3a2e7fb5a 100644 --- a/src/pack.c +++ b/src/pack.c @@ -299,29 +299,27 @@ static int pack_index_open(struct git_pack_file *p) int error = 0; size_t name_len, base_len; - if ((error = git_mutex_lock(&p->lock)) < 0) - return error; - if (p->index_map.data) - goto done; + return 0; name_len = strlen(p->pack_name); assert(name_len > strlen(".pack")); /* checked by git_pack_file alloc */ - if ((idx_name = git__malloc(name_len)) == NULL) { - error = -1; - goto done; - } + if ((idx_name = git__malloc(name_len)) == NULL) + return -1; base_len = name_len - strlen(".pack"); memcpy(idx_name, p->pack_name, base_len); memcpy(idx_name + base_len, ".idx", sizeof(".idx")); - error = pack_index_check(idx_name, p); + if ((error = git_mutex_lock(&p->lock)) < 0) + return error; + + if (!p->index_map.data) + error = pack_index_check(idx_name, p); git__free(idx_name); -done: git_mutex_unlock(&p->lock); return error; @@ -820,7 +818,6 @@ void git_packfile_free(struct git_pack_file *p) cache_free(&p->bases); git_mwindow_free_all(&p->mwf); - git_mwindow_file_deregister(&p->mwf); if (p->mwf.fd != -1) p_close(p->mwf.fd); diff --git a/src/pack.h b/src/pack.h index b734ac163..b8014b1ea 100644 --- a/src/pack.h +++ b/src/pack.h @@ -79,7 +79,7 @@ typedef struct { struct git_pack_file { git_mwindow_file mwf; git_map index_map; - git_mutex lock; + git_mutex lock; /* protect updates to mwf and index_map */ uint32_t num_objects; uint32_t num_bad_objects; diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 443871005..742ac6260 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -11,7 +11,6 @@ #include "fileops.h" #include "pack.h" #include "reflog.h" -#include "config.h" #include "refdb.h" #include "refdb_fs.h" diff --git a/src/repository.c b/src/repository.c index cda75b85f..8744b91e6 100644 --- a/src/repository.c +++ b/src/repository.c @@ -32,41 +32,43 @@ #define GIT_TEMPLATE_DIR "/usr/share/git-core/templates" +#define repo_swap_ptr(repo,item_ptr,new_value) \ + git__swap(&repo->lock, (void **)item_ptr, new_value) + static void drop_odb(git_repository *repo) { - if (repo->_odb != NULL) { - GIT_REFCOUNT_OWN(repo->_odb, NULL); - git_odb_free(repo->_odb); - repo->_odb = NULL; + git_odb *dropme = repo_swap_ptr(repo, &repo->_odb, NULL); + if (dropme != NULL) { + GIT_REFCOUNT_OWN(dropme, NULL); + git_odb_free(dropme); } } static void drop_refdb(git_repository *repo) { - if (repo->_refdb != NULL) { - GIT_REFCOUNT_OWN(repo->_refdb, NULL); - git_refdb_free(repo->_refdb); - repo->_refdb = NULL; + git_refdb *dropme = repo_swap_ptr(repo, &repo->_refdb, NULL); + if (dropme != NULL) { + GIT_REFCOUNT_OWN(dropme, NULL); + git_refdb_free(dropme); } } static void drop_config(git_repository *repo) { - if (repo->_config != NULL) { - GIT_REFCOUNT_OWN(repo->_config, NULL); - git_config_free(repo->_config); - repo->_config = NULL; + git_config *dropme = repo_swap_ptr(repo, &repo->_config, NULL); + if (dropme != NULL) { + GIT_REFCOUNT_OWN(dropme, NULL); + git_config_free(dropme); + git_repository__cvar_cache_clear(repo); } - - git_repository__cvar_cache_clear(repo); } static void drop_index(git_repository *repo) { - if (repo->_index != NULL) { - GIT_REFCOUNT_OWN(repo->_index, NULL); - git_index_free(repo->_index); - repo->_index = NULL; + git_index *dropme = repo_swap_ptr(repo, &repo->_index, NULL); + if (dropme != NULL) { + GIT_REFCOUNT_OWN(dropme, NULL); + git_index_free(dropme); } } @@ -79,14 +81,15 @@ void git_repository_free(git_repository *repo) git_attr_cache_flush(repo); git_submodule_config_free(repo); - git__free(repo->path_repository); - git__free(repo->workdir); - drop_config(repo); drop_index(repo); drop_odb(repo); drop_refdb(repo); + git__free(repo->path_repository); + git__free(repo->workdir); + + git_mutex_free(&repo->lock); git__free(repo); } @@ -119,6 +122,8 @@ static git_repository *repository_alloc(void) memset(repo, 0x0, sizeof(git_repository)); + git_mutex_init(&repo->lock); + if (git_cache_init(&repo->objects) < 0) { git__free(repo); return NULL; @@ -549,39 +554,47 @@ on_error: return error; } +static const char *path_unless_empty(git_buf *buf) +{ + return git_buf_len(buf) > 0 ? git_buf_cstr(buf) : NULL; +} + int git_repository_config__weakptr(git_config **out, git_repository *repo) { + int error = 0; + if (repo->_config == NULL) { - git_buf global_buf = GIT_BUF_INIT, xdg_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT; - int res; + git_buf global_buf = GIT_BUF_INIT; + git_buf xdg_buf = GIT_BUF_INIT; + git_buf system_buf = GIT_BUF_INIT; + git_config *config; - const char *global_config_path = NULL; - const char *xdg_config_path = NULL; - const char *system_config_path = NULL; + git_config_find_global_r(&global_buf); + git_config_find_xdg_r(&xdg_buf); + git_config_find_system_r(&system_buf); - if (git_config_find_global_r(&global_buf) == 0) - global_config_path = global_buf.ptr; + error = load_config( + &config, repo, + path_unless_empty(&global_buf), + path_unless_empty(&xdg_buf), + path_unless_empty(&system_buf)); + if (!error) { + GIT_REFCOUNT_OWN(config, repo); - if (git_config_find_xdg_r(&xdg_buf) == 0) - xdg_config_path = xdg_buf.ptr; - - if (git_config_find_system_r(&system_buf) == 0) - system_config_path = system_buf.ptr; - - res = load_config(&repo->_config, repo, global_config_path, xdg_config_path, system_config_path); + config = repo_swap_ptr(repo, &repo->_config, config); + if (config != NULL) { + GIT_REFCOUNT_OWN(config, NULL); + git_config_free(config); + } + } git_buf_free(&global_buf); git_buf_free(&xdg_buf); git_buf_free(&system_buf); - - if (res < 0) - return -1; - - GIT_REFCOUNT_OWN(repo->_config, repo); } *out = repo->_config; - return 0; + return error; } int git_repository_config(git_config **out, git_repository *repo) @@ -597,35 +610,46 @@ void git_repository_set_config(git_repository *repo, git_config *config) { assert(repo && config); - drop_config(repo); + GIT_REFCOUNT_OWN(config, repo); + GIT_REFCOUNT_INC(config); - repo->_config = config; - GIT_REFCOUNT_OWN(repo->_config, repo); - GIT_REFCOUNT_INC(repo->_config); + config = repo_swap_ptr(repo, &repo->_config, config); + if (config != NULL) { + GIT_REFCOUNT_OWN(config, NULL); + git_config_free(config); + } + + git_repository__cvar_cache_clear(repo); } int git_repository_odb__weakptr(git_odb **out, git_repository *repo) { + int error = 0; + assert(repo && out); if (repo->_odb == NULL) { git_buf odb_path = GIT_BUF_INIT; - int res; + git_odb *odb; - if (git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR) < 0) - return -1; + git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR); - res = git_odb_open(&repo->_odb, odb_path.ptr); - git_buf_free(&odb_path); /* done with path */ + error = git_odb_open(&odb, odb_path.ptr); + if (!error) { + GIT_REFCOUNT_OWN(odb, repo); - if (res < 0) - return -1; + odb = repo_swap_ptr(repo, &repo->_odb, odb); + if (odb != NULL) { + GIT_REFCOUNT_OWN(odb, NULL); + git_odb_free(odb); + } + } - GIT_REFCOUNT_OWN(repo->_odb, repo); + git_buf_free(&odb_path); } *out = repo->_odb; - return 0; + return error; } int git_repository_odb(git_odb **out, git_repository *repo) @@ -641,30 +665,39 @@ void git_repository_set_odb(git_repository *repo, git_odb *odb) { assert(repo && odb); - drop_odb(repo); - - repo->_odb = odb; - GIT_REFCOUNT_OWN(repo->_odb, repo); + GIT_REFCOUNT_OWN(odb, repo); GIT_REFCOUNT_INC(odb); + + odb = repo_swap_ptr(repo, &repo->_odb, odb); + if (odb != NULL) { + GIT_REFCOUNT_OWN(odb, NULL); + git_odb_free(odb); + } } int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo) { + int error = 0; + assert(out && repo); if (repo->_refdb == NULL) { - int res; + git_refdb *refdb; - res = git_refdb_open(&repo->_refdb, repo); + error = git_refdb_open(&refdb, repo); + if (!error) { + GIT_REFCOUNT_OWN(refdb, repo); - if (res < 0) - return -1; - - GIT_REFCOUNT_OWN(repo->_refdb, repo); + refdb = repo_swap_ptr(repo, &repo->_refdb, refdb); + if (refdb != NULL) { + GIT_REFCOUNT_OWN(refdb, NULL); + git_refdb_free(refdb); + } + } } *out = repo->_refdb; - return 0; + return error; } int git_repository_refdb(git_refdb **out, git_repository *repo) @@ -678,40 +711,48 @@ int git_repository_refdb(git_refdb **out, git_repository *repo) void git_repository_set_refdb(git_repository *repo, git_refdb *refdb) { - assert (repo && refdb); + assert(repo && refdb); - drop_refdb(repo); - - repo->_refdb = refdb; - GIT_REFCOUNT_OWN(repo->_refdb, repo); + GIT_REFCOUNT_OWN(refdb, repo); GIT_REFCOUNT_INC(refdb); + + refdb = repo_swap_ptr(repo, &repo->_refdb, refdb); + if (refdb != NULL) { + GIT_REFCOUNT_OWN(refdb, NULL); + git_refdb_free(refdb); + } } int git_repository_index__weakptr(git_index **out, git_repository *repo) { + int error = 0; + assert(out && repo); if (repo->_index == NULL) { - int res; git_buf index_path = GIT_BUF_INIT; + git_index *index; - if (git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE) < 0) - return -1; + git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE); - res = git_index_open(&repo->_index, index_path.ptr); - git_buf_free(&index_path); /* done with path */ + error = git_index_open(&index, index_path.ptr); + if (!error) { + GIT_REFCOUNT_OWN(index, repo); - if (res < 0) - return -1; + index = repo_swap_ptr(repo, &repo->_index, index); + if (index != NULL) { + GIT_REFCOUNT_OWN(index, NULL); + git_index_free(index); + } - GIT_REFCOUNT_OWN(repo->_index, repo); + error = git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER); + } - if (git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER) < 0) - return -1; + git_buf_free(&index_path); } *out = repo->_index; - return 0; + return error; } int git_repository_index(git_index **out, git_repository *repo) @@ -727,11 +768,14 @@ void git_repository_set_index(git_repository *repo, git_index *index) { assert(repo && index); - drop_index(repo); - - repo->_index = index; - GIT_REFCOUNT_OWN(repo->_index, repo); + GIT_REFCOUNT_OWN(index, repo); GIT_REFCOUNT_INC(index); + + index = repo_swap_ptr(repo, &repo->_index, index); + if (index != NULL) { + GIT_REFCOUNT_OWN(index, NULL); + git_index_free(index); + } } static int check_repositoryformatversion(git_config *config) diff --git a/src/repository.h b/src/repository.h index cc2f8c2b8..873498de0 100644 --- a/src/repository.h +++ b/src/repository.h @@ -83,6 +83,7 @@ struct git_repository { git_refdb *_refdb; git_config *_config; git_index *_index; + git_mutex lock; git_cache objects; git_attr_cache attrcache; diff --git a/src/util.h b/src/util.h index af3ef0b46..a2233a7e8 100644 --- a/src/util.h +++ b/src/util.h @@ -306,11 +306,30 @@ int git__date_parse(git_time_t *out, const char *date); /* * Unescapes a string in-place. - * + * * Edge cases behavior: * - "jackie\" -> "jacky\" * - "chan\\" -> "chan\" */ extern size_t git__unescape(char *str); +/* + * Swap a pointer with thread safety, returning old value. + */ +GIT_INLINE(void *) git__swap(git_mutex *lock, void **ptr_ptr, void *new_ptr) +{ + void *old_ptr; + + if (*ptr_ptr == new_ptr) + return NULL; + if (git_mutex_lock(lock) < 0) + return new_ptr; + + old_ptr = *ptr_ptr; + *ptr_ptr = new_ptr; + + git_mutex_unlock(lock); + return old_ptr; +} + #endif /* INCLUDE_util_h__ */ From e976b56dda6ae3d7d81bd114b61750e97cc918d3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 15 Apr 2013 14:27:53 -0700 Subject: [PATCH 059/384] Add git__compare_and_swap and use it This removes the lock from the repository object and changes the internals to use the new atomic git__compare_and_swap to update the _odb, _config, _index, and _refdb variables in a threadsafe manner. --- src/global.h | 8 --- src/repository.c | 132 +++++++++++++++++++-------------------------- src/repository.h | 1 - src/thread-utils.h | 41 ++++++++++++++ src/util.h | 19 ------- 5 files changed, 95 insertions(+), 106 deletions(-) diff --git a/src/global.h b/src/global.h index f0ad1df29..badbc0883 100644 --- a/src/global.h +++ b/src/global.h @@ -10,14 +10,6 @@ #include "mwindow.h" #include "hash.h" -#if defined(GIT_THREADS) && defined(_MSC_VER) -# define GIT_MEMORY_BARRIER MemoryBarrier() -#elif defined(GIT_THREADS) -# define GIT_MEMORY_BARRIER __sync_synchronize() -#else -# define GIT_MEMORY_BARRIER /* noop */ -#endif - typedef struct { git_error *last_error; git_error error_t; diff --git a/src/repository.c b/src/repository.c index 8744b91e6..59479dc92 100644 --- a/src/repository.c +++ b/src/repository.c @@ -32,43 +32,57 @@ #define GIT_TEMPLATE_DIR "/usr/share/git-core/templates" -#define repo_swap_ptr(repo,item_ptr,new_value) \ - git__swap(&repo->lock, (void **)item_ptr, new_value) - -static void drop_odb(git_repository *repo) +static void set_odb(git_repository *repo, git_odb *odb) { - git_odb *dropme = repo_swap_ptr(repo, &repo->_odb, NULL); - if (dropme != NULL) { - GIT_REFCOUNT_OWN(dropme, NULL); - git_odb_free(dropme); + if (odb) { + GIT_REFCOUNT_OWN(odb, repo); + GIT_REFCOUNT_INC(odb); + } + + if ((odb = git__swap(repo->_odb, odb)) != NULL) { + GIT_REFCOUNT_OWN(odb, NULL); + git_odb_free(odb); } } -static void drop_refdb(git_repository *repo) +static void set_refdb(git_repository *repo, git_refdb *refdb) { - git_refdb *dropme = repo_swap_ptr(repo, &repo->_refdb, NULL); - if (dropme != NULL) { - GIT_REFCOUNT_OWN(dropme, NULL); - git_refdb_free(dropme); + if (refdb) { + GIT_REFCOUNT_OWN(refdb, repo); + GIT_REFCOUNT_INC(refdb); + } + + if ((refdb = git__swap(repo->_refdb, refdb)) != NULL) { + GIT_REFCOUNT_OWN(refdb, NULL); + git_refdb_free(refdb); } } -static void drop_config(git_repository *repo) +static void set_config(git_repository *repo, git_config *config) { - git_config *dropme = repo_swap_ptr(repo, &repo->_config, NULL); - if (dropme != NULL) { - GIT_REFCOUNT_OWN(dropme, NULL); - git_config_free(dropme); - git_repository__cvar_cache_clear(repo); + if (config) { + GIT_REFCOUNT_OWN(config, repo); + GIT_REFCOUNT_INC(config); } + + if ((config = git__swap(repo->_config, config)) != NULL) { + GIT_REFCOUNT_OWN(config, NULL); + git_config_free(config); + } + + git_repository__cvar_cache_clear(repo); } -static void drop_index(git_repository *repo) +static void set_index(git_repository *repo, git_index *index) { - git_index *dropme = repo_swap_ptr(repo, &repo->_index, NULL); - if (dropme != NULL) { - GIT_REFCOUNT_OWN(dropme, NULL); - git_index_free(dropme); + if (index) { + GIT_REFCOUNT_OWN(index, repo); + GIT_REFCOUNT_INC(index); + } + + if ((index = git__swap(repo->_index, index)) != NULL) { + GIT_REFCOUNT_OWN(index, NULL); + git_index_free(index); } } @@ -81,15 +95,14 @@ void git_repository_free(git_repository *repo) git_attr_cache_flush(repo); git_submodule_config_free(repo); - drop_config(repo); - drop_index(repo); - drop_odb(repo); - drop_refdb(repo); + set_config(repo, NULL); + set_index(repo, NULL); + set_odb(repo, NULL); + set_refdb(repo, NULL); git__free(repo->path_repository); git__free(repo->workdir); - git_mutex_free(&repo->lock); git__free(repo); } @@ -122,8 +135,6 @@ static git_repository *repository_alloc(void) memset(repo, 0x0, sizeof(git_repository)); - git_mutex_init(&repo->lock); - if (git_cache_init(&repo->objects) < 0) { git__free(repo); return NULL; @@ -581,7 +592,7 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) if (!error) { GIT_REFCOUNT_OWN(config, repo); - config = repo_swap_ptr(repo, &repo->_config, config); + config = git__compare_and_swap(&repo->_config, NULL, config); if (config != NULL) { GIT_REFCOUNT_OWN(config, NULL); git_config_free(config); @@ -609,17 +620,7 @@ int git_repository_config(git_config **out, git_repository *repo) void git_repository_set_config(git_repository *repo, git_config *config) { assert(repo && config); - - GIT_REFCOUNT_OWN(config, repo); - GIT_REFCOUNT_INC(config); - - config = repo_swap_ptr(repo, &repo->_config, config); - if (config != NULL) { - GIT_REFCOUNT_OWN(config, NULL); - git_config_free(config); - } - - git_repository__cvar_cache_clear(repo); + set_config(repo, config); } int git_repository_odb__weakptr(git_odb **out, git_repository *repo) @@ -638,7 +639,7 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo) if (!error) { GIT_REFCOUNT_OWN(odb, repo); - odb = repo_swap_ptr(repo, &repo->_odb, odb); + odb = git__compare_and_swap(&repo->_odb, NULL, odb); if (odb != NULL) { GIT_REFCOUNT_OWN(odb, NULL); git_odb_free(odb); @@ -664,15 +665,7 @@ int git_repository_odb(git_odb **out, git_repository *repo) void git_repository_set_odb(git_repository *repo, git_odb *odb) { assert(repo && odb); - - GIT_REFCOUNT_OWN(odb, repo); - GIT_REFCOUNT_INC(odb); - - odb = repo_swap_ptr(repo, &repo->_odb, odb); - if (odb != NULL) { - GIT_REFCOUNT_OWN(odb, NULL); - git_odb_free(odb); - } + set_odb(repo, odb); } int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo) @@ -688,7 +681,7 @@ int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo) if (!error) { GIT_REFCOUNT_OWN(refdb, repo); - refdb = repo_swap_ptr(repo, &repo->_refdb, refdb); + refdb = git__compare_and_swap(&repo->_refdb, NULL, refdb); if (refdb != NULL) { GIT_REFCOUNT_OWN(refdb, NULL); git_refdb_free(refdb); @@ -712,15 +705,7 @@ int git_repository_refdb(git_refdb **out, git_repository *repo) void git_repository_set_refdb(git_repository *repo, git_refdb *refdb) { assert(repo && refdb); - - GIT_REFCOUNT_OWN(refdb, repo); - GIT_REFCOUNT_INC(refdb); - - refdb = repo_swap_ptr(repo, &repo->_refdb, refdb); - if (refdb != NULL) { - GIT_REFCOUNT_OWN(refdb, NULL); - git_refdb_free(refdb); - } + set_refdb(repo, refdb); } int git_repository_index__weakptr(git_index **out, git_repository *repo) @@ -739,7 +724,7 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo) if (!error) { GIT_REFCOUNT_OWN(index, repo); - index = repo_swap_ptr(repo, &repo->_index, index); + index = git__compare_and_swap(&repo->_index, NULL, index); if (index != NULL) { GIT_REFCOUNT_OWN(index, NULL); git_index_free(index); @@ -767,15 +752,7 @@ int git_repository_index(git_index **out, git_repository *repo) void git_repository_set_index(git_repository *repo, git_index *index) { assert(repo && index); - - GIT_REFCOUNT_OWN(index, repo); - GIT_REFCOUNT_INC(index); - - index = repo_swap_ptr(repo, &repo->_index, index); - if (index != NULL) { - GIT_REFCOUNT_OWN(index, NULL); - git_index_free(index); - } + set_index(repo, index); } static int check_repositoryformatversion(git_config *config) @@ -1465,14 +1442,13 @@ static int at_least_one_cb(const char *refname, void *payload) static int repo_contains_no_reference(git_repository *repo) { - int error; - - error = git_reference_foreach(repo, GIT_REF_LISTALL, at_least_one_cb, NULL); + int error = git_reference_foreach(repo, GIT_REF_LISTALL, at_least_one_cb, NULL); if (error == GIT_EUSER) return 0; - - return error == 0 ? 1 : error; + if (!error) + return 1; + return error; } int git_repository_is_empty(git_repository *repo) diff --git a/src/repository.h b/src/repository.h index 873498de0..cc2f8c2b8 100644 --- a/src/repository.h +++ b/src/repository.h @@ -83,7 +83,6 @@ struct git_repository { git_refdb *_refdb; git_config *_config; git_index *_index; - git_mutex lock; git_cache objects; git_attr_cache attrcache; diff --git a/src/thread-utils.h b/src/thread-utils.h index 2ca290adf..7b663182d 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -68,6 +68,21 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a) #endif } +GIT_INLINE(void *) git___compare_and_swap( + volatile void **ptr, void *oldval, void *newval) +{ + bool swapped; +#if defined(GIT_WIN32) + swapped = ((LONGLONG)oldval == InterlockedCompareExchange64( + (LONGLONG volatile *)ptr, (LONGLONG)newval, (LONGLONG)oldval)); +#elif defined(__GNUC__) + swapped = (__sync_val_compare_and_swap(ptr, oldval, newval) == oldval); +#else +# error "Unsupported architecture for atomic operations" +#endif + return swapped ? oldval : newval; +} + #else #define git_thread unsigned int @@ -101,8 +116,34 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a) return --a->val; } +GIT_INLINE(void *) git___compare_and_swap( + volatile void **ptr, void *oldval, void *newval) +{ + if (*ptr == oldval) + *ptr = newval; + else + oldval = newval; + return oldval; +} + #endif +/* Atomically replace oldval with newval + * @return oldval if it was replaced or newval if it was not + */ +#define git__compare_and_swap(P,O,N) \ + git___compare_and_swap((volatile void **)P, O, N) + +#define git__swap(ptr, val) git__compare_and_swap(&ptr, ptr, val) + extern int git_online_cpus(void); +#if defined(GIT_THREADS) && defined(GIT_WIN32) +# define GIT_MEMORY_BARRIER MemoryBarrier() +#elif defined(GIT_THREADS) +# define GIT_MEMORY_BARRIER __sync_synchronize() +#else +# define GIT_MEMORY_BARRIER /* noop */ +#endif + #endif /* INCLUDE_thread_utils_h__ */ diff --git a/src/util.h b/src/util.h index a2233a7e8..82435aee8 100644 --- a/src/util.h +++ b/src/util.h @@ -313,23 +313,4 @@ int git__date_parse(git_time_t *out, const char *date); */ extern size_t git__unescape(char *str); -/* - * Swap a pointer with thread safety, returning old value. - */ -GIT_INLINE(void *) git__swap(git_mutex *lock, void **ptr_ptr, void *new_ptr) -{ - void *old_ptr; - - if (*ptr_ptr == new_ptr) - return NULL; - if (git_mutex_lock(lock) < 0) - return new_ptr; - - old_ptr = *ptr_ptr; - *ptr_ptr = new_ptr; - - git_mutex_unlock(lock); - return old_ptr; -} - #endif /* INCLUDE_util_h__ */ From c628918625c7f779d2050a56998fb2b675f097fb Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 15 Apr 2013 16:31:04 -0700 Subject: [PATCH 060/384] Fixes for Windows cas/threading stuff --- src/thread-utils.h | 9 ++++----- src/win32/pthread.c | 17 +++++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/thread-utils.h b/src/thread-utils.h index 7b663182d..e53a60c05 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -71,16 +71,15 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a) GIT_INLINE(void *) git___compare_and_swap( volatile void **ptr, void *oldval, void *newval) { - bool swapped; + void *foundval; #if defined(GIT_WIN32) - swapped = ((LONGLONG)oldval == InterlockedCompareExchange64( - (LONGLONG volatile *)ptr, (LONGLONG)newval, (LONGLONG)oldval)); + foundval = InterlockedCompareExchangePointer(ptr, newval, oldval); #elif defined(__GNUC__) - swapped = (__sync_val_compare_and_swap(ptr, oldval, newval) == oldval); + foundval = __sync_val_compare_and_swap(ptr, oldval, newval); #else # error "Unsupported architecture for atomic operations" #endif - return swapped ? oldval : newval; + return (foundval == oldval) ? oldval : newval; } #else diff --git a/src/win32/pthread.c b/src/win32/pthread.c index 105f4b89e..c78213f15 100644 --- a/src/win32/pthread.c +++ b/src/win32/pthread.c @@ -14,18 +14,23 @@ int pthread_create( void *GIT_RESTRICT arg) { GIT_UNUSED(attr); - *thread = (pthread_t) CreateThread( + *thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL); return *thread ? 0 : -1; } int pthread_join(pthread_t thread, void **value_ptr) { - int ret; - ret = WaitForSingleObject(thread, INFINITE); - if (ret && value_ptr) - GetExitCodeThread(thread, (void*) value_ptr); - return -(!!ret); + DWORD ret = WaitForSingleObject(thread, INFINITE); + + if (ret == WAIT_OBJECT_0) { + if (value_ptr != NULL) + GetExitCodeThread(thread, (void *)value_ptr); + CloseHandle(thread); + return 0; + } + + return -1; } int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT mutex, From 38eef6113d8523abfe6746bf727cee2651398ad3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 16 Apr 2013 14:19:27 -0700 Subject: [PATCH 061/384] Make indexer use shared packfile open code The indexer was creating a packfile object separately from the code in pack.c which was a problem since I put a call to git_mutex_init into just pack.c. This commit updates the pack function for creating a new pack object (i.e. git_packfile_check()) so that it can be used in both places and then makes indexer.c use the shared initialization routine. There are also a few minor formatting and warning message fixes. --- src/indexer.c | 30 ++++++------------------------ src/pack.c | 39 +++++++++++++++++---------------------- src/thread-utils.h | 2 +- src/win32/pthread.c | 5 +++-- src/win32/pthread.h | 11 +++++++---- tests-clar/object/cache.c | 6 +++--- 6 files changed, 37 insertions(+), 56 deletions(-) diff --git a/src/indexer.c b/src/indexer.c index 2cfbd3a5a..50a9d3a37 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -60,36 +60,19 @@ const git_oid *git_indexer_stream_hash(const git_indexer_stream *idx) static int open_pack(struct git_pack_file **out, const char *filename) { - size_t namelen; struct git_pack_file *pack; - struct stat st; - int fd; - namelen = strlen(filename); - pack = git__calloc(1, sizeof(struct git_pack_file) + namelen + 1); - GITERR_CHECK_ALLOC(pack); + if (git_packfile_check(&pack, filename) < 0) + return -1; - memcpy(pack->pack_name, filename, namelen + 1); - - if (p_stat(filename, &st) < 0) { - giterr_set(GITERR_OS, "Failed to stat packfile."); - goto cleanup; - } - - if ((fd = p_open(pack->pack_name, O_RDONLY)) < 0) { + if ((pack->mwf.fd = p_open(pack->pack_name, O_RDONLY)) < 0) { giterr_set(GITERR_OS, "Failed to open packfile."); - goto cleanup; + git_packfile_free(pack); + return -1; } - pack->mwf.fd = fd; - pack->mwf.size = (git_off_t)st.st_size; - *out = pack; return 0; - -cleanup: - git__free(pack); - return -1; } static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack) @@ -391,7 +374,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz { int error = -1; struct git_pack_header hdr; - size_t processed; + size_t processed; git_mwindow_file *mwf = &idx->pack->mwf; assert(idx && data && stats); @@ -404,7 +387,6 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz /* Make sure we set the new size of the pack */ if (idx->opened_pack) { idx->pack->mwf.size += size; - //printf("\nadding %zu for %zu\n", size, idx->pack->mwf.size); } else { if (open_pack(&idx->pack, idx->pack_file.path_lock) < 0) return -1; diff --git a/src/pack.c b/src/pack.c index 3a2e7fb5a..8e8a01aa9 100644 --- a/src/pack.c +++ b/src/pack.c @@ -794,19 +794,6 @@ git_off_t get_delta_base( * ***********************************************************/ -static struct git_pack_file *packfile_alloc(size_t extra) -{ - struct git_pack_file *p = git__calloc(1, sizeof(*p) + extra); - if (!p) - return NULL; - - p->mwf.fd = -1; - git_mutex_init(&p->lock); - - return p; -} - - void git_packfile_free(struct git_pack_file *p) { if (!p) @@ -902,28 +889,33 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path) { struct stat st; struct git_pack_file *p; - size_t path_len; + size_t path_len = path ? strlen(path) : 0; *pack_out = NULL; - if (!path || (path_len = strlen(path)) <= strlen(".idx")) + if (path_len < strlen(".idx")) return git_odb__error_notfound("invalid packfile path", NULL); - p = packfile_alloc(path_len + 2); + p = git__calloc(1, sizeof(*p) + path_len + 2); GITERR_CHECK_ALLOC(p); + memcpy(p->pack_name, path, path_len + 1); + /* * Make sure a corresponding .pack file exists and that * the index looks sane. */ - path_len -= strlen(".idx"); - memcpy(p->pack_name, path, path_len); + if (git__suffixcmp(path, ".idx") == 0) { + size_t root_len = path_len - strlen(".idx"); - memcpy(p->pack_name + path_len, ".keep", sizeof(".keep")); - if (git_path_exists(p->pack_name) == true) - p->pack_keep = 1; + memcpy(p->pack_name + root_len, ".keep", sizeof(".keep")); + if (git_path_exists(p->pack_name) == true) + p->pack_keep = 1; + + memcpy(p->pack_name + root_len, ".pack", sizeof(".pack")); + path_len = path_len - strlen(".idx") + strlen(".pack"); + } - memcpy(p->pack_name + path_len, ".pack", sizeof(".pack")); if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) { git__free(p); return git_odb__error_notfound("packfile not found", NULL); @@ -932,10 +924,13 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path) /* ok, it looks sane as far as we can check without * actually mapping the pack file. */ + p->mwf.fd = -1; p->mwf.size = st.st_size; p->pack_local = 1; p->mtime = (git_time_t)st.st_mtime; + git_mutex_init(&p->lock); + /* see if we can parse the sha1 oid in the packfile name */ if (path_len < 40 || git_oid_fromstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < 0) diff --git a/src/thread-utils.h b/src/thread-utils.h index e53a60c05..dafe70ad6 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -71,7 +71,7 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a) GIT_INLINE(void *) git___compare_and_swap( volatile void **ptr, void *oldval, void *newval) { - void *foundval; + volatile void *foundval; #if defined(GIT_WIN32) foundval = InterlockedCompareExchangePointer(ptr, newval, oldval); #elif defined(__GNUC__) diff --git a/src/win32/pthread.c b/src/win32/pthread.c index c78213f15..232709e54 100644 --- a/src/win32/pthread.c +++ b/src/win32/pthread.c @@ -33,8 +33,9 @@ int pthread_join(pthread_t thread, void **value_ptr) return -1; } -int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT mutex, - const pthread_mutexattr_t *GIT_RESTRICT mutexattr) +int pthread_mutex_init( + pthread_mutex_t *GIT_RESTRICT mutex, + const pthread_mutexattr_t *GIT_RESTRICT mutexattr) { GIT_UNUSED(mutexattr); InitializeCriticalSection(mutex); diff --git a/src/win32/pthread.h b/src/win32/pthread.h index a219a0137..8277ecf6e 100644 --- a/src/win32/pthread.h +++ b/src/win32/pthread.h @@ -25,13 +25,16 @@ typedef HANDLE pthread_cond_t; #define PTHREAD_MUTEX_INITIALIZER {(void*)-1}; -int pthread_create(pthread_t *GIT_RESTRICT, - const pthread_attr_t *GIT_RESTRICT, - void *(*start_routine)(void*), void *__restrict); +int pthread_create( + pthread_t *GIT_RESTRICT, + const pthread_attr_t *GIT_RESTRICT, + void *(*start_routine)(void*), + void *__restrict); int pthread_join(pthread_t, void **); -int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT, const pthread_mutexattr_t *GIT_RESTRICT); +int pthread_mutex_init( + pthread_mutex_t *GIT_RESTRICT, const pthread_mutexattr_t *GIT_RESTRICT); int pthread_mutex_destroy(pthread_mutex_t *); int pthread_mutex_lock(pthread_mutex_t *); int pthread_mutex_unlock(pthread_mutex_t *); diff --git a/tests-clar/object/cache.c b/tests-clar/object/cache.c index 8121247c9..ed13a6fa8 100644 --- a/tests-clar/object/cache.c +++ b/tests-clar/object/cache.c @@ -213,16 +213,16 @@ void test_object_cache__threadmania(void) fn = (th & 1) ? cache_parsed : cache_raw; #ifdef GIT_THREADS - git_thread_create(&t[th], NULL, fn, data); + cl_git_pass(git_thread_create(&t[th], NULL, fn, data)); #else - fn(data); + cl_assert(fn(data) == data); git__free(data); #endif } #ifdef GIT_THREADS for (th = 0; th < THREADCOUNT; ++th) { - git_thread_join(t[th], &data); + cl_git_pass(git_thread_join(t[th], &data)); cl_assert_equal_i(th, ((int *)data)[0]); git__free(data); } From 5d2d21e536b83ca2cbf8c026b3149fdf776c3f58 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 16 Apr 2013 15:00:43 -0700 Subject: [PATCH 062/384] Consolidate packfile allocation further Rename git_packfile_check to git_packfile_alloc since it is now being used more in that capacity. Fix the various places that use it. Consolidate some repeated code in odb_pack.c related to the allocation of a new pack_backend. --- src/indexer.c | 2 +- src/odb_pack.c | 107 +++++++++++++++++++++++-------------------------- src/pack.c | 2 +- src/pack.h | 3 +- 4 files changed, 55 insertions(+), 59 deletions(-) diff --git a/src/indexer.c b/src/indexer.c index 50a9d3a37..606771927 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -62,7 +62,7 @@ static int open_pack(struct git_pack_file **out, const char *filename) { struct git_pack_file *pack; - if (git_packfile_check(&pack, filename) < 0) + if (git_packfile_alloc(&pack, filename) < 0) return -1; if ((pack->mwf.fd = p_open(pack->pack_name, O_RDONLY)) < 0) { diff --git a/src/odb_pack.c b/src/odb_pack.c index 773e14974..eec79259b 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -207,7 +207,7 @@ static int packfile_load__cb(void *_data, git_buf *path) return 0; } - error = git_packfile_check(&pack, path->ptr); + error = git_packfile_alloc(&pack, path->ptr); if (error == GIT_ENOTFOUND) /* ignore missing .pack file as git does */ return 0; @@ -527,67 +527,17 @@ static void pack_backend__free(git_odb_backend *_backend) git__free(backend); } -int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx) +static int pack_backend__alloc(struct pack_backend **out, size_t initial_size) { - struct pack_backend *backend = NULL; - struct git_pack_file *packfile = NULL; - - if (git_packfile_check(&packfile, idx) < 0) - return -1; - - backend = git__calloc(1, sizeof(struct pack_backend)); + struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend)); GITERR_CHECK_ALLOC(backend); - backend->parent.version = GIT_ODB_BACKEND_VERSION; - if (git_vector_init(&backend->packs, 1, NULL) < 0) - goto on_error; - - if (git_vector_insert(&backend->packs, packfile) < 0) - goto on_error; - - backend->parent.read = &pack_backend__read; - backend->parent.read_prefix = &pack_backend__read_prefix; - backend->parent.read_header = &pack_backend__read_header; - backend->parent.exists = &pack_backend__exists; - backend->parent.refresh = &pack_backend__refresh; - backend->parent.foreach = &pack_backend__foreach; - backend->parent.free = &pack_backend__free; - - *backend_out = (git_odb_backend *)backend; - - return 0; - -on_error: - git_vector_free(&backend->packs); - git__free(backend); - git__free(packfile); - return -1; -} - -int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) -{ - struct pack_backend *backend = NULL; - git_buf path = GIT_BUF_INIT; - - backend = git__calloc(1, sizeof(struct pack_backend)); - GITERR_CHECK_ALLOC(backend); - backend->parent.version = GIT_ODB_BACKEND_VERSION; - - if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < 0 || - git_buf_joinpath(&path, objects_dir, "pack") < 0) - { + if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) { git__free(backend); return -1; } - if (git_path_isdir(git_buf_cstr(&path)) == true) { - int error; - - backend->pack_folder = git_buf_detach(&path); - error = pack_backend__refresh((git_odb_backend *)backend); - if (error < 0) - return error; - } + backend->parent.version = GIT_ODB_BACKEND_VERSION; backend->parent.read = &pack_backend__read; backend->parent.read_prefix = &pack_backend__read_prefix; @@ -598,9 +548,54 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) backend->parent.writepack = &pack_backend__writepack; backend->parent.free = &pack_backend__free; + *out = backend; + return 0; +} + +int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx) +{ + struct pack_backend *backend = NULL; + struct git_pack_file *packfile = NULL; + + if (pack_backend__alloc(&backend, 1) < 0) + return -1; + + if (git_packfile_alloc(&packfile, idx) < 0 || + git_vector_insert(&backend->packs, packfile) < 0) + { + pack_backend__free((git_odb_backend *)backend); + return -1; + } + + *backend_out = (git_odb_backend *)backend; + return 0; +} + +int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) +{ + int error = 0; + struct pack_backend *backend = NULL; + git_buf path = GIT_BUF_INIT; + + if (pack_backend__alloc(&backend, 8) < 0) + return -1; + + if (!(error = git_buf_joinpath(&path, objects_dir, "pack")) && + git_path_isdir(git_buf_cstr(&path))) + { + backend->pack_folder = git_buf_detach(&path); + + error = pack_backend__refresh((git_odb_backend *)backend); + } + + if (error < 0) { + pack_backend__free((git_odb_backend *)backend); + backend = NULL; + } + *backend_out = (git_odb_backend *)backend; git_buf_free(&path); - return 0; + return error; } diff --git a/src/pack.c b/src/pack.c index 8e8a01aa9..33cdf760a 100644 --- a/src/pack.c +++ b/src/pack.c @@ -885,7 +885,7 @@ cleanup: return -1; } -int git_packfile_check(struct git_pack_file **pack_out, const char *path) +int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) { struct stat st; struct git_pack_file *p; diff --git a/src/pack.h b/src/pack.h index b8014b1ea..aeeac9ce1 100644 --- a/src/pack.h +++ b/src/pack.h @@ -143,7 +143,8 @@ git_off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs, git_off_t delta_obj_offset); void git_packfile_free(struct git_pack_file *p); -int git_packfile_check(struct git_pack_file **pack_out, const char *path); +int git_packfile_alloc(struct git_pack_file **pack_out, const char *path); + int git_pack_entry_find( struct git_pack_entry *e, struct git_pack_file *p, From 865e2dd4440596d7cd874556a694eea30336c69b Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 17 Apr 2013 23:58:37 +0200 Subject: [PATCH 063/384] tests: Cleanup commit parse testing code --- tests-clar/commit/parse.c | 45 ++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/tests-clar/commit/parse.c b/tests-clar/commit/parse.c index ad2c746ca..8de4401dc 100644 --- a/tests-clar/commit/parse.c +++ b/tests-clar/commit/parse.c @@ -264,37 +264,40 @@ gpgsig -----BEGIN PGP SIGNATURE-----\n\ a simple commit which works\n", }; +static int parse_commit(git_commit **out, const char *buffer) +{ + git_commit *commit; + git_odb_object fake_odb_object; + int error; + + commit = (git_commit*)git__malloc(sizeof(git_commit)); + memset(commit, 0x0, sizeof(git_commit)); + commit->object.repo = g_repo; + + memset(&fake_odb_object, 0x0, sizeof(git_odb_object)); + fake_odb_object.buffer = (char *)buffer; + fake_odb_object.cached.size = strlen(fake_odb_object.buffer); + + error = git_commit__parse(commit, &fake_odb_object); + + *out = commit; + return error; +} + void test_commit_parse__entire_commit(void) { const int failing_commit_count = ARRAY_SIZE(failing_commit_cases); const int passing_commit_count = ARRAY_SIZE(passing_commit_cases); int i; git_commit *commit; - git_odb_object fake_odb_object; - memset(&fake_odb_object, 0, sizeof(fake_odb_object)); for (i = 0; i < failing_commit_count; ++i) { - commit = (git_commit*)git__malloc(sizeof(git_commit)); - memset(commit, 0x0, sizeof(git_commit)); - commit->object.repo = g_repo; - - fake_odb_object.buffer = failing_commit_cases[i]; - fake_odb_object.cached.size = strlen(fake_odb_object.buffer); - - cl_git_fail(git_commit__parse(commit, &fake_odb_object)); - + cl_git_fail(parse_commit(&commit, failing_commit_cases[i])); git_commit__free(commit); } for (i = 0; i < passing_commit_count; ++i) { - commit = (git_commit*)git__malloc(sizeof(git_commit)); - memset(commit, 0x0, sizeof(git_commit)); - commit->object.repo = g_repo; - - fake_odb_object.buffer = passing_commit_cases[i]; - fake_odb_object.cached.size = strlen(fake_odb_object.buffer); - - cl_git_pass(git_commit__parse(commit, &fake_odb_object)); + cl_git_pass(parse_commit(&commit, passing_commit_cases[i])); if (!i) cl_assert_equal_s("", git_commit_message(commit)); @@ -387,9 +390,7 @@ This commit has a few LF at the start of the commit message"; memset(commit, 0x0, sizeof(git_commit)); commit->object.repo = g_repo; - cl_git_pass(git_commit__parse_buffer(commit, buffer, strlen(buffer))); - + cl_git_pass(parse_commit(&commit, buffer)); cl_assert_equal_s(message, git_commit_message(commit)); - git_commit__free(commit); } From cf9709b64eb49d5a0ee1181b80c950e518fb45b0 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 22 Apr 2013 16:53:46 +0200 Subject: [PATCH 064/384] tests: Do not warn for unused variable --- tests-clar/object/cache.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests-clar/object/cache.c b/tests-clar/object/cache.c index ed13a6fa8..b36bf2726 100644 --- a/tests-clar/object/cache.c +++ b/tests-clar/object/cache.c @@ -195,10 +195,13 @@ static void *cache_raw(void *arg) void test_object_cache__threadmania(void) { int try, th, max_i; - git_thread t[THREADCOUNT]; void *data; void *(*fn)(void *); +#ifdef GIT_THREADS + git_thread t[THREADCOUNT]; +#endif + for (max_i = 0; g_data[max_i].sha != NULL; ++max_i) /* count up */; From d87715926049390a2417a2476742114ec966686a Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 22 Apr 2013 17:04:52 +0200 Subject: [PATCH 065/384] cache: Max cache size, and evict when the cache fills up --- include/git2/common.h | 3 ++- src/cache.c | 25 ++++++++++++++++++------- src/cache.h | 1 + src/util.c | 6 +++++- tests-clar/object/cache.c | 6 +++--- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index 80d83d345..ccd252fda 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -131,7 +131,8 @@ enum { GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, GIT_OPT_GET_SEARCH_PATH, GIT_OPT_SET_SEARCH_PATH, - GIT_OPT_SET_CACHE_LIMIT, + GIT_OPT_SET_CACHE_OBJECT_LIMIT, + GIT_OPT_SET_CACHE_MAX_SIZE, GIT_OPT_ENABLE_CACHING }; diff --git a/src/cache.c b/src/cache.c index c51be895e..ca122fb77 100644 --- a/src/cache.c +++ b/src/cache.c @@ -18,6 +18,7 @@ GIT__USE_OIDMAP bool git_cache__enabled = true; +size_t git_cache__max_storage = (4 * 1024 * 1024); static size_t git_cache__max_object_size[8] = { 0, /* GIT_OBJ__EXT1 */ @@ -70,19 +71,25 @@ int git_cache_init(git_cache *cache) return 0; } -void git_cache_clear(git_cache *cache) +/* called with lock */ +static void clear_cache(git_cache *cache) { git_cached_obj *evict = NULL; - if (git_mutex_lock(&cache->lock) < 0) - return; - kh_foreach_value(cache->map, evict, { git_cached_obj_decref(evict); }); kh_clear(oid, cache->map); cache->used_memory = 0; +} + +void git_cache_clear(git_cache *cache) +{ + if (git_mutex_lock(&cache->lock) < 0) + return; + + clear_cache(cache); git_mutex_unlock(&cache->lock); } @@ -95,14 +102,15 @@ void git_cache_free(git_cache *cache) git_mutex_free(&cache->lock); } -/* Call with lock, yo */ -static void cache_evict_entries(git_cache *cache, size_t evict_count) +/* Called with lock */ +static void cache_evict_entries(git_cache *cache) { uint32_t seed = rand(); + size_t evict_count = 8; /* do not infinite loop if there's not enough entries to evict */ if (evict_count > kh_size(cache->map)) { - git_cache_clear(cache); + clear_cache(cache); return; } @@ -163,6 +171,9 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) if (git_mutex_lock(&cache->lock) < 0) return entry; + if (cache->used_memory > git_cache__max_storage) + cache_evict_entries(cache); + pos = kh_get(oid, cache->map, &entry->oid); /* not found */ diff --git a/src/cache.h b/src/cache.h index e95d521fe..1715c7220 100644 --- a/src/cache.h +++ b/src/cache.h @@ -35,6 +35,7 @@ typedef struct { } git_cache; extern bool git_cache__enabled; +extern size_t git_cache__max_storage; int git_cache_set_max_object_size(git_otype type, size_t size); diff --git a/src/util.c b/src/util.c index 1ed5d5d16..c3fc69756 100644 --- a/src/util.c +++ b/src/util.c @@ -95,7 +95,7 @@ int git_libgit2_opts(int key, ...) error = git_futils_dirs_set(error, va_arg(ap, const char *)); break; - case GIT_OPT_SET_CACHE_LIMIT: + case GIT_OPT_SET_CACHE_OBJECT_LIMIT: { git_otype type = (git_otype)va_arg(ap, int); size_t size = va_arg(ap, size_t); @@ -103,6 +103,10 @@ int git_libgit2_opts(int key, ...) break; } + case GIT_OPT_SET_CACHE_MAX_SIZE: + git_cache__max_storage = va_arg(ap, size_t); + break; + case GIT_OPT_ENABLE_CACHING: git_cache__enabled = (va_arg(ap, int) != 0); break; diff --git a/tests-clar/object/cache.c b/tests-clar/object/cache.c index b36bf2726..a3eba8737 100644 --- a/tests-clar/object/cache.c +++ b/tests-clar/object/cache.c @@ -13,7 +13,7 @@ void test_object_cache__cleanup(void) git_repository_free(g_repo); g_repo = NULL; - git_libgit2_opts(GIT_OPT_SET_CACHE_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0); + git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0); } static struct { @@ -54,7 +54,7 @@ void test_object_cache__cache_everything(void) git_odb *odb; git_libgit2_opts( - GIT_OPT_SET_CACHE_LIMIT, (int)GIT_OBJ_BLOB, (size_t)32767); + GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)32767); cl_git_pass(git_repository_odb(&odb, g_repo)); @@ -103,7 +103,7 @@ void test_object_cache__cache_no_blobs(void) git_object *obj; git_odb *odb; - git_libgit2_opts(GIT_OPT_SET_CACHE_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0); + git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0); cl_git_pass(git_repository_odb(&odb, g_repo)); From 05b179648aca91799274afbd03d40f31783547ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 21 Apr 2013 19:26:35 +0200 Subject: [PATCH 066/384] Make refcounting atomic --- src/util.h | 8 ++++---- tests-clar/repo/getters.c | 4 ++-- tests-clar/repo/setters.c | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/util.h b/src/util.h index 82435aee8..687afe084 100644 --- a/src/util.h +++ b/src/util.h @@ -188,20 +188,20 @@ extern int git__strncmp(const char *a, const char *b, size_t sz); extern int git__strncasecmp(const char *a, const char *b, size_t sz); typedef struct { - short refcount; + git_atomic refcount; void *owner; } git_refcount; typedef void (*git_refcount_freeptr)(void *r); #define GIT_REFCOUNT_INC(r) { \ - ((git_refcount *)(r))->refcount++; \ + git_atomic_inc(&((git_refcount *)(r))->refcount); \ } #define GIT_REFCOUNT_DEC(_r, do_free) { \ git_refcount *r = (git_refcount *)(_r); \ - r->refcount--; \ - if (r->refcount <= 0 && r->owner == NULL) { do_free(_r); } \ + int val = git_atomic_dec(&r->refcount); \ + if (val <= 0 && r->owner == NULL) { do_free(_r); } \ } #define GIT_REFCOUNT_OWN(r, o) { \ diff --git a/tests-clar/repo/getters.c b/tests-clar/repo/getters.c index b372f5b70..b8ede126c 100644 --- a/tests-clar/repo/getters.c +++ b/tests-clar/repo/getters.c @@ -31,10 +31,10 @@ void test_repo_getters__retrieving_the_odb_honors_the_refcount(void) cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); cl_git_pass(git_repository_odb(&odb, repo)); - cl_assert(((git_refcount *)odb)->refcount == 2); + cl_assert(((git_refcount *)odb)->refcount.val == 2); git_repository_free(repo); - cl_assert(((git_refcount *)odb)->refcount == 1); + cl_assert(((git_refcount *)odb)->refcount.val == 1); git_odb_free(odb); } diff --git a/tests-clar/repo/setters.c b/tests-clar/repo/setters.c index 063d76c8d..f34f1e471 100644 --- a/tests-clar/repo/setters.c +++ b/tests-clar/repo/setters.c @@ -69,13 +69,13 @@ void test_repo_setters__setting_a_new_index_on_a_repo_which_has_already_loaded_o git_index *new_index; cl_git_pass(git_index_open(&new_index, "./my-index")); - cl_assert(((git_refcount *)new_index)->refcount == 1); + cl_assert(((git_refcount *)new_index)->refcount.val == 1); git_repository_set_index(repo, new_index); - cl_assert(((git_refcount *)new_index)->refcount == 2); + cl_assert(((git_refcount *)new_index)->refcount.val == 2); git_repository_free(repo); - cl_assert(((git_refcount *)new_index)->refcount == 1); + cl_assert(((git_refcount *)new_index)->refcount.val == 1); git_index_free(new_index); @@ -90,13 +90,13 @@ void test_repo_setters__setting_a_new_odb_on_a_repo_which_already_loaded_one_pro git_odb *new_odb; cl_git_pass(git_odb_open(&new_odb, "./testrepo.git/objects")); - cl_assert(((git_refcount *)new_odb)->refcount == 1); + cl_assert(((git_refcount *)new_odb)->refcount.val == 1); git_repository_set_odb(repo, new_odb); - cl_assert(((git_refcount *)new_odb)->refcount == 2); + cl_assert(((git_refcount *)new_odb)->refcount.val == 2); git_repository_free(repo); - cl_assert(((git_refcount *)new_odb)->refcount == 1); + cl_assert(((git_refcount *)new_odb)->refcount.val == 1); git_odb_free(new_odb); From f9774eea3a37e3a2a44d3c7e9dbc895280c3d90a Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 22 Apr 2013 17:22:31 +0200 Subject: [PATCH 067/384] atomic: Add an atomic type for 64-bit operations --- src/thread-utils.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/thread-utils.h b/src/thread-utils.h index dafe70ad6..28ecd297e 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -18,6 +18,14 @@ typedef struct { #endif } git_atomic; +typedef struct { +#if defined(GIT_WIN32) + __int64 val; +#else + int64_t val; +#endif +} git_atomic64; + GIT_INLINE(void) git_atomic_set(git_atomic *a, int val) { a->val = val; @@ -57,6 +65,17 @@ GIT_INLINE(int) git_atomic_inc(git_atomic *a) #endif } +GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend) +{ +#if defined(GIT_WIN32) + return _InterlockedExchangeAdd(&a->val, addend); +#elif defined(__GNUC__) + return __sync_add_and_fetch(&a->val, addend); +#else +# error "Unsupported architecture for atomic operations" +#endif +} + GIT_INLINE(int) git_atomic_dec(git_atomic *a) { #if defined(GIT_WIN32) @@ -82,6 +101,17 @@ GIT_INLINE(void *) git___compare_and_swap( return (foundval == oldval) ? oldval : newval; } +GIT_INLINE(int) git_atomic64_add(git_atomic64 *a, int64_t addend) +{ +#if defined(GIT_WIN32) + return _InterlockedExchangeAdd64(&a->val, addend); +#elif defined(__GNUC__) + return __sync_add_and_fetch(&a->val, addend); +#else +# error "Unsupported architecture for atomic operations" +#endif +} + #else #define git_thread unsigned int @@ -110,6 +140,12 @@ GIT_INLINE(int) git_atomic_inc(git_atomic *a) return ++a->val; } +GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend) +{ + a->val += addend; + return a->val; +} + GIT_INLINE(int) git_atomic_dec(git_atomic *a) { return --a->val; @@ -125,6 +161,12 @@ GIT_INLINE(void *) git___compare_and_swap( return oldval; } +GIT_INLINE(int) git_atomic64_add(git_atomic64 *a, int64_t addend) +{ + a->val += addend; + return a->val; +} + #endif /* Atomically replace oldval with newval From a14163a79d644f0fd2856b083f355f2df19f6bdd Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 22 Apr 2013 17:30:49 +0200 Subject: [PATCH 068/384] cache: Shared meter for memory usage --- src/cache.c | 16 ++++++++++++---- src/cache.h | 4 ++-- src/util.c | 3 +-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/cache.c b/src/cache.c index ca122fb77..4b26d4351 100644 --- a/src/cache.c +++ b/src/cache.c @@ -18,7 +18,9 @@ GIT__USE_OIDMAP bool git_cache__enabled = true; -size_t git_cache__max_storage = (4 * 1024 * 1024); +int64_t git_cache__max_storage = (4 * 1024 * 1024); + +static git_atomic64 git_cache__current_storage = {0}; static size_t git_cache__max_object_size[8] = { 0, /* GIT_OBJ__EXT1 */ @@ -81,6 +83,7 @@ static void clear_cache(git_cache *cache) }); kh_clear(oid, cache->map); + git_atomic64_add(&git_cache__current_storage, -cache->used_memory); cache->used_memory = 0; } @@ -106,7 +109,7 @@ void git_cache_free(git_cache *cache) static void cache_evict_entries(git_cache *cache) { uint32_t seed = rand(); - size_t evict_count = 8; + int64_t evicted_memory = 0, evict_count = 8; /* do not infinite loop if there's not enough entries to evict */ if (evict_count > kh_size(cache->map)) { @@ -121,12 +124,15 @@ static void cache_evict_entries(git_cache *cache) git_cached_obj *evict = kh_val(cache->map, pos); evict_count--; - cache->used_memory -= evict->size; + evicted_memory += evict->size; git_cached_obj_decref(evict); kh_del(oid, cache->map, pos); } } + + cache->used_memory -= evicted_memory; + git_atomic64_add(&git_cache__current_storage, -evicted_memory); } static bool cache_should_store(git_otype object_type, size_t object_size) @@ -171,7 +177,8 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) if (git_mutex_lock(&cache->lock) < 0) return entry; - if (cache->used_memory > git_cache__max_storage) + /* soften the load on the cache */ + if (git_cache__current_storage.val > git_cache__max_storage) cache_evict_entries(cache); pos = kh_get(oid, cache->map, &entry->oid); @@ -186,6 +193,7 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) kh_val(cache->map, pos) = entry; git_cached_obj_incref(entry); cache->used_memory += entry->size; + git_atomic64_add(&git_cache__current_storage, (int64_t)entry->size); } } /* found */ diff --git a/src/cache.h b/src/cache.h index 1715c7220..f74fddfc9 100644 --- a/src/cache.h +++ b/src/cache.h @@ -31,11 +31,11 @@ typedef struct { typedef struct { git_oidmap *map; git_mutex lock; - size_t used_memory; + int64_t used_memory; } git_cache; extern bool git_cache__enabled; -extern size_t git_cache__max_storage; +extern int64_t git_cache__max_storage; int git_cache_set_max_object_size(git_otype type, size_t size); diff --git a/src/util.c b/src/util.c index c3fc69756..2dec49f17 100644 --- a/src/util.c +++ b/src/util.c @@ -39,7 +39,6 @@ int git_libgit2_capabilities() /* Declarations for tuneable settings */ extern size_t git_mwindow__window_size; extern size_t git_mwindow__mapped_limit; -extern size_t git_odb__cache_size; static int config_level_to_futils_dir(int config_level) { @@ -104,7 +103,7 @@ int git_libgit2_opts(int key, ...) } case GIT_OPT_SET_CACHE_MAX_SIZE: - git_cache__max_storage = va_arg(ap, size_t); + git_cache__max_storage = va_arg(ap, int64_t); break; case GIT_OPT_ENABLE_CACHING: From 920cbc9846c526958f6b4a7f914bdde2da1d34ec Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 22 Apr 2013 17:31:47 +0200 Subject: [PATCH 069/384] cache: More aggressive default --- src/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cache.c b/src/cache.c index 4b26d4351..b462af408 100644 --- a/src/cache.c +++ b/src/cache.c @@ -18,7 +18,7 @@ GIT__USE_OIDMAP bool git_cache__enabled = true; -int64_t git_cache__max_storage = (4 * 1024 * 1024); +int64_t git_cache__max_storage = (256 * 1024 * 1024); static git_atomic64 git_cache__current_storage = {0}; From a5de9044270e29b2388185bec02d7c0de301e7d1 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 23 Apr 2013 02:24:44 +0200 Subject: [PATCH 070/384] refs: Better error name --- src/refdb_fs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 742ac6260..2f2e67104 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -240,7 +240,7 @@ parse_failed: return -1; } -static int loose_parse_oid(git_oid *oid, git_buf *file_content) +static int loose_parse_oid(git_oid *oid, const char *filename, git_buf *file_content) { size_t len; const char *str; @@ -262,7 +262,7 @@ static int loose_parse_oid(git_oid *oid, git_buf *file_content) return 0; corrupted: - giterr_set(GITERR_REFERENCE, "Corrupted loose reference file"); + giterr_set(GITERR_REFERENCE, "Corrupted loose reference file: %s", filename); return -1; } @@ -289,7 +289,7 @@ static int loose_lookup_to_packfile( memcpy(ref->name, name, name_len); ref->name[name_len] = 0; - if (loose_parse_oid(&ref->oid, &ref_file) < 0) { + if (loose_parse_oid(&ref->oid, name, &ref_file) < 0) { git_buf_free(&ref_file); git__free(ref); return -1; @@ -431,7 +431,7 @@ static int loose_lookup( *out = git_reference__alloc_symbolic(ref_name, target); } else { - if ((error = loose_parse_oid(&oid, &ref_file)) < 0) + if ((error = loose_parse_oid(&oid, ref_name, &ref_file)) < 0) goto done; *out = git_reference__alloc(ref_name, &oid, NULL); From 6c1b6b7abcef75d421e4d59c397eff54cc1f28aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 23 Apr 2013 16:21:47 +0200 Subject: [PATCH 071/384] examples: init the threading system --- examples/network/git2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/network/git2.c b/examples/network/git2.c index ecb16630b..5b32ac809 100644 --- a/examples/network/git2.c +++ b/examples/network/git2.c @@ -54,6 +54,8 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + git_threads_init(); + for (i = 0; commands[i].name != NULL; ++i) { if (!strcmp(argv[1], commands[i].name)) return run_command(commands[i].fn, --argc, ++argv); From f0e37a8b863c2e6caba2b15e4d723bddfe74b46c Mon Sep 17 00:00:00 2001 From: Xavier L Date: Tue, 23 Apr 2013 12:22:29 -0400 Subject: [PATCH 072/384] Added function to insert commit into pack --- include/git2/pack.h | 12 ++++++++++++ src/pack-objects.c | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/git2/pack.h b/include/git2/pack.h index 2f033bef6..118b8d554 100644 --- a/include/git2/pack.h +++ b/include/git2/pack.h @@ -94,6 +94,18 @@ GIT_EXTERN(int) git_packbuilder_insert(git_packbuilder *pb, const git_oid *id, c */ GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *id); +/** + * Insert a commit object + * + * This will add a commit as well as the completed referenced tree. + * + * @param pb The packbuilder + * @param id The oid of the commit + * + * @return 0 or an error code + */ +GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *id); + /** * Write the new pack and the corresponding index to path * diff --git a/src/pack-objects.c b/src/pack-objects.c index 459201f58..56240125b 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -1284,6 +1284,21 @@ static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *pay git_buf_cstr(&ctx->buf)); } +int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid) +{ +git_commit *commit; + +if (git_commit_lookup(&commit, pb->repo, oid) < 0 || +git_packbuilder_insert(pb, oid, NULL) < 0) +return -1; + +if (git_packbuilder_insert_tree(pb, git_commit_tree_id(commit)) < 0) +return -1; + +git_commit_free(commit); +return 0; +} + int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid) { git_tree *tree; From 0b90366c3bf5c7149cf69b5fa1a8327032f8a60f Mon Sep 17 00:00:00 2001 From: Xavier L Date: Tue, 23 Apr 2013 12:27:38 -0400 Subject: [PATCH 073/384] Fixes indentation --- src/pack-objects.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pack-objects.c b/src/pack-objects.c index 56240125b..1329a9bc1 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -1286,17 +1286,17 @@ static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *pay int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid) { -git_commit *commit; + git_commit *commit; -if (git_commit_lookup(&commit, pb->repo, oid) < 0 || -git_packbuilder_insert(pb, oid, NULL) < 0) -return -1; + if (git_commit_lookup(&commit, pb->repo, oid) < 0 || + git_packbuilder_insert(pb, oid, NULL) < 0) + return -1; -if (git_packbuilder_insert_tree(pb, git_commit_tree_id(commit)) < 0) -return -1; + if (git_packbuilder_insert_tree(pb, git_commit_tree_id(commit)) < 0) + return -1; -git_commit_free(commit); -return 0; + git_commit_free(commit); + return 0; } int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid) From 9a9de29d36c82d20a821b9b076ad388364bc5553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 23 Apr 2013 19:08:13 +0200 Subject: [PATCH 074/384] Document the odb backend constructors --- include/git2/odb_backend.h | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h index d38005d15..4c2493a25 100644 --- a/include/git2/odb_backend.h +++ b/include/git2/odb_backend.h @@ -19,11 +19,43 @@ */ GIT_BEGIN_DECL -/** +/* * Constructors for in-box ODB backends. */ + +/** + * Create a backend for the packfiles. + * + * @param out location to store the odb backend pointer + * @param objects_dir the Git repository's objects directory + * + * @return 0 or an error code + */ GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir); + +/** + * Create a backend for loose objects + * + * @param out location to store the odb backend pointer + * @param objects_dir the Git repository's objects directory + * @param compression_level zlib compression level to use + * @param do_fsync whether to do an fsync() after writing (currently ignored) + * + * @return 0 or an error code + */ GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync); + +/** + * Create a backend out of a single packfile + * + * This can be useful for inspecting the contents of a single + * packfile. + * + * @param out location to store the odb backend pointer + * @param index_file path to the packfile's .idx file + * + * @return 0 or an error code + */ GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file); /** Streaming mode */ From a952b9867d21b032655ac6c0eaabb558e7875926 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Tue, 23 Apr 2013 21:28:28 +0300 Subject: [PATCH 075/384] remove git_remote_pushspec --- include/git2/remote.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 5dcd93099..f02b95678 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -181,15 +181,6 @@ GIT_EXTERN(int) git_remote_add_push(git_remote *remote, const char *refspec); */ GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, git_remote *remote); -/** - * Get the push refspec - * - * @param remote the remote - * @return a pointer to the push refspec or NULL if it doesn't exist - */ - -GIT_EXTERN(const git_refspec *) git_remote_pushspec(const git_remote *remote); - /** * Clear the refspecs * From a2378ae4fee55c95eb9a1f6b44f5a837d39fa724 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 23 Apr 2013 20:42:29 +0200 Subject: [PATCH 076/384] opts: Add getter for cached memory --- include/git2/common.h | 3 ++- src/cache.c | 3 +-- src/cache.h | 1 + src/util.c | 5 +++++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index ccd252fda..6101e13bc 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -133,7 +133,8 @@ enum { GIT_OPT_SET_SEARCH_PATH, GIT_OPT_SET_CACHE_OBJECT_LIMIT, GIT_OPT_SET_CACHE_MAX_SIZE, - GIT_OPT_ENABLE_CACHING + GIT_OPT_ENABLE_CACHING, + GIT_OPT_GET_CACHED_MEMORY }; /** diff --git a/src/cache.c b/src/cache.c index b462af408..88f643b35 100644 --- a/src/cache.c +++ b/src/cache.c @@ -19,8 +19,7 @@ GIT__USE_OIDMAP bool git_cache__enabled = true; int64_t git_cache__max_storage = (256 * 1024 * 1024); - -static git_atomic64 git_cache__current_storage = {0}; +git_atomic64 git_cache__current_storage = {0}; static size_t git_cache__max_object_size[8] = { 0, /* GIT_OBJ__EXT1 */ diff --git a/src/cache.h b/src/cache.h index f74fddfc9..1fb87dcea 100644 --- a/src/cache.h +++ b/src/cache.h @@ -36,6 +36,7 @@ typedef struct { extern bool git_cache__enabled; extern int64_t git_cache__max_storage; +extern git_atomic64 git_cache__current_storage; int git_cache_set_max_object_size(git_otype type, size_t size); diff --git a/src/util.c b/src/util.c index 2dec49f17..ce67c7e62 100644 --- a/src/util.c +++ b/src/util.c @@ -109,6 +109,11 @@ int git_libgit2_opts(int key, ...) case GIT_OPT_ENABLE_CACHING: git_cache__enabled = (va_arg(ap, int) != 0); break; + + case GIT_OPT_GET_CACHED_MEMORY: + *(va_arg(ap, int64_t *)) = git_cache__current_storage.val; + *(va_arg(ap, int64_t *)) = git_cache__max_storage; + break; } va_end(ap); From dfdf709e3f8aed5cb8ee54d9c508b65672c8a211 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Tue, 23 Apr 2013 21:29:07 +0300 Subject: [PATCH 077/384] get last refspec in clone test --- tests-clar/clone/nonetwork.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c index 02066e07d..506673737 100644 --- a/tests-clar/clone/nonetwork.c +++ b/tests-clar/clone/nonetwork.c @@ -149,7 +149,7 @@ void test_clone_nonetwork__custom_fetch_spec(void) cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); cl_git_pass(git_remote_load(&g_remote, g_repo, "origin")); - actual_fs = git_vector_get(&g_remote->refspecs, 0); + actual_fs = git_vector_get(&g_remote->refspecs, g_remote->refspecs.length - 1); cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs)); cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs)); From bd0a07f4bb0b83a84127589f7385eb2309910b66 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Tue, 23 Apr 2013 12:28:59 -0700 Subject: [PATCH 078/384] Clone: replace fetch spec with custom value --- src/clone.c | 8 +++++--- tests-clar/clone/nonetwork.c | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/clone.c b/src/clone.c index 8f10ca819..0665576e3 100644 --- a/src/clone.c +++ b/src/clone.c @@ -323,9 +323,11 @@ static int create_and_configure_origin( (error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0) goto on_error; - if (options->fetch_spec && - (error = git_remote_add_fetch(origin, options->fetch_spec)) < 0) - goto on_error; + if (options->fetch_spec) { + git_remote_clear_refspecs(origin); + if ((error = git_remote_add_fetch(origin, options->fetch_spec)) < 0) + goto on_error; + } if (options->push_spec && (error = git_remote_add_push(origin, options->push_spec)) < 0) diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c index 506673737..02066e07d 100644 --- a/tests-clar/clone/nonetwork.c +++ b/tests-clar/clone/nonetwork.c @@ -149,7 +149,7 @@ void test_clone_nonetwork__custom_fetch_spec(void) cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); cl_git_pass(git_remote_load(&g_remote, g_repo, "origin")); - actual_fs = git_vector_get(&g_remote->refspecs, g_remote->refspecs.length - 1); + actual_fs = git_vector_get(&g_remote->refspecs, 0); cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs)); cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs)); From a5df71c11fef996d67593546fa1edb2865e6a5d9 Mon Sep 17 00:00:00 2001 From: Linquize Date: Fri, 19 Apr 2013 22:36:01 +0800 Subject: [PATCH 079/384] Support diff.context config --- src/diff.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index 37c89f3f1..e6f5374e7 100644 --- a/src/diff.c +++ b/src/diff.c @@ -267,6 +267,16 @@ static int config_bool(git_config *cfg, const char *name, int defvalue) return val; } +static int config_int(git_config *cfg, const char *name, int defvalue) +{ + int val = defvalue; + + if (git_config_get_int32(&val, cfg, name) < 0) + giterr_clear(); + + return val; +} + static git_diff_list *git_diff_list_alloc( git_repository *repo, const git_diff_options *opts) { @@ -306,7 +316,8 @@ static git_diff_list *git_diff_list_alloc( if (opts == NULL) { /* Make sure we default to 3 lines */ - diff->opts.context_lines = 3; + int context = config_int(cfg, "diff.context", 3); + diff->opts.context_lines = max(context, 0); return diff; } From 608d04667ae08e991c1ffa6f33573d15930245b3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 22 Apr 2013 13:51:35 -0700 Subject: [PATCH 080/384] Make tree to tree diffs case sensitive When case insensitive tree iterators were added, we started reading the case sensitivity of the index to decide if the tree should be case sensitive. This is good for index-to-tree comparisons, but for tree-to-tree comparisons, we should really default to doing a case sensitive comparison unless the user really wants otherwise. --- src/diff.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/diff.c b/src/diff.c index e6f5374e7..d9348bba8 100644 --- a/src/diff.c +++ b/src/diff.c @@ -870,12 +870,20 @@ int git_diff_tree_to_tree( const git_diff_options *opts) { int error = 0; + git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE; assert(diff && repo); + /* for tree to tree diff, be case sensitive even if the index is + * currently case insensitive, unless the user explicitly asked + * for case insensitivity + */ + if (opts && (opts->flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0) + iflag = GIT_ITERATOR_IGNORE_CASE; + DIFF_FROM_ITERATORS( - git_iterator_for_tree(&a, old_tree, 0, pfx, pfx), - git_iterator_for_tree(&b, new_tree, 0, pfx, pfx) + git_iterator_for_tree(&a, old_tree, iflag, pfx, pfx), + git_iterator_for_tree(&b, new_tree, iflag, pfx, pfx) ); return error; From ab01cbd4ddd756c7beda3f483791cb5d4e211872 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 22 Apr 2013 14:24:12 -0700 Subject: [PATCH 081/384] Add configs to repo config cache This adds a bunch of additional config values to the repository config value cache and makes it easier to add a simple boolean config without creating enum values for each possible setting. Also, this fixes a bug in git_config_refresh where the config cache was not being cleared which could lead to potential incorrect values. The work to start using the new cached configs will come in the next couple of commits... --- src/config.c | 3 +++ src/config_cache.c | 31 ++++++++++++++++++++++++------- src/repository.h | 25 +++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/config.c b/src/config.c index 1283522ca..cb9d4014c 100644 --- a/src/config.c +++ b/src/config.c @@ -293,6 +293,9 @@ int git_config_refresh(git_config *cfg) error = file->refresh(file); } + if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL) + git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg)); + return error; } diff --git a/src/config_cache.c b/src/config_cache.c index 2f36df7d1..84de3a5ed 100644 --- a/src/config_cache.c +++ b/src/config_cache.c @@ -26,7 +26,7 @@ struct map_data { * files that have the text property set. Alternatives are lf, crlf * and native, which uses the platform's native line ending. The default * value is native. See gitattributes(5) for more information on - * end-of-line conversion. + * end-of-line conversion. */ static git_cvar_map _cvar_map_eol[] = { {GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET}, @@ -37,7 +37,7 @@ static git_cvar_map _cvar_map_eol[] = { /* * core.autocrlf - * Setting this variable to "true" is almost the same as setting + * Setting this variable to "true" is almost the same as setting * the text attribute to "auto" on all files except that text files are * not guaranteed to be normalized: files that contain CRLF in the * repository will not be touched. Use this setting if you want to have @@ -51,9 +51,22 @@ static git_cvar_map _cvar_map_autocrlf[] = { {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT} }; +/* + * Generic map for integer values + */ +static git_cvar_map _cvar_map_int[] = { + {GIT_CVAR_INT32, NULL, 0}, +}; + static struct map_data _cvar_maps[] = { {"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT}, - {"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT} + {"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT}, + {"core.symlinks", NULL, 0, GIT_SYMLINKS_DEFAULT }, + {"core.ignorecase", NULL, 0, GIT_IGNORECASE_DEFAULT }, + {"core.filemode", NULL, 0, GIT_FILEMODE_DEFAULT }, + {"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT }, + {"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT }, + {"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT }, }; int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar) @@ -69,12 +82,16 @@ int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar) if (error < 0) return error; - error = git_config_get_mapped(out, - config, data->cvar_name, data->maps, data->map_count); + if (data->maps) + error = git_config_get_mapped( + out, config, data->cvar_name, data->maps, data->map_count); + else + error = git_config_get_bool(out, config, data->cvar_name); - if (error == GIT_ENOTFOUND) + if (error == GIT_ENOTFOUND) { + giterr_clear(); *out = data->default_value; - + } else if (error < 0) return error; diff --git a/src/repository.h b/src/repository.h index cc2f8c2b8..f7f9ecb1f 100644 --- a/src/repository.h +++ b/src/repository.h @@ -12,6 +12,7 @@ #include "git2/odb.h" #include "git2/repository.h" #include "git2/object.h" +#include "git2/config.h" #include "index.h" #include "cache.h" @@ -31,7 +32,13 @@ /** Cvar cache identifiers */ typedef enum { GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */ - GIT_CVAR_EOL, /* core.eol */ + GIT_CVAR_EOL, /* core.eol */ + GIT_CVAR_SYMLINKS, /* core.symlinks */ + GIT_CVAR_IGNORECASE, /* core.ignorecase */ + GIT_CVAR_FILEMODE, /* core.filemode */ + GIT_CVAR_IGNORESTAT, /* core.ignorestat */ + GIT_CVAR_TRUSTCTIME, /* core.trustctime */ + GIT_CVAR_ABBREV, /* core.abbrev */ GIT_CVAR_CACHE_MAX } git_cvar_cached; @@ -67,7 +74,21 @@ typedef enum { #else GIT_EOL_NATIVE = GIT_EOL_LF, #endif - GIT_EOL_DEFAULT = GIT_EOL_NATIVE + GIT_EOL_DEFAULT = GIT_EOL_NATIVE, + + /* core.symlinks: bool */ + GIT_SYMLINKS_DEFAULT = GIT_CVAR_TRUE, + /* core.ignorecase */ + GIT_IGNORECASE_DEFAULT = GIT_CVAR_FALSE, + /* core.filemode */ + GIT_FILEMODE_DEFAULT = GIT_CVAR_TRUE, + /* core.ignorestat */ + GIT_IGNORESTAT_DEFAULT = GIT_CVAR_FALSE, + /* core.trustctime */ + GIT_TRUSTCTIME_DEFAULT = GIT_CVAR_TRUE, + /* core.abbrev */ + GIT_ABBREV_DEFAULT = 7, + } git_cvar_value; /* internal repository init flags */ From eac76c230c92594eaa528e50746119bd3c33ffbb Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 22 Apr 2013 14:27:36 -0700 Subject: [PATCH 082/384] Use config cache where possible This converts many of the config lookups that are done around the library to use the repository config cache. This was everything I could find that wasn't part of diff (which requires a larger fix). --- src/checkout.c | 23 ++++++++--------------- src/ignore.c | 36 +++++++++--------------------------- src/ignore.h | 2 +- src/index.c | 17 ++++++++--------- 4 files changed, 26 insertions(+), 52 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 62a73d6d0..e29fccd05 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1119,7 +1119,6 @@ static int checkout_data_init( git_checkout_opts *proposed) { int error = 0; - git_config *cfg; git_repository *repo = git_iterator_owner(target); memset(data, 0, sizeof(*data)); @@ -1132,9 +1131,6 @@ static int checkout_data_init( if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0) return error; - if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) - return error; - data->repo = repo; GITERR_CHECK_VERSION( @@ -1147,7 +1143,10 @@ static int checkout_data_init( /* refresh config and index content unless NO_REFRESH is given */ if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) { - if ((error = git_config_refresh(cfg)) < 0) + git_config *cfg; + + if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 || + (error = git_config_refresh(cfg)) < 0) goto cleanup; /* if we are checking out the index, don't reload, @@ -1184,19 +1183,13 @@ static int checkout_data_init( data->pfx = git_pathspec_prefix(&data->opts.paths); - error = git_config_get_bool(&data->can_symlink, cfg, "core.symlinks"); - if (error < 0) { - if (error != GIT_ENOTFOUND) - goto cleanup; - - /* If "core.symlinks" is not found anywhere, default to true. */ - data->can_symlink = true; - giterr_clear(); - error = 0; - } + if ((error = git_repository__cvar( + &data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0) + goto cleanup; if (!data->opts.baseline) { data->opts_free_baseline = true; + error = checkout_lookup_head_tree(&data->opts.baseline, repo); if (error == GIT_EORPHANEDHEAD) { diff --git a/src/ignore.c b/src/ignore.c index 17779522c..e150b9585 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -15,24 +15,14 @@ static int parse_ignore_file( git_attr_fnmatch *match = NULL; const char *scan = NULL; char *context = NULL; - bool ignore_case = false; - git_config *cfg = NULL; - int val; + int ignore_case = false; - /* Prefer to have the caller pass in a git_ignores as the parsedata object. - * If they did not, then we can (much more slowly) find the value of - * ignore_case by using the repository object. */ - if (parsedata != NULL) { + /* Prefer to have the caller pass in a git_ignores as the parsedata + * object. If they did not, then look up the value of ignore_case */ + if (parsedata != NULL) ignore_case = ((git_ignores *)parsedata)->ignore_case; - } else { - if ((error = git_repository_config(&cfg, repo)) < 0) - return error; - - if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0) - ignore_case = (val != 0); - - git_config_free(cfg); - } + else if (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0) + return error; if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) { context = ignores->key + 2; @@ -109,8 +99,6 @@ int git_ignore__for_path( { int error = 0; const char *workdir = git_repository_workdir(repo); - git_config *cfg = NULL; - int val; assert(ignores); @@ -118,17 +106,11 @@ int git_ignore__for_path( git_buf_init(&ignores->dir, 0); ignores->ign_internal = NULL; - /* Set the ignore_case flag appropriately */ - if ((error = git_repository_config(&cfg, repo)) < 0) + /* Read the ignore_case flag */ + if ((error = git_repository__cvar( + &ignores->ignore_case, repo, GIT_CVAR_IGNORECASE)) < 0) goto cleanup; - if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0) - ignores->ignore_case = (val != 0); - else - ignores->ignore_case = 0; - - git_config_free(cfg); - if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 || (error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 || (error = git_attr_cache__init(repo)) < 0) diff --git a/src/ignore.h b/src/ignore.h index 5af8e8e7d..e00e4a8c8 100644 --- a/src/ignore.h +++ b/src/ignore.h @@ -28,7 +28,7 @@ typedef struct { git_attr_file *ign_internal; git_vector ign_path; git_vector ign_global; - unsigned int ignore_case:1; + int ignore_case; } git_ignores; extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign); diff --git a/src/index.c b/src/index.c index 2afd28158..d8ca78e52 100644 --- a/src/index.c +++ b/src/index.c @@ -330,7 +330,7 @@ void git_index_clear(git_index *index) git_vector_clear(&index->entries); git_index_reuc_clear(index); - + git_futils_filestamp_set(&index->stamp, NULL); git_tree_cache_free(index->tree); @@ -352,19 +352,18 @@ int git_index_set_caps(git_index *index, unsigned int caps) old_ignore_case = index->ignore_case; if (caps == GIT_INDEXCAP_FROM_OWNER) { - git_config *cfg; + git_repository *repo = INDEX_OWNER(index); int val; - if (INDEX_OWNER(index) == NULL || - git_repository_config__weakptr(&cfg, INDEX_OWNER(index)) < 0) - return create_index_error(-1, - "Cannot get repository config to set index caps"); + if (!repo) + return create_index_error( + -1, "Cannot access repository to set index caps"); - if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0) + if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORECASE)) index->ignore_case = (val != 0); - if (git_config_get_bool(&val, cfg, "core.filemode") == 0) + if (!git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE)) index->distrust_filemode = (val == 0); - if (git_config_get_bool(&val, cfg, "core.symlinks") == 0) + if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS)) index->no_symlinks = (val == 0); } else { From 6be368bf16c86380ea84d7e39b65e0ebd9606174 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 22 Apr 2013 16:24:53 -0700 Subject: [PATCH 083/384] Clear repo config cache when cfgs are set This is a conservative change, but it seemed like the only safe thing to do -- i.e. clear the cvar cache when a config gets set. --- src/config.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index cb9d4014c..3c0bbe9a7 100644 --- a/src/config.c +++ b/src/config.c @@ -363,6 +363,7 @@ int git_config_set_bool(git_config *cfg, const char *name, int value) int git_config_set_string(git_config *cfg, const char *name, const char *value) { + int error; git_config_backend *file; file_internal *internal; @@ -374,7 +375,12 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) internal = git_vector_get(&cfg->files, 0); file = internal->file; - return file->set(file, name, value); + error = file->set(file, name, value); + + if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL) + git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg)); + + return error; } /*********** From b1ff7004ab132c8d4bddd181127f1c8d5ce9b7b4 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 22 Apr 2013 16:25:57 -0700 Subject: [PATCH 084/384] Improve diff config options handling This makes diff use the cvar cache for config options where possible, and also adds support for a number of other config options to diff including "diff.context", "diff.ignoreSubmodules", "diff.noprefix", "diff.mnemonicprefix", and "core.abbrev". To make this natural, this involved a rearrangement of the code that allocates the diff object vs. the code that initializes it based on the combination of options passed in by the user and read from the config. This commit includes tests for most of these new options as well. --- src/diff.c | 211 ++++++++++++++++++++++++---------------- src/diff_output.c | 17 +++- tests-clar/diff/patch.c | 78 +++++++++++++++ tests-clar/diff/tree.c | 74 ++++++++++++++ 4 files changed, 291 insertions(+), 89 deletions(-) diff --git a/src/diff.c b/src/diff.c index d9348bba8..bce2914d1 100644 --- a/src/diff.c +++ b/src/diff.c @@ -14,6 +14,8 @@ #define DIFF_FLAG_IS_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) != 0) #define DIFF_FLAG_ISNT_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) == 0) +#define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->opts.flags = \ + (VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL)) static git_diff_delta *diff_delta__alloc( git_diff_list *diff, @@ -277,68 +279,148 @@ static int config_int(git_config *cfg, const char *name, int defvalue) return val; } -static git_diff_list *git_diff_list_alloc( - git_repository *repo, const git_diff_options *opts) +static const char *diff_mnemonic_prefix( + git_iterator_type_t type, bool left_side) { - git_config *cfg; + const char *pfx = ""; + + switch (type) { + case GIT_ITERATOR_TYPE_EMPTY: pfx = "c"; break; + case GIT_ITERATOR_TYPE_TREE: pfx = "c"; break; + case GIT_ITERATOR_TYPE_INDEX: pfx = "i"; break; + case GIT_ITERATOR_TYPE_WORKDIR: pfx = "w"; break; + case GIT_ITERATOR_TYPE_FS: pfx = left_side ? "1" : "2"; break; + default: break; + } + + /* note: without a deeper look at pathspecs, there is no easy way + * to get the (o)bject / (w)ork tree mnemonics working... + */ + + return pfx; +} + +static git_diff_list *diff_list_alloc( + git_repository *repo, + git_iterator *old_iter, + git_iterator *new_iter) +{ + git_diff_options dflt = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = git__calloc(1, sizeof(git_diff_list)); - if (diff == NULL) + if (!diff) return NULL; + assert(repo && old_iter && new_iter); + GIT_REFCOUNT_INC(diff); diff->repo = repo; + diff->old_src = old_iter->type; + diff->new_src = new_iter->type; + memcpy(&diff->opts, &dflt, sizeof(diff->opts)); if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 || - git_pool_init(&diff->pool, 1, 0) < 0) - goto fail; + git_pool_init(&diff->pool, 1, 0) < 0) { + git_diff_list_free(diff); + return NULL; + } + + /* Use case-insensitive compare if either iterator has + * the ignore_case bit set */ + if (!git_iterator_ignore_case(old_iter) && + !git_iterator_ignore_case(new_iter)) + { + diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE; + + diff->strcomp = git__strcmp; + diff->strncomp = git__strncmp; + diff->pfxcomp = git__prefixcmp; + diff->entrycomp = git_index_entry__cmp; + } else { + diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE; + + diff->strcomp = git__strcasecmp; + diff->strncomp = git__strncasecmp; + diff->pfxcomp = git__prefixcmp_icase; + diff->entrycomp = git_index_entry__cmp_icase; + } + + return diff; +} + +static int diff_list_apply_options( + git_diff_list *diff, + const git_diff_options *opts) +{ + git_config *cfg; + git_repository *repo = diff->repo; + git_pool *pool = &diff->pool; + int val; + + if (opts) { + /* copy user options (except case sensitivity info from iterators) */ + bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE); + memcpy(&diff->opts, opts, sizeof(diff->opts)); + DIFF_FLAG_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE, icase); + + /* initialize pathspec from options */ + if (git_pathspec_init(&diff->pathspec, &opts->pathspec, pool) < 0) + return -1; + } + + /* flag INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */ + if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES)) + diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE; /* load config values that affect diff behavior */ if (git_repository_config__weakptr(&cfg, repo) < 0) - goto fail; - if (config_bool(cfg, "core.symlinks", 1)) + return -1; + + if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS) && val) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS; - if (config_bool(cfg, "core.ignorestat", 0)) + + if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORESTAT) && val) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED; - if (config_bool(cfg, "core.filemode", 1)) + + if ((diff->opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 && + !git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE) && val) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS; - if (config_bool(cfg, "core.trustctime", 1)) + + if (!git_repository__cvar(&val, repo, GIT_CVAR_TRUSTCTIME) && val) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME; + /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ - /* TODO: there are certain config settings where even if we were - * not given an options structure, we need the diff list to have one - * so that we can store the altered default values. - * - * - diff.ignoreSubmodules - * - diff.mnemonicprefix - * - diff.noprefix - */ + /* If not given explicit `opts`, check `diff.xyz` configs */ + if (!opts) { + diff->opts.context_lines = config_int(cfg, "diff.context", 3); - if (opts == NULL) { - /* Make sure we default to 3 lines */ - int context = config_int(cfg, "diff.context", 3); - diff->opts.context_lines = max(context, 0); - return diff; + if (config_bool(cfg, "diff.ignoreSubmodules", 0)) + diff->opts.flags |= GIT_DIFF_IGNORE_SUBMODULES; } - memcpy(&diff->opts, opts, sizeof(git_diff_options)); + /* if either prefix is not set, figure out appropriate value */ + if (!diff->opts.old_prefix || !diff->opts.new_prefix) { + const char *use_old = DIFF_OLD_PREFIX_DEFAULT; + const char *use_new = DIFF_NEW_PREFIX_DEFAULT; - if(opts->flags & GIT_DIFF_IGNORE_FILEMODE) - diff->diffcaps = diff->diffcaps & ~GIT_DIFFCAPS_TRUST_MODE_BITS; + if (config_bool(cfg, "diff.noprefix", 0)) { + use_old = use_new = ""; + } else if (config_bool(cfg, "diff.mnemonicprefix", 0)) { + use_old = diff_mnemonic_prefix(diff->old_src, true); + use_new = diff_mnemonic_prefix(diff->new_src, false); + } - /* pathspec init will do nothing for empty pathspec */ - if (git_pathspec_init(&diff->pathspec, &opts->pathspec, &diff->pool) < 0) - goto fail; - - /* TODO: handle config diff.mnemonicprefix, diff.noprefix */ - - diff->opts.old_prefix = diff_strdup_prefix(&diff->pool, - opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT); - diff->opts.new_prefix = diff_strdup_prefix(&diff->pool, - opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT); + if (!diff->opts.old_prefix) + diff->opts.old_prefix = use_old; + if (!diff->opts.new_prefix) + diff->opts.new_prefix = use_new; + } + /* strdup prefix from pool so we're not dependent on external data */ + diff->opts.old_prefix = diff_strdup_prefix(pool, diff->opts.old_prefix); + diff->opts.new_prefix = diff_strdup_prefix(pool, diff->opts.new_prefix); if (!diff->opts.old_prefix || !diff->opts.new_prefix) - goto fail; + return -1; if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) { const char *swap = diff->opts.old_prefix; @@ -346,15 +428,7 @@ static git_diff_list *git_diff_list_alloc( diff->opts.new_prefix = swap; } - /* INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */ - if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES)) - diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE; - - return diff; - -fail: - git_diff_list_free(diff); - return NULL; + return 0; } static void diff_list_free(git_diff_list *diff) @@ -618,37 +692,6 @@ static bool entry_is_prefixed( item->path[pathlen] == '/'); } -static int diff_list_init_from_iterators( - git_diff_list *diff, - git_iterator *old_iter, - git_iterator *new_iter) -{ - diff->old_src = old_iter->type; - diff->new_src = new_iter->type; - - /* Use case-insensitive compare if either iterator has - * the ignore_case bit set */ - if (!git_iterator_ignore_case(old_iter) && - !git_iterator_ignore_case(new_iter)) - { - diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE; - - diff->strcomp = git__strcmp; - diff->strncomp = git__strncmp; - diff->pfxcomp = git__prefixcmp; - diff->entrycomp = git_index_entry__cmp; - } else { - diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE; - - diff->strcomp = git__strcasecmp; - diff->strncomp = git__strncasecmp; - diff->pfxcomp = git__prefixcmp_icase; - diff->entrycomp = git_index_entry__cmp_icase; - } - - return 0; -} - int git_diff__from_iterators( git_diff_list **diff_ptr, git_repository *repo, @@ -659,20 +702,18 @@ int git_diff__from_iterators( int error = 0; const git_index_entry *oitem, *nitem; git_buf ignore_prefix = GIT_BUF_INIT; - git_diff_list *diff = git_diff_list_alloc(repo, opts); - - *diff_ptr = NULL; - - if (!diff || diff_list_init_from_iterators(diff, old_iter, new_iter) < 0) - goto fail; + git_diff_list *diff = diff_list_alloc(repo, old_iter, new_iter); + GITERR_CHECK_ALLOC(diff); + /* make iterators have matching icase behavior */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) { if (git_iterator_set_ignore_case(old_iter, true) < 0 || git_iterator_set_ignore_case(new_iter, true) < 0) goto fail; } - if (git_iterator_current(&oitem, old_iter) < 0 || + if (diff_list_apply_options(diff, opts) < 0 || + git_iterator_current(&oitem, old_iter) < 0 || git_iterator_current(&nitem, new_iter) < 0) goto fail; diff --git a/src/diff_output.c b/src/diff_output.c index 34a3e506c..b8bb73bf7 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -1114,11 +1114,20 @@ int git_diff_print_compact( static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta) { - char start_oid[8], end_oid[8]; + int abbrevlen; + char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; - /* TODO: Determine a good actual OID range to print */ - git_oid_tostr(start_oid, sizeof(start_oid), &delta->old_file.oid); - git_oid_tostr(end_oid, sizeof(end_oid), &delta->new_file.oid); + if (git_repository__cvar(&abbrevlen, pi->diff->repo, GIT_CVAR_ABBREV) < 0) + return -1; + + abbrevlen += 1; /* for NUL byte */ + if (abbrevlen < 2) + abbrevlen = 2; + else if (abbrevlen > (int)sizeof(start_oid)) + abbrevlen = (int)sizeof(start_oid); + + git_oid_tostr(start_oid, abbrevlen, &delta->old_file.oid); + git_oid_tostr(end_oid, abbrevlen, &delta->new_file.oid); /* TODO: Match git diff more closely */ if (delta->old_file.mode == delta->new_file.mode) { diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c index d41f3f12d..40b191dd5 100644 --- a/tests-clar/diff/patch.c +++ b/tests-clar/diff/patch.c @@ -135,6 +135,84 @@ void test_diff_patch__to_string(void) git_tree_free(one); } +void test_diff_patch__config_options(void) +{ + const char *one_sha = "26a125e"; /* current HEAD */ + git_tree *one; + git_config *cfg; + git_diff_list *diff; + git_diff_patch *patch; + char *text; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + char *onefile = "staged_changes_modified_file"; + const char *expected1 = "diff --git c/staged_changes_modified_file i/staged_changes_modified_file\nindex 70bd944..906ee77 100644\n--- c/staged_changes_modified_file\n+++ i/staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n"; + const char *expected2 = "diff --git i/staged_changes_modified_file w/staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- i/staged_changes_modified_file\n+++ w/staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n"; + const char *expected3 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n"; + const char *expected4 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 70bd9443ada0..906ee7711f4f 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n"; + + g_repo = cl_git_sandbox_init("status"); + cl_git_pass(git_repository_config(&cfg, g_repo)); + one = resolve_commit_oid_to_tree(g_repo, one_sha); + opts.pathspec.count = 1; + opts.pathspec.strings = &onefile; + + + cl_git_pass(git_config_set_string(cfg, "diff.mnemonicprefix", "true")); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts)); + + cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); + cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected1, text); + + git__free(text); + git_diff_patch_free(patch); + git_diff_list_free(diff); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); + cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected2, text); + + git__free(text); + git_diff_patch_free(patch); + git_diff_list_free(diff); + + + cl_git_pass(git_config_set_string(cfg, "diff.noprefix", "true")); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); + cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected3, text); + + git__free(text); + git_diff_patch_free(patch); + git_diff_list_free(diff); + + + cl_git_pass(git_config_set_int32(cfg, "core.abbrev", 12)); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts)); + + cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); + cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected4, text); + + git__free(text); + git_diff_patch_free(patch); + git_diff_list_free(diff); + + git_tree_free(one); + git_config_free(cfg); +} + void test_diff_patch__hunks_have_correct_line_numbers(void) { git_config *cfg; diff --git a/tests-clar/diff/tree.c b/tests-clar/diff/tree.c index 850feefde..f05c7869e 100644 --- a/tests-clar/diff/tree.c +++ b/tests-clar/diff/tree.c @@ -454,3 +454,77 @@ void test_diff_tree__issue_1397(void) cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]); } + +static void set_config_int(git_repository *repo, const char *name, int value) +{ + git_config *cfg; + + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_set_int32(cfg, name, value)); + git_config_free(cfg); +} + +void test_diff_tree__diff_configs(void) +{ + const char *a_commit = "d70d245e"; + const char *b_commit = "7a9e0b02"; + + g_repo = cl_git_sandbox_init("diff"); + + cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL); + cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL); + + cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL)); + + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect)); + + cl_assert_equal_i(2, expect.files); + cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(6, expect.hunks); + cl_assert_equal_i(55, expect.lines); + cl_assert_equal_i(33, expect.line_ctxt); + cl_assert_equal_i(7, expect.line_adds); + cl_assert_equal_i(15, expect.line_dels); + + git_diff_list_free(diff); + diff = NULL; + + set_config_int(g_repo, "diff.context", 1); + + memset(&expect, 0, sizeof(expect)); + + cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL)); + + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect)); + + cl_assert_equal_i(2, expect.files); + cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(7, expect.hunks); + cl_assert_equal_i(34, expect.lines); + cl_assert_equal_i(12, expect.line_ctxt); + cl_assert_equal_i(7, expect.line_adds); + cl_assert_equal_i(15, expect.line_dels); + + git_diff_list_free(diff); + diff = NULL; + + set_config_int(g_repo, "diff.context", 0); + set_config_int(g_repo, "diff.noprefix", 1); + + memset(&expect, 0, sizeof(expect)); + + cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL)); + + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect)); + + cl_assert_equal_i(2, expect.files); + cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(7, expect.hunks); + cl_assert_equal_i(22, expect.lines); + cl_assert_equal_i(0, expect.line_ctxt); + cl_assert_equal_i(7, expect.line_adds); + cl_assert_equal_i(15, expect.line_dels); +} From 687db88faf8bcfe7bccfedf65fa304f59987089c Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 22 Apr 2013 16:45:36 -0700 Subject: [PATCH 085/384] Make sure diff output is cleared on error --- src/diff.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index bce2914d1..881173cde 100644 --- a/src/diff.c +++ b/src/diff.c @@ -702,7 +702,11 @@ int git_diff__from_iterators( int error = 0; const git_index_entry *oitem, *nitem; git_buf ignore_prefix = GIT_BUF_INIT; - git_diff_list *diff = diff_list_alloc(repo, old_iter, new_iter); + git_diff_list *diff; + + *diff_ptr = NULL; + + diff = diff_list_alloc(repo, old_iter, new_iter); GITERR_CHECK_ALLOC(diff); /* make iterators have matching icase behavior */ From 879458e7cf87374241da300a864493761bf48e7c Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 24 Apr 2013 15:52:33 +0200 Subject: [PATCH 086/384] repo: Add `git_repository__cleanup` --- include/git2/sys/repository.h | 14 ++++++++++++++ src/cache.c | 3 +++ src/cache.h | 1 + src/repository.c | 19 ++++++++++++++----- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/include/git2/sys/repository.h b/include/git2/sys/repository.h index 34bc17516..ba3d65ae5 100644 --- a/include/git2/sys/repository.h +++ b/include/git2/sys/repository.h @@ -27,6 +27,20 @@ GIT_BEGIN_DECL */ GIT_EXTERN(int) git_repository_new(git_repository **out); + +/** + * Reset all the internal state in a repository. + * + * This will free all the mapped memory and internal objects + * of the repository and leave it in a "blank" state. + * + * There's no need to call this function directly unless you're + * trying to aggressively cleanup the repo before its + * deallocation. `git_repository_free` already performs this operation + * before deallocation the repo. + */ +GIT_EXTERN(void) git_repository__cleanup(git_repository *repo); + /** * Set the configuration file for this repository * diff --git a/src/cache.c b/src/cache.c index 88f643b35..be4b037a3 100644 --- a/src/cache.c +++ b/src/cache.c @@ -77,6 +77,9 @@ static void clear_cache(git_cache *cache) { git_cached_obj *evict = NULL; + if (kh_size(cache->map) == 0) + return; + kh_foreach_value(cache->map, evict, { git_cached_obj_decref(evict); }); diff --git a/src/cache.h b/src/cache.h index 1fb87dcea..16470e9c8 100644 --- a/src/cache.h +++ b/src/cache.h @@ -42,6 +42,7 @@ int git_cache_set_max_object_size(git_otype type, size_t size); int git_cache_init(git_cache *cache); void git_cache_free(git_cache *cache); +void git_cache_clear(git_cache *cache); void *git_cache_store_raw(git_cache *cache, git_odb_object *entry); void *git_cache_store_parsed(git_cache *cache, git_object *entry); diff --git a/src/repository.c b/src/repository.c index 59479dc92..2161aa697 100644 --- a/src/repository.c +++ b/src/repository.c @@ -86,19 +86,28 @@ static void set_index(git_repository *repo, git_index *index) } } -void git_repository_free(git_repository *repo) +void git_repository__cleanup(git_repository *repo) { - if (repo == NULL) - return; + assert(repo); - git_cache_free(&repo->objects); + git_cache_clear(&repo->objects); git_attr_cache_flush(repo); - git_submodule_config_free(repo); set_config(repo, NULL); set_index(repo, NULL); set_odb(repo, NULL); set_refdb(repo, NULL); +} + +void git_repository_free(git_repository *repo) +{ + if (repo == NULL) + return; + + git_repository__cleanup(repo); + + git_cache_free(&repo->objects); + git_submodule_config_free(repo); git__free(repo->path_repository); git__free(repo->workdir); From b4117e19b7a968f8e6b878d81c58a462093cf1b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 24 Apr 2013 20:09:42 +0200 Subject: [PATCH 087/384] docs: formatting fixes --- include/git2/attr.h | 2 +- include/git2/common.h | 52 +++++++++++++++++++++---------------- include/git2/cred_helpers.h | 4 +-- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/include/git2/attr.h b/include/git2/attr.h index dea44f0e3..f099245b0 100644 --- a/include/git2/attr.h +++ b/include/git2/attr.h @@ -228,7 +228,7 @@ GIT_EXTERN(void) git_attr_cache_flush( * function allows you to add others. For example, to add the default * macro, you would call: * - * git_attr_add_macro(repo, "binary", "-diff -crlf"); + * git_attr_add_macro(repo, "binary", "-diff -crlf"); */ GIT_EXTERN(int) git_attr_add_macro( git_repository *repo, diff --git a/include/git2/common.h b/include/git2/common.h index 6101e13bc..f9e9929ea 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -142,34 +142,42 @@ enum { * * Available options: * - * opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *): - * Get the maximum mmap window size + * * opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *): * - * opts(GIT_OPT_SET_MWINDOW_SIZE, size_t): - * Set the maximum mmap window size + * > Get the maximum mmap window size * - * opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t *): - * Get the maximum memory that will be mapped in total by the library + * * opts(GIT_OPT_SET_MWINDOW_SIZE, size_t): * - * opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t): - * Set the maximum amount of memory that can be mapped at any time + * > Set the maximum mmap window size + * + * * opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t *): + * + * > Get the maximum memory that will be mapped in total by the library + * + * * opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t): + * + * >Set the maximum amount of memory that can be mapped at any time * by the library * - * opts(GIT_OPT_GET_SEARCH_PATH, int level, char *out, size_t len) - * Get the search path for a given level of config data. "level" must - * be one of GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL, or - * GIT_CONFIG_LEVEL_XDG. The search path is written to the `out` - * buffer up to size `len`. Returns GIT_EBUFS if buffer is too small. + * * opts(GIT_OPT_GET_SEARCH_PATH, int level, char *out, size_t len) * - * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path) - * Set the search path for a level of config data. The search path - * applied to shared attributes and ignore files, too. - * - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR. - * Pass NULL to reset to the default (generally based on environment - * variables). Use magic path `$PATH` to include the old value - * of the path (if you want to prepend or append, for instance). - * - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL, - * or GIT_CONFIG_LEVEL_XDG. + * > Get the search path for a given level of config data. "level" must + * > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`, or + * > `GIT_CONFIG_LEVEL_XDG`. The search path is written to the `out` + * > buffer up to size `len`. Returns GIT_EBUFS if buffer is too small. + * + * * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path) + * + * > Set the search path for a level of config data. The search path + * > applied to shared attributes and ignore files, too. + * > + * > - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR. + * > Pass NULL to reset to the default (generally based on environment + * > variables). Use magic path `$PATH` to include the old value + * > of the path (if you want to prepend or append, for instance). + * > + * > - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL, + * > or GIT_CONFIG_LEVEL_XDG. * * @param option Option key * @param ... value to set the option diff --git a/include/git2/cred_helpers.h b/include/git2/cred_helpers.h index e3eb91d6c..5d93cf4dd 100644 --- a/include/git2/cred_helpers.h +++ b/include/git2/cred_helpers.h @@ -30,11 +30,11 @@ typedef struct git_cred_userpass_payload { /** * Stock callback usable as a git_cred_acquire_cb. This calls * git_cred_userpass_plaintext_new unless the protocol has not specified - * GIT_CREDTYPE_USERPASS_PLAINTEXT as an allowed type. + * `GIT_CREDTYPE_USERPASS_PLAINTEXT` as an allowed type. * * @param cred The newly created credential object. * @param url The resource for which we are demanding a credential. - * @param username_from_url The username that was embedded in a "user@host" + * @param user_from_url The username that was embedded in a "user@host" * remote url, or NULL if not included. * @param allowed_types A bitmask stating which cred types are OK to return. * @param payload The payload provided when specifying this callback. (This is From eb63fda2e24d007e31742587984a30e086249d43 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 25 Apr 2013 11:52:17 -0500 Subject: [PATCH 088/384] git_atomic_ssize for 64-bit atomics only on 64-bit platforms --- CMakeLists.txt | 9 +++++++++ src/cache.c | 13 +++++++------ src/cache.h | 6 +++--- src/thread-utils.h | 30 ++++++++++++++++++++++++++---- src/util.c | 6 +++--- 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bd25aacc..1831c8717 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -277,6 +277,15 @@ ELSE() ENDIF() FILE(GLOB SRC_GIT2 src/*.c src/transports/*.c src/xdiff/*.c) +# Determine architecture of the machine +IF (CMAKE_SIZEOF_VOID_P EQUAL 8) + ADD_DEFINITIONS(-DGIT_ARCH_64) +ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4) + ADD_DEFINITIONS(-DGIT_ARCH_32) +ELSE() + message(FATAL_ERROR "Unsupported architecture") +ENDIF() + # Compile and link libgit2 ADD_LIBRARY(git2 ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC}) TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES}) diff --git a/src/cache.c b/src/cache.c index be4b037a3..1360cc976 100644 --- a/src/cache.c +++ b/src/cache.c @@ -18,8 +18,8 @@ GIT__USE_OIDMAP bool git_cache__enabled = true; -int64_t git_cache__max_storage = (256 * 1024 * 1024); -git_atomic64 git_cache__current_storage = {0}; +ssize_t git_cache__max_storage = (256 * 1024 * 1024); +git_atomic_ssize git_cache__current_storage = {0}; static size_t git_cache__max_object_size[8] = { 0, /* GIT_OBJ__EXT1 */ @@ -85,7 +85,7 @@ static void clear_cache(git_cache *cache) }); kh_clear(oid, cache->map); - git_atomic64_add(&git_cache__current_storage, -cache->used_memory); + git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory); cache->used_memory = 0; } @@ -111,7 +111,8 @@ void git_cache_free(git_cache *cache) static void cache_evict_entries(git_cache *cache) { uint32_t seed = rand(); - int64_t evicted_memory = 0, evict_count = 8; + size_t evict_count = 8; + ssize_t evicted_memory = 0; /* do not infinite loop if there's not enough entries to evict */ if (evict_count > kh_size(cache->map)) { @@ -134,7 +135,7 @@ static void cache_evict_entries(git_cache *cache) } cache->used_memory -= evicted_memory; - git_atomic64_add(&git_cache__current_storage, -evicted_memory); + git_atomic_ssize_add(&git_cache__current_storage, -evicted_memory); } static bool cache_should_store(git_otype object_type, size_t object_size) @@ -195,7 +196,7 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) kh_val(cache->map, pos) = entry; git_cached_obj_incref(entry); cache->used_memory += entry->size; - git_atomic64_add(&git_cache__current_storage, (int64_t)entry->size); + git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size); } } /* found */ diff --git a/src/cache.h b/src/cache.h index 16470e9c8..53fbcf4e9 100644 --- a/src/cache.h +++ b/src/cache.h @@ -31,12 +31,12 @@ typedef struct { typedef struct { git_oidmap *map; git_mutex lock; - int64_t used_memory; + ssize_t used_memory; } git_cache; extern bool git_cache__enabled; -extern int64_t git_cache__max_storage; -extern git_atomic64 git_cache__current_storage; +extern ssize_t git_cache__max_storage; +extern git_atomic_ssize git_cache__current_storage; int git_cache_set_max_object_size(git_otype type, size_t size); diff --git a/src/thread-utils.h b/src/thread-utils.h index 28ecd297e..49b5f3b5e 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -18,6 +18,8 @@ typedef struct { #endif } git_atomic; +#ifdef GIT_ARCH_64 + typedef struct { #if defined(GIT_WIN32) __int64 val; @@ -26,6 +28,18 @@ typedef struct { #endif } git_atomic64; +typedef git_atomic64 git_atomic_ssize; + +#define git_atomic_ssize_add git_atomic64_add + +#else + +typedef git_atomic git_atomic_ssize; + +#define git_atomic_ssize_add git_atomic_add + +#endif + GIT_INLINE(void) git_atomic_set(git_atomic *a, int val) { a->val = val; @@ -68,7 +82,7 @@ GIT_INLINE(int) git_atomic_inc(git_atomic *a) GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend) { #if defined(GIT_WIN32) - return _InterlockedExchangeAdd(&a->val, addend); + return InterlockedExchangeAdd(&a->val, addend); #elif defined(__GNUC__) return __sync_add_and_fetch(&a->val, addend); #else @@ -101,10 +115,12 @@ GIT_INLINE(void *) git___compare_and_swap( return (foundval == oldval) ? oldval : newval; } -GIT_INLINE(int) git_atomic64_add(git_atomic64 *a, int64_t addend) +#ifdef GIT_ARCH_64 + +GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend) { #if defined(GIT_WIN32) - return _InterlockedExchangeAdd64(&a->val, addend); + return InterlockedExchangeAdd64(&a->val, addend); #elif defined(__GNUC__) return __sync_add_and_fetch(&a->val, addend); #else @@ -112,6 +128,8 @@ GIT_INLINE(int) git_atomic64_add(git_atomic64 *a, int64_t addend) #endif } +#endif + #else #define git_thread unsigned int @@ -161,7 +179,9 @@ GIT_INLINE(void *) git___compare_and_swap( return oldval; } -GIT_INLINE(int) git_atomic64_add(git_atomic64 *a, int64_t addend) +#ifdef GIT_ARCH_64 + +GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend) { a->val += addend; return a->val; @@ -169,6 +189,8 @@ GIT_INLINE(int) git_atomic64_add(git_atomic64 *a, int64_t addend) #endif +#endif + /* Atomically replace oldval with newval * @return oldval if it was replaced or newval if it was not */ diff --git a/src/util.c b/src/util.c index ce67c7e62..8c8bc1a6c 100644 --- a/src/util.c +++ b/src/util.c @@ -103,7 +103,7 @@ int git_libgit2_opts(int key, ...) } case GIT_OPT_SET_CACHE_MAX_SIZE: - git_cache__max_storage = va_arg(ap, int64_t); + git_cache__max_storage = va_arg(ap, ssize_t); break; case GIT_OPT_ENABLE_CACHING: @@ -111,8 +111,8 @@ int git_libgit2_opts(int key, ...) break; case GIT_OPT_GET_CACHED_MEMORY: - *(va_arg(ap, int64_t *)) = git_cache__current_storage.val; - *(va_arg(ap, int64_t *)) = git_cache__max_storage; + *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val; + *(va_arg(ap, ssize_t *)) = git_cache__max_storage; break; } From 528a4e24c6671d621847cf8e3121e3c56fe20d3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Apr 2013 14:16:45 +0200 Subject: [PATCH 089/384] Parse shorthand refspecs as valid Relax the ONELEVEL ref naming rules so the refspec parsing code can ask for 'master' to be considered valid. --- include/git2/refs.h | 7 +++++++ src/refs.c | 1 + src/refspec.c | 2 +- tests-clar/network/refspecs.c | 3 +++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/git2/refs.h b/include/git2/refs.h index 1ff0d4544..e1d425352 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -422,6 +422,13 @@ typedef enum { * (e.g., foo//bar but not foo/bar). */ GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1), + + /** + * Interpret the name as part of a refspec in shorthand form + * so the `ONELEVEL` naming rules aren't enforced and 'master' + * becomes a valid name. + */ + GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1 << 2), } git_reference_normalize_t; /** diff --git a/src/refs.c b/src/refs.c index 9c6684a5a..2faa4cb83 100644 --- a/src/refs.c +++ b/src/refs.c @@ -752,6 +752,7 @@ int git_reference__normalize_name( goto cleanup; if ((segments_count == 1 ) && + !(flags & GIT_REF_FORMAT_REFSPEC_SHORTHAND) && !(is_all_caps_and_underscore(name, (size_t)segment_len) || ((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name)))) goto cleanup; diff --git a/src/refspec.c b/src/refspec.c index fbdea9d93..256540819 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -60,7 +60,7 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) refspec->pattern = is_glob; refspec->src = git__strndup(lhs, llen); - flags = GIT_REF_FORMAT_ALLOW_ONELEVEL + flags = GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND | (is_glob ? GIT_REF_FORMAT_REFSPEC_PATTERN : 0); if (is_fetch) { diff --git a/tests-clar/network/refspecs.c b/tests-clar/network/refspecs.c index b3d80fb85..676a1fa99 100644 --- a/tests-clar/network/refspecs.c +++ b/tests-clar/network/refspecs.c @@ -81,4 +81,7 @@ void test_network_refspecs__parsing(void) assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*/for-linus:refs/remotes/mine/*", true); assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*/for-linus:refs/remotes/mine/*", true); + + assert_refspec(GIT_DIRECTION_FETCH, "master", true); + assert_refspec(GIT_DIRECTION_PUSH, "master", true); } From d84884571d04d609ed5f3508aecd9e24b84f47c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Apr 2013 16:26:55 +0200 Subject: [PATCH 090/384] remote: dwim the refspecs according to the remote's advertised refs As git allows you to store shorthand refspecs in the configuration, we need to do this ourselves. --- src/refspec.h | 1 + src/remote.c | 94 ++++++++++++++++++++++++++++--- tests-clar/network/remote/local.c | 41 ++++++++++++++ 3 files changed, 127 insertions(+), 9 deletions(-) diff --git a/src/refspec.h b/src/refspec.h index 29f4d5354..44d484c7b 100644 --- a/src/refspec.h +++ b/src/refspec.h @@ -17,6 +17,7 @@ struct git_refspec { unsigned int force :1, push : 1, pattern :1, + dwim :1, matching :1; }; diff --git a/src/remote.c b/src/remote.c index ffce2b6e2..1183137a6 100644 --- a/src/remote.c +++ b/src/remote.c @@ -632,28 +632,104 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur return 0; } +static int store_refs(git_remote_head *head, void *payload) +{ + git_vector *refs = (git_vector *)payload; + + return git_vector_insert(refs, head); +} + +static int dwim_refspecs(git_vector *refspecs, git_vector *refs) +{ + git_buf buf = GIT_BUF_INIT; + git_refspec *spec; + size_t i, j, pos; + git_remote_head key; + + const char* formatters[] = { + GIT_REFS_DIR "%s", + GIT_REFS_TAGS_DIR "%s", + GIT_REFS_HEADS_DIR "%s", + NULL + }; + + git_vector_foreach(refspecs, i, spec) { + if (spec->dwim) + continue; + + /* shorthand on the lhs */ + if (git__prefixcmp(spec->src, GIT_REFS_DIR)) { + for (j = 0; formatters[j]; j++) { + git_buf_clear(&buf); + if (git_buf_printf(&buf, formatters[j], spec->src) < 0) + return -1; + + key.name = (char *) git_buf_cstr(&buf); + if (!git_vector_search(&pos, refs, &key)) { + /* we found something to match the shorthand, set src to that */ + git__free(spec->src); + spec->src = git_buf_detach(&buf); + } + } + } + + if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) { + /* if it starts with "remotes" then we just prepend "refs/" */ + if (!git__prefixcmp(spec->dst, "remotes/")) { + git_buf_puts(&buf, GIT_REFS_DIR); + } else { + git_buf_puts(&buf, GIT_REFS_HEADS_DIR); + } + + if (git_buf_puts(&buf, spec->dst) < 0) + return -1; + + git__free(spec->dst); + spec->dst = git_buf_detach(&buf); + } + + spec->dwim = 1; + } + + return 0; +} + +static int remote_head_cmp(const void *_a, const void *_b) +{ + const git_remote_head *a = (git_remote_head *) _a; + const git_remote_head *b = (git_remote_head *) _b; + + return git__strcmp_cb(a->name, b->name); +} + int git_remote_download( git_remote *remote, git_transfer_progress_callback progress_cb, void *progress_payload) { int error; + git_vector refs; assert(remote); + if (git_vector_init(&refs, 16, remote_head_cmp) < 0) + return -1; + + if (git_remote_ls(remote, store_refs, &refs) < 0) { + return -1; + } + + error = dwim_refspecs(&remote->refspecs, &refs); + git_vector_free(&refs); + if (error < 0) + return -1; + if ((error = git_fetch_negotiate(remote)) < 0) return error; return git_fetch_download_pack(remote, progress_cb, progress_payload); } -static int update_tips_callback(git_remote_head *head, void *payload) -{ - git_vector *refs = (git_vector *)payload; - - return git_vector_insert(refs, head); -} - static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src) { unsigned int i; @@ -814,7 +890,7 @@ static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vecto if (!git_reference_is_valid_name(head->name)) continue; - if (git_refspec_src_matches(spec, head->name)) { + if (git_refspec_src_matches(spec, head->name) && spec->dst) { if (git_refspec_transform_r(&refname, spec, head->name) < 0) goto on_error; } else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { @@ -887,7 +963,7 @@ int git_remote_update_tips(git_remote *remote) if (git_vector_init(&refs, 16, NULL) < 0) return -1; - if (git_remote_ls(remote, update_tips_callback, &refs) < 0) + if (git_remote_ls(remote, store_refs, &refs) < 0) goto on_error; git_vector_foreach(&remote->refspecs, i, spec) { diff --git a/tests-clar/network/remote/local.c b/tests-clar/network/remote/local.c index 7e847e654..74ef63dc9 100644 --- a/tests-clar/network/remote/local.c +++ b/tests-clar/network/remote/local.c @@ -100,3 +100,44 @@ void test_network_remote_local__nested_tags_are_completely_peeled(void) cl_git_pass(git_remote_ls(remote, &ensure_peeled__cb, NULL)); } + +void test_network_remote_local__shorthand_fetch_refspec0(void) +{ + const char *refspec = "master:remotes/sloppy/master"; + const char *refspec2 = "master:boh/sloppy/master"; + + git_reference *ref; + + connect_to_local_repository(cl_fixture("testrepo.git")); + cl_git_pass(git_remote_add_fetch(remote, refspec)); + cl_git_pass(git_remote_add_fetch(remote, refspec2)); + + cl_git_pass(git_remote_download(remote, NULL, NULL)); + cl_git_pass(git_remote_update_tips(remote)); + + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master")); + git_reference_free(ref); + + cl_git_pass(git_reference_lookup(&ref, repo, "refs/heads/boh/sloppy/master")); + git_reference_free(ref); +} + +void test_network_remote_local__shorthand_fetch_refspec1(void) +{ + const char *refspec = "master"; + const char *refspec2 = "hard_tag"; + + git_reference *ref; + + connect_to_local_repository(cl_fixture("testrepo.git")); + git_remote_clear_refspecs(remote); + cl_git_pass(git_remote_add_fetch(remote, refspec)); + cl_git_pass(git_remote_add_fetch(remote, refspec2)); + + cl_git_pass(git_remote_download(remote, NULL, NULL)); + cl_git_pass(git_remote_update_tips(remote)); + + cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master")); + + cl_git_fail(git_reference_lookup(&ref, repo, "refs/tags/hard_tag")); +} From 51e4da6d8aa4a3a961d49197b004dbf7ccee8612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 29 Apr 2013 01:49:40 +0200 Subject: [PATCH 091/384] push: don't send a packfile when only issuing delete commands For update and create commands where all the objects are known to exist in the remote, we must send an empty packfile. However, if all we issue are delete commands, no packfile must be sent. Take this into consideration for push. --- src/transports/smart_protocol.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 90851980c..a5ad1e422 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -807,13 +807,13 @@ int git_smart__push(git_transport *transport, git_push *push) transport_smart *t = (transport_smart *)transport; git_smart_subtransport_stream *s; git_buf pktline = GIT_BUF_INIT; - int error = -1; + int error = -1, need_pack = 0; + push_spec *spec; + unsigned int i; #ifdef PUSH_DEBUG { git_remote_head *head; - push_spec *spec; - unsigned int i; char hex[41]; hex[40] = '\0'; git_vector_foreach(&push->remote->refs, i, head) { @@ -831,10 +831,23 @@ int git_smart__push(git_transport *transport, git_push *push) } #endif + /* + * Figure out if we need to send a packfile; which is in all + * cases except when we only send delete commands + */ + git_vector_foreach(&push->specs, i, spec) { + if (spec->lref) { + need_pack = 1; + break; + } + } + if (git_smart__get_push_stream(t, &s) < 0 || gen_pktline(&pktline, push) < 0 || - s->write(s, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0 || - git_packbuilder_foreach(push->pb, &stream_thunk, s) < 0) + s->write(s, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0) + goto on_error; + + if (need_pack && git_packbuilder_foreach(push->pb, &stream_thunk, s) < 0) goto on_error; /* If we sent nothing or the server doesn't support report-status, then From fb42a22e2fedeca8a5d0fa4a32653285374e689d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 29 Apr 2013 02:15:51 +0200 Subject: [PATCH 092/384] travis: test push Create a test repository in the VM and set up git-daemon so we can use it to test the push code. --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index ad1172dfa..0d5746f2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,10 @@ install: # Run the Build script script: + - mkdir _temp + - git init --bare _temp/test.git + - git daemon --listen=localhost --export-all --enable=receive-pack --base-path=_temp _temp 2>/dev/null & + - export GITTEST_REMOTE_URL="git://localhost/test.git" - mkdir _build - cd _build - cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS From 0c72248b9171acee7480a77edee89fa20fabdae8 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 29 Apr 2013 07:34:13 -0700 Subject: [PATCH 093/384] Introduce git_oid_compare, an exported oid cmp --- include/git2/oid.h | 9 +++++++++ src/oid.c | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/include/git2/oid.h b/include/git2/oid.h index 862f4b202..c35acdcdc 100644 --- a/include/git2/oid.h +++ b/include/git2/oid.h @@ -138,6 +138,15 @@ GIT_EXTERN(char *) git_oid_tostr(char *out, size_t n, const git_oid *id); */ GIT_EXTERN(void) git_oid_cpy(git_oid *out, const git_oid *src); +/** + * Compare two oid structures. + * + * @param a first oid structure. + * @param b second oid structure. + * @return <0, 0, >0 if a < b, a == b, a > b. + */ +GIT_EXTERN(int) git_oid_compare(const git_oid *oid_a, const git_oid *oid_b); + /** * Compare two oid structures. * diff --git a/src/oid.c b/src/oid.c index ab69eeb17..59c1546d7 100644 --- a/src/oid.c +++ b/src/oid.c @@ -166,6 +166,11 @@ void git_oid_cpy(git_oid *out, const git_oid *src) memcpy(out->id, src->id, sizeof(out->id)); } +int git_oid_compare(const git_oid *oid_a, const git_oid *oid_b) +{ + return git_oid_cmp(oid_a, oid_b); +} + int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len) { const unsigned char *a = oid_a->id; From 8564a0224abe09beaacb2d2e7a54b16f8fcea7d1 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 29 Apr 2013 08:51:24 -0700 Subject: [PATCH 094/384] Fix fragile git_oid_ncmp git_oid_ncmp was making some assumptions about the length of the data - this shifts the check to the top of the loop so it will work more robustly, limits the max, and adds some tests to verify the functionality. --- src/oid.c | 7 +++++-- tests-clar/core/oid.c | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/oid.c b/src/oid.c index 59c1546d7..4b6699009 100644 --- a/src/oid.c +++ b/src/oid.c @@ -176,13 +176,16 @@ int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len) const unsigned char *a = oid_a->id; const unsigned char *b = oid_b->id; - do { + if (len > GIT_OID_HEXSZ) + len = GIT_OID_HEXSZ; + + while (len > 1) { if (*a != *b) return 1; a++; b++; len -= 2; - } while (len > 1); + }; if (len) if ((*a ^ *b) & 0xf0) diff --git a/tests-clar/core/oid.c b/tests-clar/core/oid.c index 08791cce6..d863a3e85 100644 --- a/tests-clar/core/oid.c +++ b/tests-clar/core/oid.c @@ -27,4 +27,22 @@ void test_core_oid__streq(void) cl_assert(git_oid_streq(&idp, "deadbeef") == -1); cl_assert(git_oid_streq(&idp, "I'm not an oid.... :)") == -1); + +void test_core_oid__ncmp(void) +{ + cl_assert(!git_oid_ncmp(&id, &idp, 0)); + cl_assert(!git_oid_ncmp(&id, &idp, 1)); + cl_assert(!git_oid_ncmp(&id, &idp, 2)); + cl_assert(!git_oid_ncmp(&id, &idp, 17)); + cl_assert(!git_oid_ncmp(&id, &idp, 18)); + cl_assert(git_oid_ncmp(&id, &idp, 19)); + cl_assert(git_oid_ncmp(&id, &idp, 40)); + cl_assert(git_oid_ncmp(&id, &idp, 41)); + cl_assert(git_oid_ncmp(&id, &idp, 42)); + + cl_assert(!git_oid_ncmp(&id, &id, 0)); + cl_assert(!git_oid_ncmp(&id, &id, 1)); + cl_assert(!git_oid_ncmp(&id, &id, 39)); + cl_assert(!git_oid_ncmp(&id, &id, 40)); + cl_assert(!git_oid_ncmp(&id, &id, 41)); } From aa8f010120577e61715f3ae1286a03055815f9c3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 29 Apr 2013 08:59:46 -0700 Subject: [PATCH 095/384] Add git_oid_strcmp and use it for git_oid_streq Add a new git_oid_strcmp that compares a string OID with a hex oid for sort order, and then reimplement git_oid_streq using it. This actually should speed up git_oid_streq because it only reads as far into the string as it needs to, whereas previously it would convert the whole string into an OID and then use git_oid_cmp. --- include/git2/oid.h | 10 ++++++++++ src/oid.c | 27 ++++++++++++++++++++++----- tests-clar/core/oid.c | 40 +++++++++++++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/include/git2/oid.h b/include/git2/oid.h index c35acdcdc..8d93e79cf 100644 --- a/include/git2/oid.h +++ b/include/git2/oid.h @@ -201,6 +201,16 @@ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, size_t len); */ GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str); +/** + * Compare an oid to an hex formatted object id. + * + * @param id oid structure. + * @param str input hex string of an object id. + * @return -1 if str is not valid, <0 if id sorts before str, + * 0 if id matches str, >0 if id sorts after str. + */ +GIT_EXTERN(int) git_oid_strcmp(const git_oid *id, const char *str); + /** * Check is an oid is all zeros. * diff --git a/src/oid.c b/src/oid.c index 4b6699009..c7ce6ee50 100644 --- a/src/oid.c +++ b/src/oid.c @@ -194,14 +194,31 @@ int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len) return 0; } -int git_oid_streq(const git_oid *a, const char *str) +int git_oid_strcmp(const git_oid *oid_a, const char *str) { - git_oid id; + const unsigned char *a = oid_a->id; + unsigned char strval; + int hexval; - if (git_oid_fromstr(&id, str) < 0) - return -1; + for (a = oid_a->id; *str && (a - oid_a->id) < GIT_OID_RAWSZ; ++a) { + if ((hexval = git__fromhex(*str++)) < 0) + return -1; + strval = hexval << 4; + if (*str) { + if ((hexval = git__fromhex(*str++)) < 0) + return -1; + strval |= hexval; + } + if (*a != strval) + return (*a - strval); + } - return git_oid_cmp(a, &id) == 0 ? 0 : -1; + return 0; +} + +int git_oid_streq(const git_oid *oid_a, const char *str) +{ + return git_oid_strcmp(oid_a, str) == 0 ? 0 : -1; } int git_oid_iszero(const git_oid *oid_a) diff --git a/tests-clar/core/oid.c b/tests-clar/core/oid.c index d863a3e85..7ee6fb67d 100644 --- a/tests-clar/core/oid.c +++ b/tests-clar/core/oid.c @@ -16,17 +16,39 @@ void test_core_oid__initialize(void) void test_core_oid__streq(void) { - cl_assert(git_oid_streq(&id, str_oid) == 0); - cl_assert(git_oid_streq(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") == -1); + cl_assert_equal_i(0, git_oid_streq(&id, str_oid)); + cl_assert_equal_i(-1, git_oid_streq(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - cl_assert(git_oid_streq(&id, "deadbeef") == -1); - cl_assert(git_oid_streq(&id, "I'm not an oid.... :)") == -1); - - cl_assert(git_oid_streq(&idp, "ae90f12eea699729ed0000000000000000000000") == 0); - cl_assert(git_oid_streq(&idp, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") == -1); + cl_assert_equal_i(-1, git_oid_streq(&id, "deadbeef")); + cl_assert_equal_i(-1, git_oid_streq(&id, "I'm not an oid.... :)")); - cl_assert(git_oid_streq(&idp, "deadbeef") == -1); - cl_assert(git_oid_streq(&idp, "I'm not an oid.... :)") == -1); + cl_assert_equal_i(0, git_oid_streq(&idp, "ae90f12eea699729ed0000000000000000000000")); + cl_assert_equal_i(0, git_oid_streq(&idp, "ae90f12eea699729ed")); + cl_assert_equal_i(-1, git_oid_streq(&idp, "ae90f12eea699729ed1")); + cl_assert_equal_i(-1, git_oid_streq(&idp, "ae90f12eea699729ec")); + cl_assert_equal_i(-1, git_oid_streq(&idp, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); + + cl_assert_equal_i(-1, git_oid_streq(&idp, "deadbeef")); + cl_assert_equal_i(-1, git_oid_streq(&idp, "I'm not an oid.... :)")); +} + +void test_core_oid__strcmp(void) +{ + cl_assert_equal_i(0, git_oid_strcmp(&id, str_oid)); + cl_assert(git_oid_strcmp(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0); + + cl_assert(git_oid_strcmp(&id, "deadbeef") < 0); + cl_assert_equal_i(-1, git_oid_strcmp(&id, "I'm not an oid.... :)")); + + cl_assert_equal_i(0, git_oid_strcmp(&idp, "ae90f12eea699729ed0000000000000000000000")); + cl_assert_equal_i(0, git_oid_strcmp(&idp, "ae90f12eea699729ed")); + cl_assert(git_oid_strcmp(&idp, "ae90f12eea699729ed1") < 0); + cl_assert(git_oid_strcmp(&idp, "ae90f12eea699729ec") > 0); + cl_assert(git_oid_strcmp(&idp, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0); + + cl_assert(git_oid_strcmp(&idp, "deadbeef") < 0); + cl_assert_equal_i(-1, git_oid_strcmp(&idp, "I'm not an oid.... :)")); +} void test_core_oid__ncmp(void) { From ac1d85cf11ceb8bf9c9600ce9c29d5d037220cfb Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 29 Apr 2013 11:00:05 -0500 Subject: [PATCH 096/384] cmake 2.6 parser bug workaround --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1831c8717..9f6b06bf1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -293,7 +293,7 @@ TARGET_OS_LIBRARIES(git2) # Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240) # Win64+MSVC+static libs = linker error -IF(MSVC AND NOT BUILD_SHARED_LIBS AND (${CMAKE_SIZEOF_VOID_P} MATCHES "8") ) +IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS) SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64") ENDIF() From c8a4e8a5f6acc94c46c8a7ea2cf3f9d67b50e07a Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 29 Apr 2013 11:14:56 -0500 Subject: [PATCH 097/384] don't use uninitialized struct stat in win32 --- src/odb.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/odb.c b/src/odb.c index 53630dddc..64e1232f5 100644 --- a/src/odb.c +++ b/src/odb.c @@ -445,31 +445,39 @@ static int add_default_backends( { size_t i; struct stat st; + ino_t inode; git_odb_backend *loose, *packed; /* TODO: inodes are not really relevant on Win32, so we need to find * a cross-platform workaround for this */ -#ifndef GIT_WIN32 +#ifdef GIT_WIN32 + GIT_UNUSED(i); + GIT_UNUSED(st); + + inode = 0; +#else if (p_stat(objects_dir, &st) < 0) { giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir); return -1; } + inode = st.st_ino; + for (i = 0; i < db->backends.length; ++i) { backend_internal *backend = git_vector_get(&db->backends, i); - if (backend->disk_inode == st.st_ino) + if (backend->disk_inode == inode) return 0; } #endif /* add the loose object backend */ if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 || - add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, st.st_ino) < 0) + add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0) return -1; /* add the packed file backend */ if (git_odb_backend_pack(&packed, objects_dir) < 0 || - add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates, st.st_ino) < 0) + add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates, inode) < 0) return -1; return load_alternates(db, objects_dir, alternate_depth); From b7f167da29effa125663b143d3daf79a6ad88d2e Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 29 Apr 2013 13:52:12 -0700 Subject: [PATCH 098/384] Make git_oid_cmp public and add git_oid__cmp --- include/git2/oid.h | 14 +------------- src/attr.c | 2 +- src/checkout.c | 6 +++--- src/clone.c | 6 +++--- src/diff.c | 10 +++++----- src/diff_output.c | 4 ++-- src/diff_tform.c | 2 +- src/index.c | 2 +- src/indexer.c | 4 ++-- src/odb.c | 2 +- src/oid.c | 4 ++-- src/pack.c | 6 +++--- src/push.c | 2 +- src/refs.c | 2 +- src/refs.h | 1 + src/remote.c | 2 +- src/transports/local.c | 2 +- 17 files changed, 30 insertions(+), 41 deletions(-) diff --git a/include/git2/oid.h b/include/git2/oid.h index 8d93e79cf..288e90bc8 100644 --- a/include/git2/oid.h +++ b/include/git2/oid.h @@ -154,19 +154,7 @@ GIT_EXTERN(int) git_oid_compare(const git_oid *oid_a, const git_oid *oid_b); * @param b second oid structure. * @return <0, 0, >0 if a < b, a == b, a > b. */ -GIT_INLINE(int) git_oid_cmp(const git_oid *a, const git_oid *b) -{ - const unsigned char *sha1 = a->id; - const unsigned char *sha2 = b->id; - int i; - - for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) { - if (*sha1 != *sha2) - return *sha1 - *sha2; - } - - return 0; -} +GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b); /** * Compare two oid structures for equality diff --git a/src/attr.c b/src/attr.c index 979fecc14..6dd2c7e2f 100644 --- a/src/attr.c +++ b/src/attr.c @@ -312,7 +312,7 @@ static int load_attr_blob_from_index( entry = git_index_get_byindex(index, pos); - if (old_oid && git_oid_cmp(old_oid, &entry->oid) == 0) + if (old_oid && git_oid__cmp(old_oid, &entry->oid) == 0) return GIT_ENOTFOUND; if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0) diff --git a/src/checkout.c b/src/checkout.c index e29fccd05..96e15093c 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -138,7 +138,7 @@ static bool checkout_is_workdir_modified( if (!sm_oid) return false; - return (git_oid_cmp(&baseitem->oid, sm_oid) != 0); + return (git_oid__cmp(&baseitem->oid, sm_oid) != 0); } /* Look at the cache to decide if the workdir is modified. If not, @@ -149,7 +149,7 @@ static bool checkout_is_workdir_modified( if (wditem->mtime.seconds == ie->mtime.seconds && wditem->mtime.nanoseconds == ie->mtime.nanoseconds && wditem->file_size == ie->file_size) - return (git_oid_cmp(&baseitem->oid, &ie->oid) != 0); + return (git_oid__cmp(&baseitem->oid, &ie->oid) != 0); } /* depending on where base is coming from, we may or may not know @@ -163,7 +163,7 @@ static bool checkout_is_workdir_modified( wditem->file_size, &oid) < 0) return false; - return (git_oid_cmp(&baseitem->oid, &oid) != 0); + return (git_oid__cmp(&baseitem->oid, &oid) != 0); } #define CHECKOUT_ACTION_IF(FLAG,YES,NO) \ diff --git a/src/clone.c b/src/clone.c index 0665576e3..aeb7bbf5c 100644 --- a/src/clone.c +++ b/src/clone.c @@ -132,14 +132,14 @@ static int reference_matches_remote_head( return 0; } - if (git_oid_cmp(&head_info->remote_head_oid, &oid) == 0) { + if (git_oid__cmp(&head_info->remote_head_oid, &oid) == 0) { /* Determine the local reference name from the remote tracking one */ if (git_refspec_transform_l( - &head_info->branchname, + &head_info->branchname, head_info->refspec, reference_name) < 0) return -1; - + if (git_buf_len(&head_info->branchname) > 0) { if (git_buf_sets( &head_info->branchname, diff --git a/src/diff.c b/src/diff.c index 881173cde..6612abf06 100644 --- a/src/diff.c +++ b/src/diff.c @@ -196,21 +196,21 @@ static git_diff_delta *diff_delta__last_for_item( switch (delta->status) { case GIT_DELTA_UNMODIFIED: case GIT_DELTA_DELETED: - if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0) + if (git_oid__cmp(&delta->old_file.oid, &item->oid) == 0) return delta; break; case GIT_DELTA_ADDED: - if (git_oid_cmp(&delta->new_file.oid, &item->oid) == 0) + if (git_oid__cmp(&delta->new_file.oid, &item->oid) == 0) return delta; break; case GIT_DELTA_UNTRACKED: if (diff->strcomp(delta->new_file.path, item->path) == 0 && - git_oid_cmp(&delta->new_file.oid, &item->oid) == 0) + git_oid__cmp(&delta->new_file.oid, &item->oid) == 0) return delta; break; case GIT_DELTA_MODIFIED: - if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0 || - git_oid_cmp(&delta->new_file.oid, &item->oid) == 0) + if (git_oid__cmp(&delta->old_file.oid, &item->oid) == 0 || + git_oid__cmp(&delta->new_file.oid, &item->oid) == 0) return delta; break; default: diff --git a/src/diff_output.c b/src/diff_output.c index b8bb73bf7..4ce01bc62 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -660,7 +660,7 @@ static int diff_patch_load( */ if (check_if_unmodified && delta->old_file.mode == delta->new_file.mode && - !git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid)) + !git_oid__cmp(&delta->old_file.oid, &delta->new_file.oid)) { delta->status = GIT_DELTA_UNMODIFIED; @@ -1388,7 +1388,7 @@ static int diff_single_apply(diff_single_data *data) (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) : (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED); - if (git_oid_cmp(&delta->new_file.oid, &delta->old_file.oid) == 0) + if (git_oid__cmp(&delta->new_file.oid, &delta->old_file.oid) == 0) delta->status = GIT_DELTA_UNMODIFIED; if ((error = diff_delta_is_binary_by_content( diff --git a/src/diff_tform.c b/src/diff_tform.c index efcb19d95..5c1a86cb9 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -429,7 +429,7 @@ static int similarity_measure( if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode)) return 0; - if (git_oid_cmp(&a_file->oid, &b_file->oid) == 0) + if (git_oid__cmp(&a_file->oid, &b_file->oid) == 0) return 100; /* update signature cache if needed */ diff --git a/src/index.c b/src/index.c index d8ca78e52..2e2d373b5 100644 --- a/src/index.c +++ b/src/index.c @@ -1411,7 +1411,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) /* 160-bit SHA-1 over the content of the index file before this checksum. */ git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer); - if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0) + if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0) return index_error_invalid("calculated checksum does not match expected"); #undef seek_forward diff --git a/src/indexer.c b/src/indexer.c index 606771927..91b7ba5d9 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -9,7 +9,6 @@ #include "git2/indexer.h" #include "git2/object.h" -#include "git2/oid.h" #include "common.h" #include "pack.h" @@ -17,6 +16,7 @@ #include "posix.h" #include "pack.h" #include "filebuf.h" +#include "oid.h" #include "oidmap.h" #define UINT31_MAX (0x7FFFFFFF) @@ -103,7 +103,7 @@ static int objects_cmp(const void *a, const void *b) const struct entry *entrya = a; const struct entry *entryb = b; - return git_oid_cmp(&entrya->oid, &entryb->oid); + return git_oid__cmp(&entrya->oid, &entryb->oid); } int git_indexer_stream_new( diff --git a/src/odb.c b/src/odb.c index 53630dddc..2574c6789 100644 --- a/src/odb.c +++ b/src/odb.c @@ -764,7 +764,7 @@ attempt_lookup: git__free(data); data = raw.data; - if (found && git_oid_cmp(&full_oid, &found_full_oid)) + if (found && git_oid__cmp(&full_oid, &found_full_oid)) return git_odb__error_ambiguous("multiple matches for prefix"); found_full_oid = full_oid; diff --git a/src/oid.c b/src/oid.c index c7ce6ee50..e74640c57 100644 --- a/src/oid.c +++ b/src/oid.c @@ -166,9 +166,9 @@ void git_oid_cpy(git_oid *out, const git_oid *src) memcpy(out->id, src->id, sizeof(out->id)); } -int git_oid_compare(const git_oid *oid_a, const git_oid *oid_b) +int git_oid_cmp(const git_oid *a, const git_oid *b) { - return git_oid_cmp(oid_a, oid_b); + return git_oid__cmp(a, b); } int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len) diff --git a/src/pack.c b/src/pack.c index 33cdf760a..f8b621ef8 100644 --- a/src/pack.c +++ b/src/pack.c @@ -12,8 +12,8 @@ #include "sha1_lookup.h" #include "mwindow.h" #include "fileops.h" +#include "oid.h" -#include "git2/oid.h" #include static int packfile_open(struct git_pack_file *p); @@ -875,7 +875,7 @@ static int packfile_open(struct git_pack_file *p) idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40; - if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) == 0) + if (git_oid__cmp(&sha1, (git_oid *)idx_sha1) == 0) return 0; cleanup: @@ -1139,7 +1139,7 @@ int git_pack_entry_find( if (len == GIT_OID_HEXSZ && p->num_bad_objects) { unsigned i; for (i = 0; i < p->num_bad_objects; i++) - if (git_oid_cmp(short_oid, &p->bad_object_sha1[i]) == 0) + if (git_oid__cmp(short_oid, &p->bad_object_sha1[i]) == 0) return packfile_error("bad object found in packfile"); } diff --git a/src/push.c b/src/push.c index b6be1a4e1..9b1e78c8e 100644 --- a/src/push.c +++ b/src/push.c @@ -376,7 +376,7 @@ static int queue_differences( 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)) + if (!git_oid__cmp(&b_entry->oid, &d_entry->oid)) goto loop; cmp = strcmp(b_entry->filename, d_entry->filename); diff --git a/src/refs.c b/src/refs.c index 9c6684a5a..f3a504147 100644 --- a/src/refs.c +++ b/src/refs.c @@ -831,7 +831,7 @@ int git_reference_cmp(git_reference *ref1, git_reference *ref2) if (type1 == GIT_REF_SYMBOLIC) return strcmp(ref1->target.symbolic, ref2->target.symbolic); - return git_oid_cmp(&ref1->target.oid, &ref2->target.oid); + return git_oid__cmp(&ref1->target.oid, &ref2->target.oid); } static int reference__update_terminal( diff --git a/src/refs.h b/src/refs.h index 97d4d2eb5..908e86f29 100644 --- a/src/refs.h +++ b/src/refs.h @@ -13,6 +13,7 @@ #include "git2/refdb.h" #include "strmap.h" #include "buffer.h" +#include "oid.h" #define GIT_REFS_DIR "refs/" #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/" diff --git a/src/remote.c b/src/remote.c index ffce2b6e2..306bc7356 100644 --- a/src/remote.c +++ b/src/remote.c @@ -845,7 +845,7 @@ static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vecto if (error == GIT_ENOTFOUND) memset(&old, 0, GIT_OID_RAWSZ); - if (!git_oid_cmp(&old, &head->oid)) + if (!git_oid__cmp(&old, &head->oid)) continue; /* In autotag mode, don't overwrite any locally-existing tags */ diff --git a/src/transports/local.c b/src/transports/local.c index 8af970eac..8b4d50c14 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -282,7 +282,7 @@ static int local_push_copy_object( odb_obj_size) < 0 || odb_stream->finalize_write(&remote_odb_obj_oid, odb_stream) < 0) { error = -1; - } else if (git_oid_cmp(&obj->id, &remote_odb_obj_oid) != 0) { + } else if (git_oid__cmp(&obj->id, &remote_odb_obj_oid) != 0) { giterr_set(GITERR_ODB, "Error when writing object to remote odb " "during local push operation. Remote odb object oid does not " "match local oid."); From d77611022c4a43d5e67cf52ce2bc2b11ee5bcdc0 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 29 Apr 2013 14:22:06 -0700 Subject: [PATCH 099/384] Standardize cast versions of git_object accessors This removes the GIT_INLINE versions of the simple git_object accessors and standardizes them with a helper macro in src/object.h to build the function bodies. --- include/git2/blob.h | 28 +++++++++++----------------- include/git2/tag.h | 31 +++++++++++++------------------ include/git2/tree.h | 20 +++++--------------- src/blob.c | 2 ++ src/object.h | 12 ++++++++++++ src/tag.c | 7 ++----- src/tree.c | 12 ++---------- 7 files changed, 47 insertions(+), 65 deletions(-) diff --git a/include/git2/blob.h b/include/git2/blob.h index 0a2aa9d36..8fca48966 100644 --- a/include/git2/blob.h +++ b/include/git2/blob.h @@ -29,10 +29,7 @@ GIT_BEGIN_DECL * @param id identity of the blob to locate. * @return 0 or an error code */ -GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id) -{ - return git_object_lookup((git_object **)blob, repo, id, GIT_OBJ_BLOB); -} +GIT_EXTERN(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id); /** * Lookup a blob object from a repository, @@ -46,10 +43,7 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git * @param len the length of the short identifier * @return 0 or an error code */ -GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len) -{ - return git_object_lookup_prefix((git_object **)blob, repo, id, len, GIT_OBJ_BLOB); -} +GIT_EXTERN(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len); /** * Close an open blob @@ -62,11 +56,7 @@ GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, co * * @param blob the blob to close */ - -GIT_INLINE(void) git_blob_free(git_blob *blob) -{ - git_object_free((git_object *) blob); -} +GIT_EXTERN(void) git_blob_free(git_blob *blob); /** * Get the id of a blob. @@ -74,11 +64,15 @@ GIT_INLINE(void) git_blob_free(git_blob *blob) * @param blob a previously loaded blob. * @return SHA1 hash for this blob. */ -GIT_INLINE(const git_oid *) git_blob_id(const git_blob *blob) -{ - return git_object_id((const git_object *)blob); -} +GIT_EXTERN(const git_oid *) git_blob_id(const git_blob *blob); +/** + * Get the repository that contains the blob. + * + * @param blob A previously loaded blob. + * @return Repository that contains this blob. + */ +GIT_EXTERN(git_repository *) git_blob_owner(const git_blob *blob); /** * Get a read-only buffer with the raw content of a blob. diff --git a/include/git2/tag.h b/include/git2/tag.h index 84c954c27..469b1d72b 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -30,12 +30,8 @@ GIT_BEGIN_DECL * @param id identity of the tag to locate. * @return 0 or an error code */ -GIT_INLINE(int) git_tag_lookup( - git_tag **out, git_repository *repo, const git_oid *id) -{ - return git_object_lookup( - (git_object **)out, repo, id, (git_otype)GIT_OBJ_TAG); -} +GIT_EXTERN(int) git_tag_lookup( + git_tag **out, git_repository *repo, const git_oid *id); /** * Lookup a tag object from the repository, @@ -49,12 +45,8 @@ GIT_INLINE(int) git_tag_lookup( * @param len the length of the short identifier * @return 0 or an error code */ -GIT_INLINE(int) git_tag_lookup_prefix( - git_tag **out, git_repository *repo, const git_oid *id, size_t len) -{ - return git_object_lookup_prefix( - (git_object **)out, repo, id, len, (git_otype)GIT_OBJ_TAG); -} +GIT_EXTERN(int) git_tag_lookup_prefix( + git_tag **out, git_repository *repo, const git_oid *id, size_t len); /** * Close an open tag @@ -66,12 +58,7 @@ GIT_INLINE(int) git_tag_lookup_prefix( * * @param tag the tag to close */ - -GIT_INLINE(void) git_tag_free(git_tag *tag) -{ - git_object_free((git_object *)tag); -} - +GIT_EXTERN(void) git_tag_free(git_tag *tag); /** * Get the id of a tag. @@ -81,6 +68,14 @@ GIT_INLINE(void) git_tag_free(git_tag *tag) */ GIT_EXTERN(const git_oid *) git_tag_id(const git_tag *tag); +/** + * Get the repository that contains the tag. + * + * @param tag A previously loaded tag. + * @return Repository that contains this tag. + */ +GIT_EXTERN(git_repository *) git_tag_owner(const git_tag *tag); + /** * Get the tagged object of a tag * diff --git a/include/git2/tree.h b/include/git2/tree.h index 73bfc86f4..6ad722048 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -29,11 +29,8 @@ GIT_BEGIN_DECL * @param id Identity of the tree to locate. * @return 0 or an error code */ -GIT_INLINE(int) git_tree_lookup( - git_tree **out, git_repository *repo, const git_oid *id) -{ - return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TREE); -} +GIT_EXTERN(int) git_tree_lookup( + git_tree **out, git_repository *repo, const git_oid *id); /** * Lookup a tree object from the repository, @@ -47,15 +44,11 @@ GIT_INLINE(int) git_tree_lookup( * @param len the length of the short identifier * @return 0 or an error code */ -GIT_INLINE(int) git_tree_lookup_prefix( +GIT_EXTERN(int) git_tree_lookup_prefix( git_tree **out, git_repository *repo, const git_oid *id, - size_t len) -{ - return git_object_lookup_prefix( - (git_object **)out, repo, id, len, GIT_OBJ_TREE); -} + size_t len); /** * Close an open tree @@ -67,10 +60,7 @@ GIT_INLINE(int) git_tree_lookup_prefix( * * @param tree The tree to close */ -GIT_INLINE(void) git_tree_free(git_tree *tree) -{ - git_object_free((git_object *)tree); -} +GIT_EXTERN(void) git_tree_free(git_tree *tree); /** * Get the id of a tree. diff --git a/src/blob.c b/src/blob.c index a68c4cc3e..d656576b8 100644 --- a/src/blob.c +++ b/src/blob.c @@ -15,6 +15,8 @@ #include "filter.h" #include "buf_text.h" +GIT_OBJ_WRAPPER(git_blob, GIT_OBJ_BLOB) + const void *git_blob_rawcontent(const git_blob *blob) { assert(blob); diff --git a/src/object.h b/src/object.h index d187c55b7..906d40736 100644 --- a/src/object.h +++ b/src/object.h @@ -28,4 +28,16 @@ int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid); +#define GIT_OBJ_WRAPPER(TYPE,OBJTYPE) \ + int TYPE##_lookup(TYPE **out, git_repository *repo, const git_oid *id) { \ + return git_object_lookup((git_object **)out, repo, id, OBJTYPE); } \ + int TYPE##_lookup_prefix(TYPE **out, git_repository *repo, const git_oid *id, size_t len) { \ + return git_object_lookup_prefix((git_object **)out, repo, id, len, OBJTYPE); } \ + void TYPE##_free(TYPE *obj) { \ + git_object_free((git_object *)obj); } \ + const git_oid *TYPE##_id(const TYPE *obj) { \ + return git_object_id((const git_object *)obj); } \ + git_repository *TYPE##_owner(const TYPE *obj) { \ + return git_object_owner((const git_object *)obj); } + #endif diff --git a/src/tag.c b/src/tag.c index b9a806cd1..ad3a8fd36 100644 --- a/src/tag.c +++ b/src/tag.c @@ -15,6 +15,8 @@ #include "git2/signature.h" #include "git2/odb_backend.h" +GIT_OBJ_WRAPPER(git_tag, GIT_OBJ_TAG) + void git_tag__free(void *_tag) { git_tag *tag = _tag; @@ -24,11 +26,6 @@ void git_tag__free(void *_tag) git__free(tag); } -const git_oid *git_tag_id(const git_tag *c) -{ - return git_object_id((const git_object *)c); -} - int git_tag_target(git_object **target, const git_tag *t) { assert(t); diff --git a/src/tree.c b/src/tree.c index 58eb92f35..67c9a068d 100644 --- a/src/tree.c +++ b/src/tree.c @@ -11,6 +11,8 @@ #include "git2/repository.h" #include "git2/object.h" +GIT_OBJ_WRAPPER(git_tree, GIT_OBJ_TREE) + #define DEFAULT_TREE_SIZE 16 #define MAX_FILEMODE_BYTES 6 @@ -232,16 +234,6 @@ void git_tree__free(void *_tree) git__free(tree); } -const git_oid *git_tree_id(const git_tree *t) -{ - return git_object_id((const git_object *)t); -} - -git_repository *git_tree_owner(const git_tree *t) -{ - return git_object_owner((const git_object *)t); -} - git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry) { return (git_filemode_t)entry->attr; From e4af0f001600cfe7d72cfb140fc7dc2d25be2c37 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 29 Apr 2013 18:15:43 -0700 Subject: [PATCH 100/384] Add new src/oid.h --- src/oid.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/oid.h diff --git a/src/oid.h b/src/oid.h new file mode 100644 index 000000000..077d0a4c8 --- /dev/null +++ b/src/oid.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_oid_h__ +#define INCLUDE_oid_h__ + +#include "git2/oid.h" + +/* + * Compare two oid structures. + * + * @param a first oid structure. + * @param b second oid structure. + * @return <0, 0, >0 if a < b, a == b, a > b. + */ +GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b) +{ + const unsigned char *sha1 = a->id; + const unsigned char *sha2 = b->id; + int i; + + for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) { + if (*sha1 != *sha2) + return *sha1 - *sha2; + } + + return 0; +} + +#endif From 203d5b0e6829242ea412bbef7751e3c522ac5dd8 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 29 Apr 2013 18:20:58 -0700 Subject: [PATCH 101/384] Some cleanups Removed useless prototype and renamed object typecast functions declaration macro. --- include/git2/oid.h | 9 --------- src/blob.c | 2 +- src/object.h | 2 +- src/tag.c | 2 +- src/tree.c | 2 +- 5 files changed, 4 insertions(+), 13 deletions(-) diff --git a/include/git2/oid.h b/include/git2/oid.h index 288e90bc8..b20bb221a 100644 --- a/include/git2/oid.h +++ b/include/git2/oid.h @@ -138,15 +138,6 @@ GIT_EXTERN(char *) git_oid_tostr(char *out, size_t n, const git_oid *id); */ GIT_EXTERN(void) git_oid_cpy(git_oid *out, const git_oid *src); -/** - * Compare two oid structures. - * - * @param a first oid structure. - * @param b second oid structure. - * @return <0, 0, >0 if a < b, a == b, a > b. - */ -GIT_EXTERN(int) git_oid_compare(const git_oid *oid_a, const git_oid *oid_b); - /** * Compare two oid structures. * diff --git a/src/blob.c b/src/blob.c index d656576b8..25ea0df30 100644 --- a/src/blob.c +++ b/src/blob.c @@ -15,7 +15,7 @@ #include "filter.h" #include "buf_text.h" -GIT_OBJ_WRAPPER(git_blob, GIT_OBJ_BLOB) +GIT_OBJECT__TYPED_FUNCTIONS(git_blob, GIT_OBJ_BLOB) const void *git_blob_rawcontent(const git_blob *blob) { diff --git a/src/object.h b/src/object.h index 906d40736..7b25fc342 100644 --- a/src/object.h +++ b/src/object.h @@ -28,7 +28,7 @@ int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid); -#define GIT_OBJ_WRAPPER(TYPE,OBJTYPE) \ +#define GIT_OBJECT__TYPED_FUNCTIONS(TYPE,OBJTYPE) \ int TYPE##_lookup(TYPE **out, git_repository *repo, const git_oid *id) { \ return git_object_lookup((git_object **)out, repo, id, OBJTYPE); } \ int TYPE##_lookup_prefix(TYPE **out, git_repository *repo, const git_oid *id, size_t len) { \ diff --git a/src/tag.c b/src/tag.c index ad3a8fd36..a0ecce176 100644 --- a/src/tag.c +++ b/src/tag.c @@ -15,7 +15,7 @@ #include "git2/signature.h" #include "git2/odb_backend.h" -GIT_OBJ_WRAPPER(git_tag, GIT_OBJ_TAG) +GIT_OBJECT__TYPED_FUNCTIONS(git_tag, GIT_OBJ_TAG) void git_tag__free(void *_tag) { diff --git a/src/tree.c b/src/tree.c index 67c9a068d..0a94aec10 100644 --- a/src/tree.c +++ b/src/tree.c @@ -11,7 +11,7 @@ #include "git2/repository.h" #include "git2/object.h" -GIT_OBJ_WRAPPER(git_tree, GIT_OBJ_TREE) +GIT_OBJECT__TYPED_FUNCTIONS(git_tree, GIT_OBJ_TREE) #define DEFAULT_TREE_SIZE 16 #define MAX_FILEMODE_BYTES 6 From 8d39f2a79067c9551286bb552457db71b88b64d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Apr 2013 10:55:17 +0200 Subject: [PATCH 102/384] refspec: add direction accessor --- include/git2/refspec.h | 8 ++++++++ src/refspec.c | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/include/git2/refspec.h b/include/git2/refspec.h index 3e1b502ef..89ab81b02 100644 --- a/include/git2/refspec.h +++ b/include/git2/refspec.h @@ -51,6 +51,14 @@ GIT_EXTERN(const char *) git_refspec_string(const git_refspec *refspec); */ GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec); +/** + * Get the refspec's direction. + * + * @param the refspec + * @return GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH + */ +GIT_EXTERN(git_direction) git_refspec_direction(const git_refspec *spec); + /** * Check if a refspec's source descriptor matches a reference * diff --git a/src/refspec.c b/src/refspec.c index 256540819..a907df84c 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -274,3 +274,10 @@ int git_refspec_is_wildcard(const git_refspec *spec) return (spec->src[strlen(spec->src) - 1] == '*'); } + +git_direction git_refspec_direction(const git_refspec *spec) +{ + assert(spec); + + return spec->push; +} From 1ffd0806f406a9dc300dbdefaf1e1d036a4294b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Apr 2013 11:18:16 +0200 Subject: [PATCH 103/384] remote: add resfpec list accessors Bring back a way of acessing the git_refspec* from a remote. Closes #1514 --- include/git2/refspec.h | 1 + include/git2/remote.h | 26 ++++++++++++++++++++++++++ src/remote.c | 26 ++++++++++++++++++++++++++ tests-clar/clone/nonetwork.c | 4 ++-- tests-clar/network/remote/remotes.c | 15 +++++++-------- 5 files changed, 62 insertions(+), 10 deletions(-) diff --git a/include/git2/refspec.h b/include/git2/refspec.h index 89ab81b02..c0b410cbf 100644 --- a/include/git2/refspec.h +++ b/include/git2/refspec.h @@ -9,6 +9,7 @@ #include "common.h" #include "types.h" +#include "net.h" /** * @file git2/refspec.h diff --git a/include/git2/remote.h b/include/git2/remote.h index f02b95678..2aa384a54 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -190,6 +190,32 @@ GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, git_remote *re */ GIT_EXTERN(void) git_remote_clear_refspecs(git_remote *remote); +/** + * Get the number of refspecs for a remote + * + * @param remote the remote + * @return the amount of refspecs configured in this remote + */ +GIT_EXTERN(size_t) git_remote_refspec_count(git_remote *remote); + +/** + * Get a refspec from the remote + * + * @param remote the remote to query + * @param n the refspec to get + * @return the nth refspec + */ +GIT_EXTERN(const git_refspec *)git_remote_get_refspec(git_remote *remote, size_t n); + +/** + * Remove a refspec from the remote + * + * @param remote the remote to query + * @param n the refspec to remove + * @return 0 or GIT_ENOTFOUND + */ +GIT_EXTERN(int) git_remote_remove_refspec(git_remote *remote, size_t n); + /** * Open a connection to a remote * diff --git a/src/remote.c b/src/remote.c index 1183137a6..153c93470 100644 --- a/src/remote.c +++ b/src/remote.c @@ -8,6 +8,7 @@ #include "git2/config.h" #include "git2/types.h" #include "git2/oid.h" +#include "git2/net.h" #include "config.h" #include "repository.h" @@ -1574,3 +1575,28 @@ int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote) { return copy_refspecs(array, remote, true); } + +size_t git_remote_refspec_count(git_remote *remote) +{ + return remote->refspecs.length; +} + +const git_refspec *git_remote_get_refspec(git_remote *remote, size_t n) +{ + return git_vector_get(&remote->refspecs, n); +} + +int git_remote_remove_refspec(git_remote *remote, size_t n) +{ + git_refspec *spec; + + assert(remote); + + spec = git_vector_get(&remote->refspecs, n); + if (spec) { + git_refspec__free(spec); + git__free(spec); + } + + return git_vector_remove(&remote->refspecs, n); +} diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c index 02066e07d..545fe3a06 100644 --- a/tests-clar/clone/nonetwork.c +++ b/tests-clar/clone/nonetwork.c @@ -149,7 +149,7 @@ void test_clone_nonetwork__custom_fetch_spec(void) cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); cl_git_pass(git_remote_load(&g_remote, g_repo, "origin")); - actual_fs = git_vector_get(&g_remote->refspecs, 0); + actual_fs = git_remote_get_refspec(g_remote, 0); cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs)); cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs)); @@ -165,7 +165,7 @@ void test_clone_nonetwork__custom_push_spec(void) cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); cl_git_pass(git_remote_load(&g_remote, g_repo, "origin")); - actual_fs = git_vector_get(&g_remote->refspecs, g_remote->refspecs.length - 1); + actual_fs = git_remote_get_refspec(g_remote, git_remote_refspec_count(g_remote) - 1); cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs)); cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs)); } diff --git a/tests-clar/network/remote/remotes.c b/tests-clar/network/remote/remotes.c index 908e17d96..4c24db8eb 100644 --- a/tests-clar/network/remote/remotes.c +++ b/tests-clar/network/remote/remotes.c @@ -13,7 +13,7 @@ void test_network_remote_remotes__initialize(void) cl_git_pass(git_remote_load(&_remote, _repo, "test")); - _refspec = git_vector_get(&_remote->refspecs, 0); + _refspec = git_remote_get_refspec(_remote, 0); cl_assert(_refspec != NULL); } @@ -113,15 +113,14 @@ void test_network_remote_remotes__add_fetchspec(void) { size_t size; - size = _remote->refspecs.length; - cl_assert_equal_i(size, _remote->refspecs.length); + size = git_remote_refspec_count(_remote); cl_git_pass(git_remote_add_fetch(_remote, "refs/*:refs/*")); size++; - cl_assert_equal_i(size, _remote->refspecs.length); + cl_assert_equal_i(size, git_remote_refspec_count(_remote)); - _refspec = git_vector_get(&_remote->refspecs, size-1); + _refspec = git_remote_get_refspec(_remote, size - 1); cl_assert_equal_s(git_refspec_src(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*"); @@ -132,13 +131,13 @@ void test_network_remote_remotes__add_pushspec(void) { size_t size; - size = _remote->refspecs.length; + size = git_remote_refspec_count(_remote); cl_git_pass(git_remote_add_push(_remote, "refs/*:refs/*")); size++; - cl_assert_equal_i(size, _remote->refspecs.length); + cl_assert_equal_i(size, git_remote_refspec_count(_remote)); - _refspec = git_vector_get(&_remote->refspecs, size-1); + _refspec = git_remote_get_refspec(_remote, size - 1); cl_assert_equal_s(git_refspec_src(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*"); From 9c5d4b2e807cdbb83cf55868700b0387cd4e8c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Apr 2013 12:05:16 +0200 Subject: [PATCH 104/384] remote: fix a leak when dwim'ing refspecs --- src/remote.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/remote.c b/src/remote.c index 153c93470..08f56cd51 100644 --- a/src/remote.c +++ b/src/remote.c @@ -692,6 +692,7 @@ static int dwim_refspecs(git_vector *refspecs, git_vector *refs) spec->dwim = 1; } + git_buf_free(&buf); return 0; } From 0a1755c045b930de474883eb6e7fedcc3403b494 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 30 Apr 2013 03:15:45 -0700 Subject: [PATCH 105/384] Catch issue in config set with no config file This prevents a segfault when setting a value in the config of a repository that doesn't have a config file. --- src/config.c | 6 ++++++ tests-clar/repo/open.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/config.c b/src/config.c index 3c0bbe9a7..2e1268ef3 100644 --- a/src/config.c +++ b/src/config.c @@ -373,6 +373,12 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) } internal = git_vector_get(&cfg->files, 0); + if (!internal) { + /* Should we auto-vivify .git/config? Tricky from this location */ + giterr_set(GITERR_CONFIG, "Cannot set value when no config files exist"); + return GIT_ENOTFOUND; + } + file = internal->file; error = file->set(file, name, value); diff --git a/tests-clar/repo/open.c b/tests-clar/repo/open.c index 7f93ae91a..6b5253797 100644 --- a/tests-clar/repo/open.c +++ b/tests-clar/repo/open.c @@ -280,3 +280,38 @@ void test_repo_open__opening_a_non_existing_repository_returns_ENOTFOUND(void) git_repository *repo; cl_assert_equal_i(GIT_ENOTFOUND, git_repository_open(&repo, "i-do-not/exist")); } + +void test_repo_open__no_config(void) +{ + git_buf path = GIT_BUF_INIT; + git_repository *repo; + git_config *config; + + cl_fixture_sandbox("empty_standard_repo"); + cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git")); + + /* remove local config */ + cl_git_pass(git_futils_rmdir_r( + "empty_standard_repo/.git/config", NULL, GIT_RMDIR_REMOVE_FILES)); + + /* isolate from system level configs */ + cl_must_pass(p_mkdir("alternate", 0777)); + cl_git_pass(git_path_prettify(&path, "alternate", NULL)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); + + git_buf_free(&path); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + cl_git_pass(git_repository_config(&config, repo)); + + cl_git_fail(git_config_set_string(config, "test.set", "42")); + + git_config_free(config); + git_repository_free(repo); + cl_fixture_cleanup("empty_standard_repo"); +} From 0b726701f3d3c5a3a596b53d8db0b7a4b4032dfb Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 30 Apr 2013 13:13:38 +0200 Subject: [PATCH 106/384] object: Explicitly define helper API methods for all obj types --- include/git2/commit.h | 20 ++----- src/blob.c | 2 - src/object.h | 12 ---- src/object_api.c | 129 ++++++++++++++++++++++++++++++++++++++++++ src/tag.c | 2 - src/tree.c | 2 - 6 files changed, 133 insertions(+), 34 deletions(-) create mode 100644 src/object_api.c diff --git a/include/git2/commit.h b/include/git2/commit.h index 0f7601252..f536ac7c8 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -30,10 +30,7 @@ GIT_BEGIN_DECL * an annotated tag it will be peeled back to the commit. * @return 0 or an error code */ -GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id) -{ - return git_object_lookup((git_object **)commit, repo, id, GIT_OBJ_COMMIT); -} +GIT_EXTERN(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id); /** * Lookup a commit object from a repository, @@ -48,10 +45,7 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con * @param len the length of the short identifier * @return 0 or an error code */ -GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len) -{ - return git_object_lookup_prefix((git_object **)commit, repo, id, len, GIT_OBJ_COMMIT); -} +GIT_EXTERN(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len); /** * Close an open commit @@ -65,10 +59,7 @@ GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *re * @param commit the commit to close */ -GIT_INLINE(void) git_commit_free(git_commit *commit) -{ - git_object_free((git_object *) commit); -} +GIT_EXTERN(void) git_commit_free(git_commit *commit); /** * Get the id of a commit. @@ -76,10 +67,7 @@ GIT_INLINE(void) git_commit_free(git_commit *commit) * @param commit a previously loaded commit. * @return object identity for the commit. */ -GIT_INLINE(const git_oid *) git_commit_id(const git_commit *commit) -{ - return git_object_id((const git_object *)commit); -} +GIT_EXTERN(const git_oid *) git_commit_id(const git_commit *commit); /** * Get the encoding for the message of a commit, diff --git a/src/blob.c b/src/blob.c index 25ea0df30..a68c4cc3e 100644 --- a/src/blob.c +++ b/src/blob.c @@ -15,8 +15,6 @@ #include "filter.h" #include "buf_text.h" -GIT_OBJECT__TYPED_FUNCTIONS(git_blob, GIT_OBJ_BLOB) - const void *git_blob_rawcontent(const git_blob *blob) { assert(blob); diff --git a/src/object.h b/src/object.h index 7b25fc342..d187c55b7 100644 --- a/src/object.h +++ b/src/object.h @@ -28,16 +28,4 @@ int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid); -#define GIT_OBJECT__TYPED_FUNCTIONS(TYPE,OBJTYPE) \ - int TYPE##_lookup(TYPE **out, git_repository *repo, const git_oid *id) { \ - return git_object_lookup((git_object **)out, repo, id, OBJTYPE); } \ - int TYPE##_lookup_prefix(TYPE **out, git_repository *repo, const git_oid *id, size_t len) { \ - return git_object_lookup_prefix((git_object **)out, repo, id, len, OBJTYPE); } \ - void TYPE##_free(TYPE *obj) { \ - git_object_free((git_object *)obj); } \ - const git_oid *TYPE##_id(const TYPE *obj) { \ - return git_object_id((const git_object *)obj); } \ - git_repository *TYPE##_owner(const TYPE *obj) { \ - return git_object_owner((const git_object *)obj); } - #endif diff --git a/src/object_api.c b/src/object_api.c new file mode 100644 index 000000000..620617dfd --- /dev/null +++ b/src/object_api.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#include "git2/object.h" + +#include "common.h" +#include "repository.h" + +#include "commit.h" +#include "tree.h" +#include "blob.h" +#include "tag.h" + +/** + * Blob + */ +int git_commit_lookup(git_commit **out, git_repository *repo, const git_oid *id) +{ + return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_COMMIT); +} + +int git_commit_lookup_prefix(git_commit **out, git_repository *repo, const git_oid *id, size_t len) +{ + return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_COMMIT); +} + +void git_commit_free(git_commit *obj) +{ + git_object_free((git_object *)obj); +} + +const git_oid *git_commit_id(const git_commit *obj) +{ + return git_object_id((const git_object *)obj); +} + +git_repository *git_commit_owner(const git_commit *obj) +{ + return git_object_owner((const git_object *)obj); +} + + +/** + * Tree + */ +int git_tree_lookup(git_tree **out, git_repository *repo, const git_oid *id) +{ + return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_COMMIT); +} + +int git_tree_lookup_prefix(git_tree **out, git_repository *repo, const git_oid *id, size_t len) +{ + return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_COMMIT); +} + +void git_tree_free(git_tree *obj) +{ + git_object_free((git_object *)obj); +} + +const git_oid *git_tree_id(const git_tree *obj) +{ + return git_object_id((const git_object *)obj); +} + +git_repository *git_tree_owner(const git_tree *obj) +{ + return git_object_owner((const git_object *)obj); +} + + +/** + * Tag + */ +int git_tag_lookup(git_tag **out, git_repository *repo, const git_oid *id) +{ + return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_COMMIT); +} + +int git_tag_lookup_prefix(git_tag **out, git_repository *repo, const git_oid *id, size_t len) +{ + return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_COMMIT); +} + +void git_tag_free(git_tag *obj) +{ + git_object_free((git_object *)obj); +} + +const git_oid *git_tag_id(const git_tag *obj) +{ + return git_object_id((const git_object *)obj); +} + +git_repository *git_tag_owner(const git_tag *obj) +{ + return git_object_owner((const git_object *)obj); +} + +/** + * Blob + */ +int git_blob_lookup(git_blob **out, git_repository *repo, const git_oid *id) +{ + return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_COMMIT); +} + +int git_blob_lookup_prefix(git_blob **out, git_repository *repo, const git_oid *id, size_t len) +{ + return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_COMMIT); +} + +void git_blob_free(git_blob *obj) +{ + git_object_free((git_object *)obj); +} + +const git_oid *git_blob_id(const git_blob *obj) +{ + return git_object_id((const git_object *)obj); +} + +git_repository *git_blob_owner(const git_blob *obj) +{ + return git_object_owner((const git_object *)obj); +} diff --git a/src/tag.c b/src/tag.c index a0ecce176..a4f2e2581 100644 --- a/src/tag.c +++ b/src/tag.c @@ -15,8 +15,6 @@ #include "git2/signature.h" #include "git2/odb_backend.h" -GIT_OBJECT__TYPED_FUNCTIONS(git_tag, GIT_OBJ_TAG) - void git_tag__free(void *_tag) { git_tag *tag = _tag; diff --git a/src/tree.c b/src/tree.c index 0a94aec10..79cbcffcb 100644 --- a/src/tree.c +++ b/src/tree.c @@ -11,8 +11,6 @@ #include "git2/repository.h" #include "git2/object.h" -GIT_OBJECT__TYPED_FUNCTIONS(git_tree, GIT_OBJ_TREE) - #define DEFAULT_TREE_SIZE 16 #define MAX_FILEMODE_BYTES 6 From 7dcda3aa3780292e33bb9229ff998ffe4edc07bf Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 30 Apr 2013 13:19:02 +0200 Subject: [PATCH 107/384] object: haha --- src/object_api.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/object_api.c b/src/object_api.c index 620617dfd..838bba323 100644 --- a/src/object_api.c +++ b/src/object_api.c @@ -48,12 +48,12 @@ git_repository *git_commit_owner(const git_commit *obj) */ int git_tree_lookup(git_tree **out, git_repository *repo, const git_oid *id) { - return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_COMMIT); + return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TREE); } int git_tree_lookup_prefix(git_tree **out, git_repository *repo, const git_oid *id, size_t len) { - return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_COMMIT); + return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_TREE); } void git_tree_free(git_tree *obj) @@ -77,12 +77,12 @@ git_repository *git_tree_owner(const git_tree *obj) */ int git_tag_lookup(git_tag **out, git_repository *repo, const git_oid *id) { - return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_COMMIT); + return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TAG); } int git_tag_lookup_prefix(git_tag **out, git_repository *repo, const git_oid *id, size_t len) { - return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_COMMIT); + return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_TAG); } void git_tag_free(git_tag *obj) @@ -105,12 +105,12 @@ git_repository *git_tag_owner(const git_tag *obj) */ int git_blob_lookup(git_blob **out, git_repository *repo, const git_oid *id) { - return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_COMMIT); + return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_BLOB); } int git_blob_lookup_prefix(git_blob **out, git_repository *repo, const git_oid *id, size_t len) { - return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_COMMIT); + return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_BLOB); } void git_blob_free(git_blob *obj) From fdb3034e725ccf2c7be11871fcc374ced436983e Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 25 Apr 2013 14:57:13 -0700 Subject: [PATCH 108/384] Reorganize diff code into functions In preparation for more changes to the internal diff logic, it seemed wise to split the very large git_diff__from_iterators into separate functions that handle the four main cases (unmatched old item, unmatched new item, unmatched new directory, and matched old and new items). Hopefully this will keep the logic easier to follow even as more cases have to be added to this code. --- src/diff.c | 377 ++++++++++++++++++++++++++++------------------------- 1 file changed, 202 insertions(+), 175 deletions(-) diff --git a/src/diff.c b/src/diff.c index 6612abf06..58c7eacc6 100644 --- a/src/diff.c +++ b/src/diff.c @@ -530,24 +530,30 @@ cleanup: return result; } +typedef struct { + git_repository *repo; + git_iterator *old_iter; + git_iterator *new_iter; + const git_index_entry *oitem; + const git_index_entry *nitem; + git_buf ignore_prefix; +} diff_in_progress; + #define MODE_BITS_MASK 0000777 static int maybe_modified( - git_iterator *old_iter, - const git_index_entry *oitem, - git_iterator *new_iter, - const git_index_entry *nitem, - git_diff_list *diff) + git_diff_list *diff, + diff_in_progress *info) { git_oid noid, *use_noid = NULL; git_delta_t status = GIT_DELTA_MODIFIED; + const git_index_entry *oitem = info->oitem; + const git_index_entry *nitem = info->nitem; unsigned int omode = oitem->mode; unsigned int nmode = nitem->mode; - bool new_is_workdir = (new_iter->type == GIT_ITERATOR_TYPE_WORKDIR); + bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR); const char *matched_pathspec; - GIT_UNUSED(old_iter); - if (!git_pathspec_match_path( &diff->pathspec, oitem->path, DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH), @@ -692,6 +698,168 @@ static bool entry_is_prefixed( item->path[pathlen] == '/'); } +static int handle_unmatched_new_directory( + git_diff_list *diff, diff_in_progress *info, git_delta_t *delta) +{ + int error = 0; + const git_index_entry *nitem = info->nitem; + bool contains_oitem = entry_is_prefixed(diff, info->oitem, nitem); + bool recurse_into_dir = + (*delta == GIT_DELTA_UNTRACKED && + DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) || + (*delta == GIT_DELTA_IGNORED && + DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)); + + /* do not advance into directories that contain a .git file */ + if (!contains_oitem && recurse_into_dir) { + git_buf *full = NULL; + if (git_iterator_current_workdir_path(&full, info->new_iter) < 0) + return -1; + if (git_path_contains_dir(full, DOT_GIT)) + recurse_into_dir = false; + } + + /* if directory is ignored, remember ignore_prefix */ + if ((contains_oitem || recurse_into_dir) && + *delta == GIT_DELTA_UNTRACKED && + git_iterator_current_is_ignored(info->new_iter)) + { + git_buf_sets(&info->ignore_prefix, info->nitem->path); + *delta = GIT_DELTA_IGNORED; + + /* skip recursion if we've just learned this is ignored */ + if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) + recurse_into_dir = false; + } + + if (contains_oitem || recurse_into_dir) { + /* advance into directory */ + error = git_iterator_advance_into(&info->nitem, info->new_iter); + + /* if directory is empty, can't advance into it, so skip */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = git_iterator_advance(&info->nitem, info->new_iter); + + git_buf_clear(&info->ignore_prefix); + } + + /* return UNMODIFIED to tell caller not to create a new record */ + *delta = GIT_DELTA_UNMODIFIED; + } + + return error; +} + +static int handle_unmatched_new_item( + git_diff_list *diff, diff_in_progress *info) +{ + int error = 0; + const git_index_entry *nitem = info->nitem; + git_delta_t delta_type = GIT_DELTA_UNTRACKED; + + /* check if contained in ignored parent directory */ + if (git_buf_len(&info->ignore_prefix) && + diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0) + delta_type = GIT_DELTA_IGNORED; + + if (S_ISDIR(nitem->mode)) { + error = handle_unmatched_new_directory(diff, info, &delta_type); + + if (error || delta_type == GIT_DELTA_UNMODIFIED) + return error; + } + + /* In core git, the next two "else if" clauses are effectively + * reversed -- i.e. when an untracked file contained in an + * ignored directory is individually ignored, it shows up as an + * ignored file in the diff list, even though other untracked + * files in the same directory are skipped completely. + * + * To me, this is odd. If the directory is ignored and the file + * is untracked, we should skip it consistently, regardless of + * whether it happens to match a pattern in the ignore file. + * + * To match the core git behavior, just reverse the following + * two "else if" cases so that individual file ignores are + * checked before container directory exclusions are used to + * skip the file. + */ + else if (delta_type == GIT_DELTA_IGNORED && + DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) + return git_iterator_advance(&info->nitem, info->new_iter); + + else if (git_iterator_current_is_ignored(info->new_iter)) + delta_type = GIT_DELTA_IGNORED; + + else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR) + delta_type = GIT_DELTA_ADDED; + + if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0) + return error; + + /* if we are generating TYPECHANGE records then check for that + * instead of just generating an ADDED/UNTRACKED record + */ + if (delta_type != GIT_DELTA_IGNORED && + DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) && + entry_is_prefixed(diff, info->oitem, nitem)) + { + /* this entry was prefixed with a tree - make TYPECHANGE */ + git_diff_delta *last = diff_delta__last_for_item(diff, nitem); + if (last) { + last->status = GIT_DELTA_TYPECHANGE; + last->old_file.mode = GIT_FILEMODE_TREE; + } + } + + return git_iterator_advance(&info->nitem, info->new_iter); +} + +static int handle_unmatched_old_item( + git_diff_list *diff, diff_in_progress *info) +{ + int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem); + if (error < 0) + return error; + + /* if we are generating TYPECHANGE records then check for that + * instead of just generating a DELETE record + */ + if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) && + entry_is_prefixed(diff, info->nitem, info->oitem)) + { + /* this entry has become a tree! convert to TYPECHANGE */ + git_diff_delta *last = diff_delta__last_for_item(diff, info->oitem); + if (last) { + last->status = GIT_DELTA_TYPECHANGE; + last->new_file.mode = GIT_FILEMODE_TREE; + } + + /* If new_iter is a workdir iterator, then this situation + * will certainly be followed by a series of untracked items. + * Unless RECURSE_UNTRACKED_DIRS is set, skip over them... + */ + if (S_ISDIR(info->nitem->mode) && + DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) + return git_iterator_advance(&info->nitem, info->new_iter); + } + + return git_iterator_advance(&info->oitem, info->old_iter); +} + +static int handle_matched_item( + git_diff_list *diff, diff_in_progress *info) +{ + int error = 0; + + if (!(error = maybe_modified(diff, info)) && + !(error = git_iterator_advance(&info->oitem, info->old_iter))) + error = git_iterator_advance(&info->nitem, info->new_iter); + + return error; +} + int git_diff__from_iterators( git_diff_list **diff_ptr, git_repository *repo, @@ -700,8 +868,7 @@ int git_diff__from_iterators( const git_diff_options *opts) { int error = 0; - const git_index_entry *oitem, *nitem; - git_buf ignore_prefix = GIT_BUF_INIT; + diff_in_progress info; git_diff_list *diff; *diff_ptr = NULL; @@ -709,191 +876,51 @@ int git_diff__from_iterators( diff = diff_list_alloc(repo, old_iter, new_iter); GITERR_CHECK_ALLOC(diff); + info.repo = repo; + info.old_iter = old_iter; + info.new_iter = new_iter; + git_buf_init(&info.ignore_prefix, 0); + /* make iterators have matching icase behavior */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) { - if (git_iterator_set_ignore_case(old_iter, true) < 0 || - git_iterator_set_ignore_case(new_iter, true) < 0) - goto fail; + if (!(error = git_iterator_set_ignore_case(old_iter, true))) + error = git_iterator_set_ignore_case(new_iter, true); } - if (diff_list_apply_options(diff, opts) < 0 || - git_iterator_current(&oitem, old_iter) < 0 || - git_iterator_current(&nitem, new_iter) < 0) - goto fail; + /* finish initialization */ + if (!error && + !(error = diff_list_apply_options(diff, opts)) && + !(error = git_iterator_current(&info.oitem, old_iter))) + error = git_iterator_current(&info.nitem, new_iter); /* run iterators building diffs */ - while (oitem || nitem) { - int cmp = oitem ? (nitem ? diff->entrycomp(oitem, nitem) : -1) : 1; + while (!error && (info.oitem || info.nitem)) { + int cmp = info.oitem ? + (info.nitem ? diff->entrycomp(info.oitem, info.nitem) : -1) : 1; /* create DELETED records for old items not matched in new */ - if (cmp < 0) { - if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0) - goto fail; - - /* if we are generating TYPECHANGE records then check for that - * instead of just generating a DELETE record - */ - if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) && - entry_is_prefixed(diff, nitem, oitem)) - { - /* this entry has become a tree! convert to TYPECHANGE */ - git_diff_delta *last = diff_delta__last_for_item(diff, oitem); - if (last) { - last->status = GIT_DELTA_TYPECHANGE; - last->new_file.mode = GIT_FILEMODE_TREE; - } - - /* If new_iter is a workdir iterator, then this situation - * will certainly be followed by a series of untracked items. - * Unless RECURSE_UNTRACKED_DIRS is set, skip over them... - */ - if (S_ISDIR(nitem->mode) && - DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) - { - if (git_iterator_advance(&nitem, new_iter) < 0) - goto fail; - } - } - - if (git_iterator_advance(&oitem, old_iter) < 0) - goto fail; - } + if (cmp < 0) + error = handle_unmatched_old_item(diff, &info); /* create ADDED, TRACKED, or IGNORED records for new items not * matched in old (and/or descend into directories as needed) */ - else if (cmp > 0) { - git_delta_t delta_type = GIT_DELTA_UNTRACKED; - bool contains_oitem = entry_is_prefixed(diff, oitem, nitem); - - /* check if contained in ignored parent directory */ - if (git_buf_len(&ignore_prefix) && - diff->pfxcomp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0) - delta_type = GIT_DELTA_IGNORED; - - if (S_ISDIR(nitem->mode)) { - /* recurse into directory only if there are tracked items in - * it or if the user requested the contents of untracked - * directories and it is not under an ignored directory. - */ - bool recurse_into_dir = - (delta_type == GIT_DELTA_UNTRACKED && - DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) || - (delta_type == GIT_DELTA_IGNORED && - DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)); - - /* do not advance into directories that contain a .git file */ - if (!contains_oitem && recurse_into_dir) { - git_buf *full = NULL; - if (git_iterator_current_workdir_path(&full, new_iter) < 0) - goto fail; - if (git_path_contains_dir(full, DOT_GIT)) - recurse_into_dir = false; - } - - /* if directory is ignored, remember ignore_prefix */ - if ((contains_oitem || recurse_into_dir) && - delta_type == GIT_DELTA_UNTRACKED && - git_iterator_current_is_ignored(new_iter)) - { - git_buf_sets(&ignore_prefix, nitem->path); - delta_type = GIT_DELTA_IGNORED; - - /* skip recursion if we've just learned this is ignored */ - if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) - recurse_into_dir = false; - } - - if (contains_oitem || recurse_into_dir) { - /* advance into directory */ - error = git_iterator_advance_into(&nitem, new_iter); - - /* if directory is empty, can't advance into it, so skip */ - if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = git_iterator_advance(&nitem, new_iter); - - git_buf_clear(&ignore_prefix); - } - - if (error < 0) - goto fail; - continue; - } - } - - /* In core git, the next two "else if" clauses are effectively - * reversed -- i.e. when an untracked file contained in an - * ignored directory is individually ignored, it shows up as an - * ignored file in the diff list, even though other untracked - * files in the same directory are skipped completely. - * - * To me, this is odd. If the directory is ignored and the file - * is untracked, we should skip it consistently, regardless of - * whether it happens to match a pattern in the ignore file. - * - * To match the core git behavior, just reverse the following - * two "else if" cases so that individual file ignores are - * checked before container directory exclusions are used to - * skip the file. - */ - else if (delta_type == GIT_DELTA_IGNORED && - DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) { - if (git_iterator_advance(&nitem, new_iter) < 0) - goto fail; - continue; /* ignored parent directory, so skip completely */ - } - - else if (git_iterator_current_is_ignored(new_iter)) - delta_type = GIT_DELTA_IGNORED; - - else if (new_iter->type != GIT_ITERATOR_TYPE_WORKDIR) - delta_type = GIT_DELTA_ADDED; - - if (diff_delta__from_one(diff, delta_type, nitem) < 0) - goto fail; - - /* if we are generating TYPECHANGE records then check for that - * instead of just generating an ADDED/UNTRACKED record - */ - if (delta_type != GIT_DELTA_IGNORED && - DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) && - contains_oitem) - { - /* this entry was prefixed with a tree - make TYPECHANGE */ - git_diff_delta *last = diff_delta__last_for_item(diff, nitem); - if (last) { - last->status = GIT_DELTA_TYPECHANGE; - last->old_file.mode = GIT_FILEMODE_TREE; - } - } - - if (git_iterator_advance(&nitem, new_iter) < 0) - goto fail; - } + else if (cmp > 0) + error = handle_unmatched_new_item(diff, &info); /* otherwise item paths match, so create MODIFIED record * (or ADDED and DELETED pair if type changed) */ - else { - assert(oitem && nitem && cmp == 0); - - if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 || - git_iterator_advance(&oitem, old_iter) < 0 || - git_iterator_advance(&nitem, new_iter) < 0) - goto fail; - } + else + error = handle_matched_item(diff, &info); } - *diff_ptr = diff; - -fail: - if (!*diff_ptr) { + if (!error) + *diff_ptr = diff; + else git_diff_list_free(diff); - error = -1; - } - git_buf_free(&ignore_prefix); + git_buf_free(&info.ignore_prefix); return error; } From e26b14c0345ef82771f222aa50be926f5969531d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 26 Apr 2013 15:35:47 -0700 Subject: [PATCH 109/384] Update diff handling of untracked directories When diff encounters an untracked directory, there was a shortcut that it took which is not compatible with core git. This makes the default behavior no longer take that shortcut and instead look inside the untracked directory to see if there are any untracked files within it. If there are not, then the directory is treated as an ignore directory instead of an untracked directory. This has implications for the git_status APIs. --- include/git2/diff.h | 7 + include/git2/submodule.h | 28 ++-- src/diff.c | 209 ++++++++++++++++++++--------- src/vector.c | 8 +- tests-clar/status/status_helpers.c | 3 +- tests-clar/status/status_helpers.h | 1 + tests-clar/status/worktree.c | 5 +- tests-clar/submodule/status.c | 27 ++++ 8 files changed, 202 insertions(+), 86 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index d9ceadf20..cc16d01b6 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -124,6 +124,13 @@ typedef enum { * adds all files under the directory as IGNORED entries, too. */ GIT_DIFF_RECURSE_IGNORED_DIRS = (1 << 18), + /** For an untracked directory, diff can immediately label it UNTRACKED, + * but this differs from core Git which scans underneath for untracked + * or ignored files and marks the directory ignored unless it contains + * untracked files under it. That search can be slow. This flag makes + * diff skip ahead and immediately report the directory as untracked. + */ + GIT_DIFF_FAST_UNTRACKED_DIRS = (1 << 19), } git_diff_option_t; /** diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 40934b3ed..004665050 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -103,20 +103,20 @@ typedef enum { * * WD_UNTRACKED - wd contains untracked files */ typedef enum { - GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0), - GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1), - GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2), - GIT_SUBMODULE_STATUS_IN_WD = (1u << 3), - GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4), - GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5), - GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6), - GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7), - GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8), - GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9), - GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10), - GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11), - GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12), - GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13), + GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0), + GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1), + GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2), + GIT_SUBMODULE_STATUS_IN_WD = (1u << 3), + GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4), + GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5), + GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6), + GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7), + GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8), + GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9), + GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10), + GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11), + GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12), + GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13), } git_submodule_status_t; #define GIT_SUBMODULE_STATUS__IN_FLAGS \ diff --git a/src/diff.c b/src/diff.c index 58c7eacc6..cea3fdb22 100644 --- a/src/diff.c +++ b/src/diff.c @@ -698,56 +698,58 @@ static bool entry_is_prefixed( item->path[pathlen] == '/'); } -static int handle_unmatched_new_directory( - git_diff_list *diff, diff_in_progress *info, git_delta_t *delta) +static int diff_scan_inside_untracked_dir( + git_diff_list *diff, diff_in_progress *info, git_delta_t *delta_type) { int error = 0; - const git_index_entry *nitem = info->nitem; - bool contains_oitem = entry_is_prefixed(diff, info->oitem, nitem); - bool recurse_into_dir = - (*delta == GIT_DELTA_UNTRACKED && - DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) || - (*delta == GIT_DELTA_IGNORED && - DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)); + git_buf base = GIT_BUF_INIT; + bool is_ignored; - /* do not advance into directories that contain a .git file */ - if (!contains_oitem && recurse_into_dir) { - git_buf *full = NULL; - if (git_iterator_current_workdir_path(&full, info->new_iter) < 0) - return -1; - if (git_path_contains_dir(full, DOT_GIT)) - recurse_into_dir = false; - } + *delta_type = GIT_DELTA_IGNORED; + git_buf_sets(&base, info->nitem->path); - /* if directory is ignored, remember ignore_prefix */ - if ((contains_oitem || recurse_into_dir) && - *delta == GIT_DELTA_UNTRACKED && - git_iterator_current_is_ignored(info->new_iter)) - { - git_buf_sets(&info->ignore_prefix, info->nitem->path); - *delta = GIT_DELTA_IGNORED; + /* advance into untracked directory */ + if ((error = git_iterator_advance_into(&info->nitem, info->new_iter)) < 0) { - /* skip recursion if we've just learned this is ignored */ - if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) - recurse_into_dir = false; - } - - if (contains_oitem || recurse_into_dir) { - /* advance into directory */ - error = git_iterator_advance_into(&info->nitem, info->new_iter); - - /* if directory is empty, can't advance into it, so skip */ + /* skip ahead if empty */ if (error == GIT_ENOTFOUND) { giterr_clear(); error = git_iterator_advance(&info->nitem, info->new_iter); - - git_buf_clear(&info->ignore_prefix); } - /* return UNMODIFIED to tell caller not to create a new record */ - *delta = GIT_DELTA_UNMODIFIED; + return error; } + /* look for actual untracked file */ + while (!diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) { + is_ignored = git_iterator_current_is_ignored(info->new_iter); + + /* need to recurse into non-ignored directories */ + if (!is_ignored && S_ISDIR(info->nitem->mode)) { + if ((error = git_iterator_advance_into( + &info->nitem, info->new_iter)) < 0) + break; + continue; + } + + /* found a non-ignored item - treat parent dir as untracked */ + if (!is_ignored) { + *delta_type = GIT_DELTA_UNTRACKED; + break; + } + + if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0) + break; + } + + /* finish off scan */ + while (!diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) { + if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0) + break; + } + + git_buf_free(&base); + return error; } @@ -757,36 +759,116 @@ static int handle_unmatched_new_item( int error = 0; const git_index_entry *nitem = info->nitem; git_delta_t delta_type = GIT_DELTA_UNTRACKED; + bool contains_oitem; - /* check if contained in ignored parent directory */ - if (git_buf_len(&info->ignore_prefix) && - diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0) - delta_type = GIT_DELTA_IGNORED; + /* check if this is a prefix of the other side */ + contains_oitem = entry_is_prefixed(diff, info->oitem, nitem); - if (S_ISDIR(nitem->mode)) { - error = handle_unmatched_new_directory(diff, info, &delta_type); - - if (error || delta_type == GIT_DELTA_UNMODIFIED) - return error; + /* check if this is contained in an ignored parent directory */ + if (git_buf_len(&info->ignore_prefix)) { + if (diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0) + delta_type = GIT_DELTA_IGNORED; + else + git_buf_clear(&info->ignore_prefix); } - /* In core git, the next two "else if" clauses are effectively - * reversed -- i.e. when an untracked file contained in an - * ignored directory is individually ignored, it shows up as an - * ignored file in the diff list, even though other untracked - * files in the same directory are skipped completely. + if (S_ISDIR(nitem->mode)) { + bool recurse_into_dir = contains_oitem; + + /* if not already inside an ignored dir, check if this is ignored */ + if (delta_type != GIT_DELTA_IGNORED && + git_iterator_current_is_ignored(info->new_iter)) + { + delta_type = GIT_DELTA_IGNORED; + git_buf_sets(&info->ignore_prefix, nitem->path); + } + + /* check if user requests recursion into this type of dir */ + recurse_into_dir = contains_oitem || + (delta_type == GIT_DELTA_UNTRACKED && + DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) || + (delta_type == GIT_DELTA_IGNORED && + DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)); + + /* do not advance into directories that contain a .git file */ + if (recurse_into_dir) { + git_buf *full = NULL; + if (git_iterator_current_workdir_path(&full, info->new_iter) < 0) + return -1; + if (full && git_path_contains_dir(full, DOT_GIT)) + recurse_into_dir = false; + } + + /* still have to look into untracked directories to match core git - + * with no untracked files, directory is treated as ignored + */ + if (!recurse_into_dir && + delta_type == GIT_DELTA_UNTRACKED && + DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_FAST_UNTRACKED_DIRS)) + { + git_diff_delta *last; + + /* attempt to insert record for this directory */ + if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0) + return error; + + /* if delta wasn't created (because of rules), just skip ahead */ + last = diff_delta__last_for_item(diff, nitem); + if (!last) + return git_iterator_advance(&info->nitem, info->new_iter); + + /* iterate into dir looking for an actual untracked file */ + if (diff_scan_inside_untracked_dir(diff, info, &delta_type) < 0) + return -1; + + /* it iteration changed delta type, the update the record */ + if (delta_type == GIT_DELTA_IGNORED) { + last->status = GIT_DELTA_IGNORED; + + /* remove the record if we don't want ignored records */ + if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED)) { + git_vector_pop(&diff->deltas); + git__free(last); + } + } + + return 0; + } + + /* try to advance into directory if necessary */ + if (recurse_into_dir) { + error = git_iterator_advance_into(&info->nitem, info->new_iter); + + /* if real error or no error, proceed with iteration */ + if (error != GIT_ENOTFOUND) + return error; + giterr_clear(); + + /* if directory is empty, can't advance into it, so either skip + * it or ignore it + */ + if (contains_oitem) + return git_iterator_advance(&info->nitem, info->new_iter); + delta_type = GIT_DELTA_IGNORED; + } + } + + /* In core git, the next two checks are effectively reversed -- + * i.e. when an file contained in an ignored directory is explicitly + * ignored, it shows up as an ignored file in the diff list, even though + * other untracked files in the same directory are skipped completely. * - * To me, this is odd. If the directory is ignored and the file - * is untracked, we should skip it consistently, regardless of - * whether it happens to match a pattern in the ignore file. + * To me, this seems odd. If the directory is ignored and the file is + * untracked, we should skip it consistently, regardless of whether it + * happens to match a pattern in the ignore file. * - * To match the core git behavior, just reverse the following - * two "else if" cases so that individual file ignores are - * checked before container directory exclusions are used to - * skip the file. + * To match the core git behavior, reverse the following two if checks + * so that individual file ignores are checked before container + * directory exclusions are used to skip the file. */ else if (delta_type == GIT_DELTA_IGNORED && - DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) + DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) + /* item contained in ignored directory, so skip over it */ return git_iterator_advance(&info->nitem, info->new_iter); else if (git_iterator_current_is_ignored(info->new_iter)) @@ -795,15 +877,16 @@ static int handle_unmatched_new_item( else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR) delta_type = GIT_DELTA_ADDED; + /* Actually create the record for this item if necessary */ if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0) return error; - /* if we are generating TYPECHANGE records then check for that - * instead of just generating an ADDED/UNTRACKED record + /* If user requested TYPECHANGE records, then check for that instead of + * just generating an ADDED/UNTRACKED record */ if (delta_type != GIT_DELTA_IGNORED && DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) && - entry_is_prefixed(diff, info->oitem, nitem)) + contains_oitem) { /* this entry was prefixed with a tree - make TYPECHANGE */ git_diff_delta *last = diff_delta__last_for_item(diff, nitem); diff --git a/src/vector.c b/src/vector.c index f4a818ed2..5ba2fab18 100644 --- a/src/vector.c +++ b/src/vector.c @@ -277,15 +277,13 @@ void git_vector_swap(git_vector *a, git_vector *b) int git_vector_resize_to(git_vector *v, size_t new_length) { - if (new_length <= v->length) - return 0; - if (new_length > v->_alloc_size && resize_vector(v, new_length) < 0) return -1; - memset(&v->contents[v->length], 0, - sizeof(void *) * (new_length - v->length)); + if (new_length > v->length) + memset(&v->contents[v->length], 0, + sizeof(void *) * (new_length - v->length)); v->length = new_length; diff --git a/tests-clar/status/status_helpers.c b/tests-clar/status/status_helpers.c index 24546d45c..f073c2491 100644 --- a/tests-clar/status/status_helpers.c +++ b/tests-clar/status/status_helpers.c @@ -40,7 +40,8 @@ int cb_status__single(const char *p, unsigned int s, void *payload) { status_entry_single *data = (status_entry_single *)payload; - GIT_UNUSED(p); + if (data->debug) + fprintf(stderr, "%02d: %s (%04x)\n", data->count, p, s); data->count++; data->status = s; diff --git a/tests-clar/status/status_helpers.h b/tests-clar/status/status_helpers.h index 1aa0263ee..ae1469e79 100644 --- a/tests-clar/status/status_helpers.h +++ b/tests-clar/status/status_helpers.h @@ -24,6 +24,7 @@ extern int cb_status__count(const char *p, unsigned int s, void *payload); typedef struct { int count; unsigned int status; + bool debug; } status_entry_single; /* cb_status__single takes payload of "status_entry_single *" */ diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index a9b8a12ed..0138b1712 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -258,9 +258,8 @@ void test_status_worktree__ignores(void) static int cb_status__check_592(const char *p, unsigned int s, void *payload) { - GIT_UNUSED(payload); - - if (s != GIT_STATUS_WT_DELETED || (payload != NULL && strcmp(p, (const char *)payload) != 0)) + if (s != GIT_STATUS_WT_DELETED || + (payload != NULL && strcmp(p, (const char *)payload) != 0)) return -1; return 0; diff --git a/tests-clar/submodule/status.c b/tests-clar/submodule/status.c index 282e82758..fca84af63 100644 --- a/tests-clar/submodule/status.c +++ b/tests-clar/submodule/status.c @@ -383,3 +383,30 @@ void test_submodule_status__iterator(void) cl_git_pass(git_status_foreach_ext(g_repo, &opts, confirm_submodule_status, &exp)); } + +void test_submodule_status__untracked_dirs_containing_ignored_files(void) +{ + git_buf path = GIT_BUF_INIT; + unsigned int status, expected; + git_submodule *sm; + + cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), "modules/sm_unchanged/info/exclude")); + cl_git_append2file(git_buf_cstr(&path), "\n*.ignored\n"); + + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged/directory")); + cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0)); + cl_git_pass(git_buf_joinpath(&path, git_buf_cstr(&path), "i_am.ignored")); + cl_git_mkfile(git_buf_cstr(&path), "ignored this file, please\n"); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); + cl_git_pass(git_submodule_status(&status, sm)); + + cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); + + expected = GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_IN_WD; + + cl_assert(status == expected); +} From a66c4bc846cb59512c1aa164211f3f912d9bc425 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 29 Apr 2013 02:57:01 -0700 Subject: [PATCH 110/384] More tests for diff untracked directories This includes more tests for various scenarios when diff includes an untracked directory in the workdir with contents either ignored or not. --- tests-clar/diff/diff_helpers.c | 10 +- tests-clar/diff/diff_helpers.h | 7 ++ tests-clar/diff/workdir.c | 187 +++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-) diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c index 19c005e2e..e7f97c034 100644 --- a/tests-clar/diff/diff_helpers.c +++ b/tests-clar/diff/diff_helpers.c @@ -28,7 +28,15 @@ int diff_file_cb( { diff_expects *e = payload; - GIT_UNUSED(progress); + if (e->debug) + fprintf(stderr, "%c %s (%.3f)\n", + git_diff_status_char(delta->status), + delta->old_file.path, progress); + + if (e->names) + cl_assert_equal_s(e->names[e->files], delta->old_file.path); + if (e->statuses) + cl_assert_equal_i(e->statuses[e->files], (int)delta->status); e->files++; diff --git a/tests-clar/diff/diff_helpers.h b/tests-clar/diff/diff_helpers.h index 674fd8e19..b39a69d1d 100644 --- a/tests-clar/diff/diff_helpers.h +++ b/tests-clar/diff/diff_helpers.h @@ -18,6 +18,13 @@ typedef struct { int line_ctxt; int line_adds; int line_dels; + + /* optional arrays of expected specific values */ + const char **names; + int *statuses; + + int debug; + } diff_expects; typedef struct { diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c index 435bd4f2c..94fd7165d 100644 --- a/tests-clar/diff/workdir.c +++ b/tests-clar/diff/workdir.c @@ -1033,3 +1033,190 @@ void test_diff_workdir__to_tree_issue_1397(void) git_diff_list_free(diff); git_tree_free(a); } + +void test_diff_workdir__untracked_directory_scenarios(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff = NULL; + diff_expects exp; + char *pathspec = NULL; + static const char *files0[] = { + "subdir/deleted_file", + "subdir/modified_file", + "subdir/new_file", + NULL + }; + static const char *files1[] = { + "subdir/deleted_file", + "subdir/directory/", + "subdir/modified_file", + "subdir/new_file", + NULL + }; + static const char *files2[] = { + "subdir/deleted_file", + "subdir/directory/more/notignored", + "subdir/modified_file", + "subdir/new_file", + NULL + }; + + g_repo = cl_git_sandbox_init("status"); + cl_git_mkfile("status/.gitignore", "ignored\n"); + + opts.context_lines = 3; + opts.interhunk_lines = 1; + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; + opts.pathspec.strings = &pathspec; + opts.pathspec.count = 1; + pathspec = "subdir"; + + /* baseline for "subdir" pathspec */ + + memset(&exp, 0, sizeof(exp)); + exp.names = files0; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + + /* empty directory */ + + cl_git_pass(p_mkdir("status/subdir/directory", 0777)); + + memset(&exp, 0, sizeof(exp)); + exp.names = files1; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + + /* directory with only ignored files */ + + cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777)); + cl_git_mkfile("status/subdir/directory/deeper/ignored", "ignore me\n"); + + cl_git_pass(p_mkdir("status/subdir/directory/another", 0777)); + cl_git_mkfile("status/subdir/directory/another/ignored", "ignore me\n"); + + memset(&exp, 0, sizeof(exp)); + exp.names = files1; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + + /* directory with ignored directory (contents irrelevant) */ + + cl_git_pass(p_mkdir("status/subdir/directory/more", 0777)); + cl_git_pass(p_mkdir("status/subdir/directory/more/ignored", 0777)); + cl_git_mkfile("status/subdir/directory/more/ignored/notignored", + "inside ignored dir\n"); + + memset(&exp, 0, sizeof(exp)); + exp.names = files1; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + + /* quick version avoids directory scan */ + + opts.flags = opts.flags | GIT_DIFF_FAST_UNTRACKED_DIRS; + + memset(&exp, 0, sizeof(exp)); + exp.names = files1; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + + /* directory with nested non-ignored content */ + + opts.flags = opts.flags & ~GIT_DIFF_FAST_UNTRACKED_DIRS; + + cl_git_mkfile("status/subdir/directory/more/notignored", + "not ignored deep under untracked\n"); + + memset(&exp, 0, sizeof(exp)); + exp.names = files1; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + + /* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */ + + opts.flags = opts.flags & ~GIT_DIFF_INCLUDE_IGNORED; + opts.flags = opts.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS; + + memset(&exp, 0, sizeof(exp)); + exp.names = files2; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); +} From 61c00541ac3c92eb3a82c4a1f67e47510ca1318b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 29 Apr 2013 06:21:56 -0700 Subject: [PATCH 111/384] Update comment for clarity --- include/git2/diff.h | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index cc16d01b6..0ef47c018 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -88,47 +88,59 @@ typedef enum { GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8), /** Include unmodified files in the diff list */ GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9), + /** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked directory * will be marked with only a single entry in the diff list; this flag * adds all files under the directory as UNTRACKED entries, too. */ GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10), + /** If the pathspec is set in the diff options, this flags means to * apply it as an exact match instead of as an fnmatch pattern. */ GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11), + /** Use case insensitive filename comparisons */ GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12), + /** When generating patch text, include the content of untracked files */ GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13), + /** Disable updating of the `binary` flag in delta records. This is * useful when iterating over a diff if you don't need hunk and data * callbacks and want to avoid having to load file completely. */ GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14), + /** Normally, a type change between files will be converted into a * DELETED record for the old and an ADDED record for the new; this * options enabled the generation of TYPECHANGE delta records. */ GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15), + /** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still * generally show as a DELETED blob. This flag tries to correctly * label blob->tree transitions as TYPECHANGE records with new_file's * mode set to tree. Note: the tree SHA will not be available. */ GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1 << 16), + /** Ignore file mode changes */ GIT_DIFF_IGNORE_FILEMODE = (1 << 17), + /** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory * will be marked with only a single entry in the diff list; this flag * adds all files under the directory as IGNORED entries, too. */ GIT_DIFF_RECURSE_IGNORED_DIRS = (1 << 18), - /** For an untracked directory, diff can immediately label it UNTRACKED, - * but this differs from core Git which scans underneath for untracked - * or ignored files and marks the directory ignored unless it contains - * untracked files under it. That search can be slow. This flag makes - * diff skip ahead and immediately report the directory as untracked. + + /** Core Git scans inside untracked directories, labeling them IGNORED + * if they are empty or only contain ignored files; a directory is + * consider UNTRACKED only if it has an actual untracked file in it. + * This scan is extra work for a case you often don't care about. This + * flag makes libgit2 immediately label an untracked directory as + * UNTRACKED without looking insde it (which differs from core Git). + * Of course, ignore rules are still checked for the directory itself. */ GIT_DIFF_FAST_UNTRACKED_DIRS = (1 << 19), } git_diff_option_t; From 5fa7e469848bef4eab19cc069df9aa3b0134c9f7 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 30 Apr 2013 04:13:39 -0700 Subject: [PATCH 112/384] Fix some formatting inconsistency --- src/diff.c | 6 ++---- src/diff_output.c | 50 +++++++++++++++++++---------------------------- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/diff.c b/src/diff.c index cea3fdb22..a154e67e8 100644 --- a/src/diff.c +++ b/src/diff.c @@ -327,8 +327,7 @@ static git_diff_list *diff_list_alloc( /* Use case-insensitive compare if either iterator has * the ignore_case bit set */ if (!git_iterator_ignore_case(old_iter) && - !git_iterator_ignore_case(new_iter)) - { + !git_iterator_ignore_case(new_iter)) { diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE; diff->strcomp = git__strcmp; @@ -777,8 +776,7 @@ static int handle_unmatched_new_item( /* if not already inside an ignored dir, check if this is ignored */ if (delta_type != GIT_DELTA_IGNORED && - git_iterator_current_is_ignored(info->new_iter)) - { + git_iterator_current_is_ignored(info->new_iter)) { delta_type = GIT_DELTA_IGNORED; git_buf_sets(&info->ignore_prefix, nitem->path); } diff --git a/src/diff_output.c b/src/diff_output.c index 4ce01bc62..64ff6b5be 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -101,8 +101,8 @@ static bool diff_delta_is_binary_forced( /* make sure files are conceivably mmap-able */ if ((git_off_t)((size_t)delta->old_file.size) != delta->old_file.size || - (git_off_t)((size_t)delta->new_file.size) != delta->new_file.size) - { + (git_off_t)((size_t)delta->new_file.size) != delta->new_file.size) { + delta->old_file.flags |= GIT_DIFF_FLAG_BINARY; delta->new_file.flags |= GIT_DIFF_FLAG_BINARY; delta->flags |= GIT_DIFF_FLAG_BINARY; @@ -232,8 +232,7 @@ static int get_blob_content( if (git_oid_iszero(&file->oid)) return 0; - if (file->mode == GIT_FILEMODE_COMMIT) - { + if (file->mode == GIT_FILEMODE_COMMIT) { char oidstr[GIT_OID_HEXSZ+1]; git_buf content = GIT_BUF_INIT; @@ -299,8 +298,8 @@ static int get_workdir_sm_content( char oidstr[GIT_OID_HEXSZ+1]; if ((error = git_submodule_lookup(&sm, ctxt->repo, file->path)) < 0 || - (error = git_submodule_status(&sm_status, sm)) < 0) - { + (error = git_submodule_status(&sm_status, sm)) < 0) { + /* GIT_EEXISTS means a "submodule" that has not been git added */ if (error == GIT_EEXISTS) error = 0; @@ -312,8 +311,8 @@ static int get_workdir_sm_content( const git_oid* sm_head; if ((sm_head = git_submodule_wd_id(sm)) != NULL || - (sm_head = git_submodule_head_id(sm)) != NULL) - { + (sm_head = git_submodule_head_id(sm)) != NULL) { + git_oid_cpy(&file->oid, sm_head); file->flags |= GIT_DIFF_FLAG_VALID_OID; } @@ -660,8 +659,8 @@ static int diff_patch_load( */ if (check_if_unmodified && delta->old_file.mode == delta->new_file.mode && - !git_oid__cmp(&delta->old_file.oid, &delta->new_file.oid)) - { + !git_oid__cmp(&delta->old_file.oid, &delta->new_file.oid)) { + delta->status = GIT_DELTA_UNMODIFIED; if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) @@ -1049,6 +1048,12 @@ char git_diff_status_char(git_delta_t status) return code; } +static int callback_error(void) +{ + giterr_clear(); + return GIT_EUSER; +} + static int print_compact( const git_diff_delta *delta, float progress, void *data) { @@ -1083,10 +1088,7 @@ static int print_compact( if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - { - giterr_clear(); - return GIT_EUSER; - } + return callback_error(); return 0; } @@ -1200,10 +1202,7 @@ static int print_patch_file( if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - { - giterr_clear(); - return GIT_EUSER; - } + return callback_error(); if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) return 0; @@ -1217,10 +1216,7 @@ static int print_patch_file( if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY, git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - { - giterr_clear(); - return GIT_EUSER; - } + return callback_error(); return 0; } @@ -1243,10 +1239,7 @@ static int print_patch_hunk( if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - { - giterr_clear(); - return GIT_EUSER; - } + return callback_error(); return 0; } @@ -1278,10 +1271,7 @@ static int print_patch_line( if (pi->print_cb(delta, range, line_origin, git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - { - giterr_clear(); - return GIT_EUSER; - } + return callback_error(); return 0; } From bade51948c08c36ac0bea63cf62ee1a9dd952501 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 30 Apr 2013 21:02:13 +0200 Subject: [PATCH 113/384] lol namespaces --- include/git2/repository.h | 22 ++++++++++++++++++++++ src/refdb_fs.c | 11 +++++++++-- src/repository.c | 18 ++++++++++++++++++ src/repository.h | 1 + 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/include/git2/repository.h b/include/git2/repository.h index 08024cd89..cd238e17c 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -626,6 +626,28 @@ typedef enum { */ GIT_EXTERN(int) git_repository_state(git_repository *repo); +/** + * Sets the active namespace for this Git Repository + * + * This namespace affects all reference operations for the repo. + * See `man gitnamespaces` + * + * @param repo The repo + * @param nmspace The namespace. This should not include the refs + * folder, e.g. to namespace all references under `refs/namespaces/foo/`, + * use `foo` as the namespace. + * @return 0 on success, -1 on error + */ +GIT_EXTERN(int) git_repository_set_namespace(git_repository *repo, const char *nmspace); + +/** + * Get the currently active namespace for this repository + * + * @param repo The repo + * @return the active namespace, or NULL if there isn't one + */ +GIT_EXTERN(const char *) git_repository_get_namespace(git_repository *repo); + /** @} */ GIT_END_DECL #endif diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 2f2e67104..5228cb811 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -41,7 +41,7 @@ typedef struct refdb_fs_backend { git_refdb_backend parent; git_repository *repo; - const char *path; + char *path; git_refcache refcache; } refdb_fs_backend; @@ -993,6 +993,7 @@ static void refdb_fs_backend__free(git_refdb_backend *_backend) backend = (refdb_fs_backend *)_backend; refcache_free(&backend->refcache); + git__free(backend->path); git__free(backend); } @@ -1000,13 +1001,19 @@ int git_refdb_backend_fs( git_refdb_backend **backend_out, git_repository *repository) { + git_buf path = GIT_BUF_INIT; refdb_fs_backend *backend; backend = git__calloc(1, sizeof(refdb_fs_backend)); GITERR_CHECK_ALLOC(backend); backend->repo = repository; - backend->path = repository->path_repository; + + git_buf_puts(&path, repository->path_repository); + if (repository->namespace != NULL) + git_buf_printf(&path, "refs/%s/", repository->namespace); + + backend->path = git_buf_detach(&path); backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; diff --git a/src/repository.c b/src/repository.c index 2161aa697..e6eaf753c 100644 --- a/src/repository.c +++ b/src/repository.c @@ -111,6 +111,7 @@ void git_repository_free(git_repository *repo) git__free(repo->path_repository); git__free(repo->workdir); + git__free(repo->namespace); git__free(repo); } @@ -764,6 +765,23 @@ void git_repository_set_index(git_repository *repo, git_index *index) set_index(repo, index); } +int git_repository_set_namespace(git_repository *repo, const char *namespace) +{ + git__free(repo->namespace); + + if (namespace == NULL) { + repo->namespace = NULL; + return 0; + } + + return (repo->namespace = git__strdup(namespace)) ? 0 : -1; +} + +const char *git_repository_get_namespace(git_repository *repo) +{ + return repo->namespace; +} + static int check_repositoryformatversion(git_config *config) { int version; diff --git a/src/repository.h b/src/repository.h index f7f9ecb1f..bd5f63dac 100644 --- a/src/repository.h +++ b/src/repository.h @@ -111,6 +111,7 @@ struct git_repository { char *path_repository; char *workdir; + char *namespace; unsigned is_bare:1; unsigned int lru_counter; From bec65a5e994bc4701216c9ca2c7dae83770b3edc Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 1 Apr 2013 22:16:21 -0500 Subject: [PATCH 114/384] merge! --- docs/merge-df_conflicts.txt | 41 + include/git2/index.h | 6 +- include/git2/merge.h | 64 +- src/index.c | 19 +- src/merge.c | 854 +++++++++++++++++- src/merge.h | 115 ++- src/merge_file.c | 175 ++++ src/merge_file.h | 71 ++ tests-clar/merge/merge_helpers.c | 224 +++++ tests-clar/merge/merge_helpers.h | 62 ++ tests-clar/merge/trees/automerge.c | 217 +++++ tests-clar/merge/trees/modeconflict.c | 59 ++ tests-clar/merge/trees/treediff.c | 279 ++++++ tests-clar/merge/trees/trivial.c | 396 ++++++++ tests-clar/merge/{ => workdir}/setup.c | 21 +- .../merge-resolve/.gitted/COMMIT_EDITMSG | 1 + .../resources/merge-resolve/.gitted/HEAD | 1 + .../resources/merge-resolve/.gitted/ORIG_HEAD | 1 + .../resources/merge-resolve/.gitted/config | 6 + .../merge-resolve/.gitted/description | 1 + .../resources/merge-resolve/.gitted/index | Bin 0 -> 624 bytes .../resources/merge-resolve/.gitted/logs/HEAD | 236 +++++ .../.gitted/logs/refs/heads/branch | 2 + .../.gitted/logs/refs/heads/df_ancestor | 5 + .../.gitted/logs/refs/heads/df_side1 | 14 + .../.gitted/logs/refs/heads/df_side2 | 9 + .../.gitted/logs/refs/heads/ff_branch | 5 + .../.gitted/logs/refs/heads/master | 5 + .../.gitted/logs/refs/heads/octo1 | 2 + .../.gitted/logs/refs/heads/octo2 | 2 + .../.gitted/logs/refs/heads/octo3 | 2 + .../.gitted/logs/refs/heads/octo4 | 2 + .../.gitted/logs/refs/heads/octo5 | 2 + .../.gitted/logs/refs/heads/octo6 | 3 + .../.gitted/logs/refs/heads/renames1 | 2 + .../.gitted/logs/refs/heads/renames2 | 3 + .../.gitted/logs/refs/heads/trivial-10 | 3 + .../.gitted/logs/refs/heads/trivial-10-branch | 2 + .../.gitted/logs/refs/heads/trivial-11 | 3 + .../.gitted/logs/refs/heads/trivial-11-branch | 2 + .../.gitted/logs/refs/heads/trivial-13 | 3 + .../.gitted/logs/refs/heads/trivial-13-branch | 2 + .../.gitted/logs/refs/heads/trivial-14 | 3 + .../.gitted/logs/refs/heads/trivial-14-branch | 2 + .../.gitted/logs/refs/heads/trivial-2alt | 2 + .../logs/refs/heads/trivial-2alt-branch | 2 + .../.gitted/logs/refs/heads/trivial-3alt | 3 + .../logs/refs/heads/trivial-3alt-branch | 1 + .../.gitted/logs/refs/heads/trivial-4 | 2 + .../.gitted/logs/refs/heads/trivial-4-branch | 2 + .../.gitted/logs/refs/heads/trivial-5alt-1 | 2 + .../logs/refs/heads/trivial-5alt-1-branch | 2 + .../.gitted/logs/refs/heads/trivial-5alt-2 | 3 + .../logs/refs/heads/trivial-5alt-2-branch | 2 + .../.gitted/logs/refs/heads/trivial-6 | 3 + .../.gitted/logs/refs/heads/trivial-6-branch | 2 + .../.gitted/logs/refs/heads/trivial-7 | 3 + .../.gitted/logs/refs/heads/trivial-7-branch | 5 + .../.gitted/logs/refs/heads/trivial-8 | 3 + .../.gitted/logs/refs/heads/trivial-8-branch | 2 + .../.gitted/logs/refs/heads/trivial-9 | 3 + .../.gitted/logs/refs/heads/trivial-9-branch | 2 + .../.gitted/logs/refs/heads/unrelated | 1 + .../00/5b6fcc8fec71d2550bef8462d169b3c26aa14b | Bin 0 -> 168 bytes .../00/9b9cab6fdac02915a88ecd078b7a792ed802d8 | Bin 0 -> 164 bytes .../00/c7d33f1ffa79d19c2272b370fcaeaadba49c08 | Bin 0 -> 147 bytes .../01/f149e1b8f84bd8896aaff6d6b22af88459ded0 | Bin 0 -> 166 bytes .../02/04a84f822acbf6386b36d33f1f6bc68bbbf858 | Bin 0 -> 168 bytes .../02/251f990ca8e92e7ae61d3426163fa821c64001 | Bin 0 -> 264 bytes .../03/21415405cb906c46869919af56d51dbbe5e85c | Bin 0 -> 271 bytes .../03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0 | Bin 0 -> 29 bytes .../03/b87706555accbf874ccd410dbda01e8e70a67f | Bin 0 -> 353 bytes .../03/dad1005e5d06d418f50b12e0bcd48ff2306a03 | Bin 0 -> 264 bytes .../05/1ffd7901a442faf56b226161649074f15c7c47 | 1 + .../05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe | Bin 0 -> 63 bytes .../05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c | Bin 0 -> 170 bytes .../07/a759da919f737221791d542f176ab49c88837f | Bin 0 -> 165 bytes .../07/c514b04698e068892b31c8d352b85813b99c6e | Bin 0 -> 32 bytes .../09/055301463b7f2f8ee5d368f8ed5c0a40ad8515 | Bin 0 -> 41 bytes .../09/17bb159596aea4d295f4857da77e8f96b3c7dc | Bin 0 -> 36 bytes .../09/2ce8682d7f3a2a3a769a6daca58950168ba5c4 | Bin 0 -> 163 bytes .../09/3bebf072dd4bbba88833667d6ffe454df199e1 | Bin 0 -> 266 bytes .../09/768bed22680cdb0859683fa9677ccc8d5a25c1 | Bin 0 -> 275 bytes .../0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7 | Bin 0 -> 275 bytes .../0c/fd6c54ef6532d862408f562309dc9c74a401e8 | Bin 0 -> 28 bytes .../0d/52e3a556e189ba0948ae56780918011c1b167d | Bin 0 -> 235 bytes .../0d/872f8e871a30208305978ecbf9e66d864f1638 | Bin 0 -> 89 bytes .../0e/c5f433959cd46177f745903353efb5be08d151 | Bin 0 -> 165 bytes .../11/deab00b2d3a6f5a3073988ac050c2d7b6655e2 | Bin 0 -> 34 bytes .../11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa | 1 + .../13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e | Bin 0 -> 168 bytes .../14/39088f509b79b1535b64193137d3ce4b240734 | Bin 0 -> 58 bytes .../15/8dc7bedb202f5b26502bf3574faa7f4238d56c | 2 + .../16/f825815cfd20a07a75c71554e82d8eede0b061 | 1 + .../17/8940b450f238a56c0d75b7955cb57b38191982 | Bin 0 -> 65 bytes .../18/3310e30fb1499af8c619108ffea4d300b5e778 | Bin 0 -> 170 bytes .../18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9 | Bin 0 -> 68 bytes .../19/b7ac485269b672a101060894de3ba9c2a24dd1 | Bin 0 -> 53 bytes .../1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b | Bin 0 -> 33 bytes .../1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99 | Bin 0 -> 29 bytes .../1f/81433e3161efbf250576c58fede7f6b836f3d3 | Bin 0 -> 262 bytes .../20/91d94c8bd3eb0835dc5220de5e8bb310fa1513 | Bin 0 -> 271 bytes .../21/671e290278286fb2ce4c63d01699b67adce331 | Bin 0 -> 79 bytes .../22/7792b52aaa0b238bea00ec7e509b02623f168c | Bin 0 -> 102 bytes .../23/3c0919c998ed110a4b6ff36f353aec8b713487 | Bin 0 -> 43 bytes .../23/92a2dacc9efb562b8635d6579fb458751c7c5b | Bin 0 -> 142 bytes .../24/1a1005cd9b980732741b74385b891142bcba28 | Bin 0 -> 67 bytes .../24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d | Bin 0 -> 30 bytes .../24/90b9f1a079420870027deefb49f51d6656cf74 | Bin 0 -> 268 bytes .../25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3 | Bin 0 -> 381 bytes .../25/c40b7660c08c8fb581f770312f41b9b03119d1 | Bin 0 -> 31 bytes .../26/153a3ff3649b6c2bb652d3f06878c6e0a172f9 | Bin 0 -> 48 bytes .../27/133da702ba3c60af2a01e96c2555ff4045d692 | Bin 0 -> 32 bytes .../2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add | Bin 0 -> 147 bytes .../2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136 | Bin 0 -> 53 bytes .../2b/d0a343aeef7a2cf0d158478966a6e587ff3863 | Bin 0 -> 56 bytes .../2d/a538570bc1e5b2c3e855bf702f35248ad0735f | 2 + .../2f/2e37b7ebbae467978610896ca3aafcdad2ee67 | Bin 0 -> 52 bytes .../2f/4024ce528d36d8670c289cce5a7963e625bb0c | Bin 0 -> 179 bytes .../2f/56120107d680129a5d9791b521cb1e73a2ed31 | 3 + .../2f/598248eeccfc27e5ca44d9d96383f6dfea7b16 | 1 + .../31/68dca1a561889b045a6441909f4c56145e666d | 2 + .../31/d5472536041a83d986829240bbbdc897c6f8a6 | Bin 0 -> 41 bytes .../32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b | Bin 0 -> 47 bytes .../33/46d64325b39e5323733492cd55f808994a2475 | Bin 0 -> 33 bytes .../33/d500f588fbbe65901d82b4e6b008e549064be0 | 2 + .../34/bfafff88eaf118402b44e6f3e2dbbf1a582b05 | 1 + .../35/0c6eb3010efc403a6bed682332635314e9ed58 | Bin 0 -> 92 bytes .../35/411bfb77cd2cc431f3a03a2b4976ed94b5d241 | Bin 0 -> 31 bytes .../35/4704d3613ad4228e4786fc76656b11e98236c4 | Bin 0 -> 41 bytes .../35/632e43612c06a3ea924bfbacd48333da874c29 | 1 + .../35/75826c96a975031d2c14368529cc5c4353a8fd | Bin 0 -> 163 bytes .../36/219b49367146cb2e6a1555b5a9ebd4d0328495 | Bin 0 -> 68 bytes .../36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3 | Bin 0 -> 35 bytes .../37/48859b001c6e627e712a07951aee40afd19b41 | Bin 0 -> 41 bytes .../38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced | 2 + .../3b/47b031b3e55ae11e14a05260b1c3ffd6838d55 | Bin 0 -> 161 bytes .../3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f | Bin 0 -> 269 bytes .../3e/f4d30382ca33fdeba9fda895a99e0891ba37aa | Bin 0 -> 36 bytes .../3e/f9bfe82f9635518ae89152322f3b46fd4ba25b | Bin 0 -> 172 bytes .../40/2784a46a4a3982294231594cbeb431f506d22c | Bin 0 -> 83 bytes .../41/2b32fb66137366147f1801ecc962452757d48a | 2 + .../43/aafd43bea779ec74317dc361f45ae3f532a505 | Bin 0 -> 37 bytes .../43/c338656342227a3a3cd3aa85cbf784061f5425 | Bin 0 -> 266 bytes .../45/299c1ca5e07bba1fd90843056fb559f96b1f5a | Bin 0 -> 58 bytes .../46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289 | Bin 0 -> 382 bytes .../47/6dbb3e207313d1d8aaa120c6ad204bf1295e53 | Bin 0 -> 522 bytes .../47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 | Bin 0 -> 165 bytes .../49/130a28ef567af9a6a6104c38773fedfa5f9742 | Bin 0 -> 37 bytes .../49/9df817155e4bdd3c6ee192a72c52f481818230 | Bin 0 -> 35 bytes .../4a/9550ebcc97ce22b22f45af7b829bb030d003f5 | Bin 0 -> 53 bytes .../4b/253da36a0ae8bfce63aeabd8c5b58429925594 | 2 + .../4b/48deed3a433909bfd6b6ab3d4b91348b6af464 | Bin 0 -> 24 bytes .../4b/825dc642cb6eb9a060e54bf8d69288fbee4904 | Bin 0 -> 15 bytes .../4c/9fac0707f8d4195037ae5a681aa48626491541 | Bin 0 -> 167 bytes .../4c/a408a8c88655f7586a1b580be6fad138121e98 | Bin 0 -> 159 bytes .../4e/0d9401aee78eb345a8685a859d37c8c3c0bbed | Bin 0 -> 262 bytes .../4e/886e602529caa9ab11d71f86634bd1b6e0de10 | Bin 0 -> 56 bytes .../4e/b04c9e79e88f6640d01ff5b25ca2a60764f216 | Bin 0 -> 34 bytes .../4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6 | Bin 0 -> 161 bytes .../50/12fd565b1393bdfda1805d4ec38ce6619e1fd1 | Bin 0 -> 29 bytes .../50/4f75ac95a71ef98051817618576a68505b92f9 | Bin 0 -> 93 bytes .../50/84fc2a88b6bdba8db93bd3953a8f4fdb470238 | Bin 0 -> 53 bytes .../50/ce7d7d01217679e26c55939eef119e0c93e272 | Bin 0 -> 159 bytes .../51/95a1b480f66691b667f10a9e41e70115a78351 | Bin 0 -> 170 bytes .../52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 | 3 + .../53/825f41ac8d640612f9423a2f03a69f3d96809a | Bin 0 -> 164 bytes .../54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae | 2 + .../54/59c89aa0026d543ce8343bd89871bce543f9c2 | 3 + .../54/7607c690372fe81fab8e3bb44c530e129118fd | Bin 0 -> 58 bytes .../55/b4e4687e7a0d9ca367016ed930f385d4022e6f | 1 + .../56/6ab53c220a2eafc1212af1a024513230280ab9 | 3 + .../56/a638b76b75e068590ac999c2f8621e7f3e264c | 1 + .../57/079a46233ae2b6df62e9ade71c4948512abefb | Bin 0 -> 168 bytes .../58/43febcb23480df0b5edb22a21c59c772bb8e29 | Bin 0 -> 71 bytes .../58/e853f66699fd02629fd50bde08082bc005933a | Bin 0 -> 160 bytes .../59/6803b523203a4851c824c07366906f8353f4ad | Bin 0 -> 163 bytes .../5c/2411f8075f48a6b2fdb85ebc0d371747c4df15 | Bin 0 -> 37 bytes .../5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d | Bin 0 -> 37 bytes .../5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5 | Bin 0 -> 40 bytes .../5d/c1018e90b19654bee986b7a0c268804d39659d | Bin 0 -> 168 bytes .../5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f | Bin 0 -> 43 bytes .../5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d | Bin 0 -> 268 bytes .../5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165 | Bin 0 -> 283 bytes .../60/91fc2c036a382a69489e3f518ee5aae9a4e567 | Bin 0 -> 258 bytes .../61/340eeed7340fa6a8792def9a5938bb5d4434bb | Bin 0 -> 92 bytes .../61/78885b38fe96e825ac0f492c0a941f288b37f6 | Bin 0 -> 289 bytes .../62/12c31dab5e482247d7977e4f0dd3601decf13b | Bin 0 -> 45 bytes .../62/269111c3b02a9355badcb9da8678b1bf41787b | Bin 0 -> 269 bytes .../62/c4f6533c9a3894191fdcb96a3be935ade63f1a | Bin 0 -> 53 bytes .../63/247125386de9ec90a27ad36169307bf8a11a38 | 1 + .../67/18a45909532d1fcf5600d0877f7fe7e78f0b86 | 1 + .../68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e | Bin 0 -> 55 bytes .../69/f570c57b24ea7c086e94c5e574964798321435 | Bin 0 -> 266 bytes .../6a/e1a3967031a42cf955d9d5c2395211ac82f6cf | Bin 0 -> 272 bytes .../6b/7e37be8ce0b897093f2878a9dcd8f396beda2c | Bin 0 -> 53 bytes .../6c/06dcd163587c2cc18be44857e0b71116382aeb | Bin 0 -> 30 bytes .../6f/32739c3724d1d5f855299309f388606f407468 | Bin 0 -> 630 bytes .../6f/a33014764bf1120a454eb8437ae098238e409b | Bin 0 -> 168 bytes .../6f/be9fb85c86d7d1435f728da418bdff52c640a9 | Bin 0 -> 83 bytes .../71/17467b18605a660ebe5586df69e2311ed5609f | Bin 0 -> 265 bytes .../71/2ebba6669ea847d9829e4f1059d6c830c8b531 | Bin 0 -> 152 bytes .../71/add2d7b93d55bf3600f8a1582beceebbd050c8 | Bin 0 -> 264 bytes .../74/df13f0793afdaa972150bba976f7de8284914e | Bin 0 -> 26 bytes .../75/a811bf6bc57694adb3fe604786f3a4efd1cd1b | 2 + .../76/63fce0130db092936b137cabd693ec234eb060 | Bin 0 -> 49 bytes .../76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9 | Bin 0 -> 37 bytes .../7a/a3edf2bcfee22398e6b55295aa56366b7aaf76 | Bin 0 -> 271 bytes .../7c/2c5228c9e90170d4a35e6558e47163daf092e5 | Bin 0 -> 172 bytes .../7c/b63eed597130ba4abb87b3e544b85021905520 | 3 + .../7e/2d058d5fedf8329db44db4fac610d6b1a89159 | Bin 0 -> 165 bytes .../7f/7a2da58126226986d71c6ddfab4afba693280d | Bin 0 -> 199 bytes .../80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b | Bin 0 -> 168 bytes .../81/87117062b750eed4f93fd7e899f17b52ce554d | Bin 0 -> 170 bytes .../83/07d93a155903a5c49576583f0ce1f6ff897c0e | Bin 0 -> 30 bytes .../83/824a8c6658768e2013905219cc8c64cc3d9a2e | Bin 0 -> 382 bytes .../84/9619b03ae540acee4d1edec96b86993da6b497 | 3 + .../84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe | Bin 0 -> 41 bytes .../86/088dae8bade454995b21a1c88107b0e1accdab | Bin 0 -> 47 bytes .../87/b4926260d77a3b851e71ecce06839bd650b231 | Bin 0 -> 43 bytes .../88/e185910a15cd13bdf44854ad037f4842b03b29 | Bin 0 -> 177 bytes .../8a/ad9d0ea334951da47b621a475b39cc6ed759bf | Bin 0 -> 51 bytes .../8a/ae714f7d939309d7f132b30646d96743134a9f | 1 + .../8b/095d8fd01594f4d14454d073e3ac57b9ce485f | Bin 0 -> 201 bytes .../8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a | 1 + .../8c/749d9968d4b10dcfb06c9f97d0e5d92d337071 | 2 + .../8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa | Bin 0 -> 164 bytes .../90/a336c7dacbe295159413559b0043b8bdc60d57 | Bin 0 -> 271 bytes .../91/2b2d7819cf9c1029e414883857ed61d597a1a5 | Bin 0 -> 295 bytes .../91/8bb3e09090a9995d48af9a2a6296d7e6088d1c | Bin 0 -> 38 bytes .../92/7d4943cdbdc9a667db8e62cfd0a41870235c51 | Bin 0 -> 535 bytes .../93/77fccdb210540b8c0520cc6e80eb632c20bd25 | Bin 0 -> 53 bytes .../94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a | 2 + .../94/8ba6e701c1edab0c2d394fb7c5538334129793 | Bin 0 -> 71 bytes .../95/646149ab6b6ba6edc83cff678582538b457b2b | 3 + .../95/9de65e568274120fdf9e3af9f77b1550122149 | Bin 0 -> 40 bytes .../96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4 | Bin 0 -> 53 bytes .../97/7c696519c5a3004c5f1d15d60c89dbeb8f235f | Bin 0 -> 160 bytes .../98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714 | 1 + .../98/d52d07c0b0bbf2b46548f6aa521295c2cb55db | 3 + .../99/b4f7e4f24470fa06b980bc21f1095c2a9425c0 | Bin 0 -> 164 bytes .../9a/301fbe6fada7dcb74fcd7c20269b5c743459a7 | Bin 0 -> 163 bytes .../9a/f731fa116d1eb9a6c0109562472cfee6f5a979 | Bin 0 -> 48 bytes .../9c/0b6c34ef379a42d858f03fef38630f476b9102 | Bin 0 -> 38 bytes .../9e/7f4359c469f309b6057febf4c6e80742cbed5b | Bin 0 -> 539 bytes .../9e/fe7723802d4305142eee177e018fee1572c4f4 | Bin 0 -> 36 bytes .../9f/74397a3397b3585faf09e9926b110d7f654254 | Bin 0 -> 621 bytes .../a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4 | Bin 0 -> 37 bytes .../a3/9a620dae5bc8b4e771cd4d251b7d080401a21e | Bin 0 -> 29 bytes .../a3/fabece9eb8748da810e1e08266fef9b7136ad4 | Bin 0 -> 164 bytes .../a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27 | Bin 0 -> 92 bytes .../a4/3150a738849c59376cf30bb2a68348a83c8f48 | Bin 0 -> 162 bytes .../a5/563304ddf6caba25cb50323a2ea6f7dbfcadca | Bin 0 -> 48 bytes .../a7/08b253bd507417ec42d1467a7fd2d7519c4956 | Bin 0 -> 40 bytes .../a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b | Bin 0 -> 170 bytes .../a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543 | Bin 0 -> 65 bytes .../a7/dbfcbfc1a60709cb80b5ca24539008456531d0 | 1 + .../a8/02e06f1782a9645b9851bc7202cee74a8a4972 | Bin 0 -> 172 bytes .../a8/87dd39ad3edd610fc9083dcb61e40ab50673d1 | 1 + .../a9/0bc3fb6f15181972a2959a921429efbd81a473 | 2 + .../ab/40af3cb8a3ed2e2843e96d9aa7871336b94573 | Bin 0 -> 161 bytes .../ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b | Bin 0 -> 33 bytes .../ab/929391ac42572f92110f3deeb4f0844a951e22 | Bin 0 -> 40 bytes .../ac/4045f965119e6998f4340ed0f411decfb3ec05 | Bin 0 -> 29 bytes .../ad/a14492498136771f69dd451866cabcb0e9ef9a | Bin 0 -> 39 bytes .../ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87 | 1 + .../b2/d399ae15224e1d58066e3c8df70ce37de7a656 | 2 + .../b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11 | 5 + .../b6/9fe837e4cecfd4c9a40cdca7c138468687df07 | 2 + .../b6/f610aef53bd343e6c96227de874c66f00ee8e8 | Bin 0 -> 162 bytes .../b7/a2576f9fc20024ac9ef17cb134acbd1ac73127 | Bin 0 -> 320 bytes .../b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d | Bin 0 -> 286 bytes .../ba/cac9b3493509aa15e1730e1545fc0919d1dae0 | Bin 0 -> 29 bytes .../bc/744705e1d8a019993cf88f62bc4020f1b80919 | 2 + .../bc/95c75d59386147d1e79a87c33068d8dbfd71f2 | Bin 0 -> 348 bytes .../bd/593285fc7fe4ca18ccdbabf027f5d689101452 | Bin 0 -> 159 bytes .../bd/867fbae2faa80b920b002b80b1c91bcade7784 | Bin 0 -> 48 bytes .../bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35 | Bin 0 -> 51 bytes .../be/f6e37b3ee632ba74159168836f382fed21d77d | 2 + .../c0/6a9be584ac49aa02c5551312d9e2982c91df10 | Bin 0 -> 348 bytes .../c1/b17981db0840109a820dae8674ee29684134ff | Bin 0 -> 348 bytes .../c1/b6a51bbb87c2f82b161412c3d20b59fc69b090 | Bin 0 -> 47 bytes .../c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a | Bin 0 -> 162 bytes .../c3/d02eeef75183df7584d8d13ac03053910c1301 | Bin 0 -> 67 bytes .../c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618 | Bin 0 -> 538 bytes .../c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd | 1 + .../c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c | Bin 0 -> 269 bytes .../c6/07fc30883e335def28cd686b51f6cfa02b06ec | 2 + .../c6/92ecf62007c0ac9fb26e2aa884de2933de15ed | Bin 0 -> 40 bytes .../c8/f06f2e3bb2964174677e91f0abead0e43c9e5d | Bin 0 -> 45 bytes .../c9/174cef549ec94ecbc43ef03cdc775b4950becb | 2 + .../c9/4b27e41064c521120627e07e2035cca1d24ffa | Bin 0 -> 162 bytes .../ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b | Bin 0 -> 40 bytes .../cb/491780d82e46dc88a065b965ab307a038f2bc2 | Bin 0 -> 163 bytes .../cb/6693a788715b82440a54e0eacd19ba9f6ec559 | Bin 0 -> 41 bytes .../cc/3e3009134cb88014129fc8858d1101359e5e2f | 2 + .../ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3 | Bin 0 -> 48 bytes .../ce/e656c392ad0557b3aae0fb411475c206e2926f | Bin 0 -> 32 bytes .../cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d | Bin 0 -> 29 bytes .../d0/7ec190c306ec690bac349e87d01c4358e49bb2 | 2 + .../d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb | Bin 0 -> 51 bytes .../d2/f8637f2eab2507a1e13cbc9df4729ec386627e | Bin 0 -> 268 bytes .../d3/719a5ae8e4d92276b5313ce976f6ee5af2b436 | 2 + .../d3/7aa3bbfe1c0c49b909781251b956dbabe85f96 | Bin 0 -> 80 bytes .../d3/7ad72a2052685fc6201c2af90103ad42d2079b | Bin 0 -> 233 bytes .../d4/207f77243500bec335ab477f9227fcdb1e271a | 2 + .../d4/27e0b2e138501a3d15cc376077a3631e15bd46 | Bin 0 -> 38 bytes .../d5/093787ef302b941b6aab081b99fb4880038bd8 | Bin 0 -> 30 bytes .../d5/a61b0b4992a4f0caa887fa08b52431e727bb6f | Bin 0 -> 81 bytes .../d5/b6fc965c926a1bfc9ee456042b94088b5c5d21 | Bin 0 -> 319 bytes .../d5/ec1152fe25e9fec00189eb00b3db71db24c218 | Bin 0 -> 24 bytes .../d6/42b9770c66bba94a08df09b5efb095001f76d7 | Bin 0 -> 539 bytes .../d6/462fa3f5292857db599c54aea2bf91616230c5 | Bin 0 -> 48 bytes .../d6/cf6c7741b3316826af1314042550c97ded1d50 | 2 + .../d8/74671ef5b20184836cb983bb273e5280384d0b | Bin 0 -> 162 bytes .../d8/fa77b6833082c1ea36b7828a582d4c43882450 | 1 + .../d9/63979c237d08b6ba39062ee7bf64c7d34a27f8 | Bin 0 -> 48 bytes .../da/178208145ef585a1bd5ca5f4c9785d738df2cf | Bin 0 -> 41 bytes .../db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5 | Bin 0 -> 624 bytes .../dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9 | Bin 0 -> 36 bytes .../de/872ee3618b894992e9d1e18ba2ebe256a112f9 | 1 + .../df/e3f22baa1f6fce5447901c3086bae368de6bdd | Bin 0 -> 40 bytes .../e0/67f9361140f19391472df8a82d6610813c73b7 | Bin 0 -> 53 bytes .../e1/129b3cfb5898e0fbd606e0cb80b2755e50d161 | Bin 0 -> 92 bytes .../e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9 | Bin 0 -> 264 bytes .../e2/c6abbd55fed5ac71a5f2751e29b4a34726a595 | 1 + .../e3/1e7ad3ed298f24e383c4950f4671993ec078e4 | Bin 0 -> 210 bytes .../e3/76fbdd06ebf021c92724da9f26f44212734e3e | 3 + .../e4/9f917b448d1340b31d76e54ba388268fd4c922 | Bin 0 -> 36 bytes .../e4/f618a2c3ed0669308735727df5ebf2447f022f | 2 + .../e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 | Bin 0 -> 160 bytes .../e8/107f24196736b870a318a0e28f048e29f6feff | 3 + .../e9/2cdb7017dc6c5aed25cb4202c5b0104b872246 | Bin 0 -> 48 bytes .../e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5 | Bin 0 -> 36 bytes .../e9/f48beccc62d535739bfbdebe0a55ed716d8366 | Bin 0 -> 382 bytes .../eb/c09d0137cfb0c26697aed0109fb943ad906f3f | Bin 0 -> 166 bytes .../ec/67e5a86adff465359f1c8f995e12dbdfa08d8a | Bin 0 -> 166 bytes .../ed/9523e62e453e50dd9be1606af19399b96e397a | Bin 0 -> 87 bytes .../ee/1d6f164893c1866a323f072eeed36b855656be | Bin 0 -> 291 bytes .../ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf | Bin 0 -> 64 bytes .../ee/a9286df54245fea72c5b557291470eb825f38f | Bin 0 -> 235 bytes .../ef/58fdd8086c243bdc81f99e379acacfd21d32d6 | 2 + .../ef/c499524cf105d5264ac7fc54e07e95764e8075 | Bin 0 -> 32 bytes .../ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09 | Bin 0 -> 160 bytes .../f0/053b8060bb3f0be5cbcc3147a07ece26bf097e | Bin 0 -> 163 bytes .../f0/ce2b8e4986084d9b308fb72709e414c23eb5e6 | Bin 0 -> 125 bytes .../f2/0c9063fa0bda9a397c96947a7b687305c49753 | Bin 0 -> 29 bytes .../f2/9e7fb590551095230c6149cbe72f2e9104a796 | Bin 0 -> 41 bytes .../f3/293571dcd708b6a3faf03818cd2844d000e198 | 1 + .../f3/f1164b68b57b1995b658a828320e6df3081fae | Bin 0 -> 310 bytes .../f4/15caf3fcad16304cb424b67f0ee6b12dc03aae | Bin 0 -> 320 bytes .../f4/8097eb340dc5a7cae55aabcf1faf4548aa821f | Bin 0 -> 165 bytes .../f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2 | Bin 0 -> 42 bytes .../f5/b50c85a87cac64d7eb3254cdd1aec9564c0293 | Bin 0 -> 35 bytes .../f5/f9dd5886a6ee20272be0aafc790cba43b31931 | Bin 0 -> 244 bytes .../f6/be049e284c0f9dcbbc745543885be3502ea521 | Bin 0 -> 265 bytes .../f7/c332bd4d4d4b777366cae4d24d1687477576bf | Bin 0 -> 156 bytes .../f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884 | 2 + .../fa/c03f2c5139618d87d53614c153823bf1f31396 | Bin 0 -> 76 bytes .../fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d | Bin 0 -> 168 bytes .../fb/738a106cfd097a4acb96ce132ecb1ad6c46b03 | Bin 0 -> 264 bytes .../fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08 | Bin 0 -> 29 bytes .../fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99 | Bin 0 -> 575 bytes .../fc/90237dc4891fa6c69827fc465632225e391618 | Bin 0 -> 163 bytes .../fd/57d2d6770fad8e9959124793a17f441b571e66 | Bin 0 -> 279 bytes .../fd/89f8cffb663ac89095a0f9764902e93ceaca6a | 2 + .../fe/5407fc50a53aecb41d1a6e9ea7b612e581af87 | Bin 0 -> 48 bytes .../ff/49d07869831ad761bbdaea026086f8789bcb00 | Bin 0 -> 24 bytes .../ff/b312248d607284c290023f9502eea010d34efd | Bin 0 -> 68 bytes .../merge-resolve/.gitted/refs/heads/branch | 1 + .../.gitted/refs/heads/df_ancestor | 1 + .../merge-resolve/.gitted/refs/heads/df_side1 | 1 + .../merge-resolve/.gitted/refs/heads/df_side2 | 1 + .../.gitted/refs/heads/ff_branch | 1 + .../merge-resolve/.gitted/refs/heads/master | 1 + .../merge-resolve/.gitted/refs/heads/octo1 | 1 + .../merge-resolve/.gitted/refs/heads/octo2 | 1 + .../merge-resolve/.gitted/refs/heads/octo3 | 1 + .../merge-resolve/.gitted/refs/heads/octo4 | 1 + .../merge-resolve/.gitted/refs/heads/octo5 | 1 + .../merge-resolve/.gitted/refs/heads/octo6 | 1 + .../refs/heads/rename_conflict_ancestor | 1 + .../.gitted/refs/heads/rename_conflict_ours | 1 + .../.gitted/refs/heads/rename_conflict_theirs | 1 + .../merge-resolve/.gitted/refs/heads/renames1 | 1 + .../merge-resolve/.gitted/refs/heads/renames2 | 1 + .../.gitted/refs/heads/trivial-10 | 1 + .../.gitted/refs/heads/trivial-10-branch | 1 + .../.gitted/refs/heads/trivial-11 | 1 + .../.gitted/refs/heads/trivial-11-branch | 1 + .../.gitted/refs/heads/trivial-13 | 1 + .../.gitted/refs/heads/trivial-13-branch | 1 + .../.gitted/refs/heads/trivial-14 | 1 + .../.gitted/refs/heads/trivial-14-branch | 1 + .../.gitted/refs/heads/trivial-2alt | 1 + .../.gitted/refs/heads/trivial-2alt-branch | 1 + .../.gitted/refs/heads/trivial-3alt | 1 + .../.gitted/refs/heads/trivial-3alt-branch | 1 + .../.gitted/refs/heads/trivial-4 | 1 + .../.gitted/refs/heads/trivial-4-branch | 1 + .../.gitted/refs/heads/trivial-5alt-1 | 1 + .../.gitted/refs/heads/trivial-5alt-1-branch | 1 + .../.gitted/refs/heads/trivial-5alt-2 | 1 + .../.gitted/refs/heads/trivial-5alt-2-branch | 1 + .../.gitted/refs/heads/trivial-6 | 1 + .../.gitted/refs/heads/trivial-6-branch | 1 + .../.gitted/refs/heads/trivial-7 | 1 + .../.gitted/refs/heads/trivial-7-branch | 1 + .../.gitted/refs/heads/trivial-8 | 1 + .../.gitted/refs/heads/trivial-8-branch | 1 + .../.gitted/refs/heads/trivial-9 | 1 + .../.gitted/refs/heads/trivial-9-branch | 1 + .../.gitted/refs/heads/unrelated | 1 + .../merge-resolve/added-in-master.txt | 1 + .../resources/merge-resolve/automergeable.txt | 9 + .../merge-resolve/changed-in-branch.txt | 1 + .../merge-resolve/changed-in-master.txt | 1 + .../resources/merge-resolve/conflicting.txt | 1 + .../merge-resolve/removed-in-branch.txt | 1 + .../resources/merge-resolve/unchanged.txt | 1 + 420 files changed, 3103 insertions(+), 38 deletions(-) create mode 100644 docs/merge-df_conflicts.txt create mode 100644 src/merge_file.c create mode 100644 src/merge_file.h create mode 100644 tests-clar/merge/merge_helpers.c create mode 100644 tests-clar/merge/merge_helpers.h create mode 100644 tests-clar/merge/trees/automerge.c create mode 100644 tests-clar/merge/trees/modeconflict.c create mode 100644 tests-clar/merge/trees/treediff.c create mode 100644 tests-clar/merge/trees/trivial.c rename tests-clar/merge/{ => workdir}/setup.c (85%) create mode 100644 tests-clar/resources/merge-resolve/.gitted/COMMIT_EDITMSG create mode 100644 tests-clar/resources/merge-resolve/.gitted/HEAD create mode 100644 tests-clar/resources/merge-resolve/.gitted/ORIG_HEAD create mode 100644 tests-clar/resources/merge-resolve/.gitted/config create mode 100644 tests-clar/resources/merge-resolve/.gitted/description create mode 100644 tests-clar/resources/merge-resolve/.gitted/index create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/HEAD create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/master create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo3 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo5 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo6 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/unrelated create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00 create mode 100644 tests-clar/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/df_ancestor create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/ff_branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/master create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/octo1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/octo2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/octo3 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/octo4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/octo5 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/octo6 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/renames1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/renames2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9 create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch create mode 100644 tests-clar/resources/merge-resolve/.gitted/refs/heads/unrelated create mode 100644 tests-clar/resources/merge-resolve/added-in-master.txt create mode 100644 tests-clar/resources/merge-resolve/automergeable.txt create mode 100644 tests-clar/resources/merge-resolve/changed-in-branch.txt create mode 100644 tests-clar/resources/merge-resolve/changed-in-master.txt create mode 100644 tests-clar/resources/merge-resolve/conflicting.txt create mode 100644 tests-clar/resources/merge-resolve/removed-in-branch.txt create mode 100644 tests-clar/resources/merge-resolve/unchanged.txt diff --git a/docs/merge-df_conflicts.txt b/docs/merge-df_conflicts.txt new file mode 100644 index 000000000..09780ee2d --- /dev/null +++ b/docs/merge-df_conflicts.txt @@ -0,0 +1,41 @@ +Anc / Our / Thr represent the ancestor / ours / theirs side of a merge +from branch "branch" into HEAD. Workdir represents the expected files in +the working directory. Index represents the expected files in the index, +with stage markers. + + Anc Our Thr Workdir Index +1 D D + D/F D/F D/F [0] + +2 D D+ D~HEAD (mod/del) D/F [0] + D/F D/F D [1] + D [2] + +3 D D D/F D/F [0] + D/F + +4 D D+ D~branch (mod/del) D/F [0] + D/F D/F D [1] + D [3] + +5 D D/F (add/add) D/F [2] + D/F D/F [3] + D/F + +6 D/F D/F D D [0] + D + +7 D/F D/F+ D/F (mod/del) D/F [1] + D D~branch (fil/dir) D/F [2] + D [3] + +8 D/F D/F D D [0] + D + +9 D/F D/F+ D/F (mod/del) D/F [1] + D D~HEAD (fil/dir) D [2] + D/F [3] + +10 D/F D/F (fil/dir) D/F [0] + D D~HEAD D [2] + D diff --git a/include/git2/index.h b/include/git2/index.h index 3d4bd15a8..fcfe4be3d 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -551,9 +551,9 @@ GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *i * @return 0 or an error code */ GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path, - int ancestor_mode, git_oid *ancestor_id, - int our_mode, git_oid *our_id, - int their_mode, git_oid *their_id); + int ancestor_mode, const git_oid *ancestor_id, + int our_mode, const git_oid *our_id, + int their_mode, const git_oid *their_id); /** * Remove an resolve undo entry from the index diff --git a/include/git2/merge.h b/include/git2/merge.h index f4c5d9881..43a61f0e4 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -7,19 +7,51 @@ #ifndef INCLUDE_git_merge_h__ #define INCLUDE_git_merge_h__ -#include "common.h" -#include "types.h" -#include "oid.h" +#include "git2/common.h" +#include "git2/types.h" +#include "git2/oid.h" +#include "git2/checkout.h" +#include "git2/index.h" /** * @file git2/merge.h - * @brief Git merge-base routines - * @defgroup git_revwalk Git merge-base routines + * @brief Git merge routines + * @defgroup git_merge Git merge routines * @ingroup Git * @{ */ GIT_BEGIN_DECL +/** + * Flags for tree_many diff options. A combination of these flags can be + * passed in via the `flags` value in the `git_diff_tree_many_options`. + */ +typedef enum { +} git_merge_tree_flags; + +/** + * Automerge options for `git_merge_trees_opts`. + */ +typedef enum { + GIT_MERGE_AUTOMERGE_NORMAL = 0, + GIT_MERGE_AUTOMERGE_NONE = 1, + GIT_MERGE_AUTOMERGE_FAVOR_OURS = 2, + GIT_MERGE_AUTOMERGE_FAVOR_THEIRS = 3, +} git_merge_automerge_flags; + + +typedef struct { + unsigned int version; + git_merge_tree_flags flags; + + /** Flags for automerging content. */ + git_merge_automerge_flags automerge_flags; +} git_merge_tree_opts; + +#define GIT_MERGE_TREE_OPTS_VERSION 1 +#define GIT_MERGE_TREE_OPTS_INIT {GIT_MERGE_TREE_OPTS_VERSION} + + /** * Find a merge base between two commits * @@ -50,6 +82,28 @@ GIT_EXTERN(int) git_merge_base_many( const git_oid input_array[], size_t length); +/** + * Merge two trees, producing a `git_index` that reflects the result of + * the merge. + * + * The returned index must be freed explicitly with `git_index_free`. + * + * @param out pointer to store the index result in + * @param repo repository that contains the given trees + * @param ancestor_tree the common ancestor between the trees (or null if none) + * @param our_tree the tree that reflects the destination tree + * @param their_tree the tree to merge in to `our_tree` + * @param opts the merge tree options (or null for defaults) + * @return zero on success, -1 on failure. + */ +GIT_EXTERN(int) git_merge_trees( + git_index **out, + git_repository *repo, + const git_tree *ancestor_tree, + const git_tree *our_tree, + const git_tree *their_tree, + const git_merge_tree_opts *opts); + /** @} */ GIT_END_DECL #endif diff --git a/src/index.c b/src/index.c index 2e2d373b5..fe3a2104b 100644 --- a/src/index.c +++ b/src/index.c @@ -585,8 +585,9 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, const char *path, - int ancestor_mode, git_oid *ancestor_oid, - int our_mode, git_oid *our_oid, int their_mode, git_oid *their_oid) + int ancestor_mode, const git_oid *ancestor_oid, + int our_mode, const git_oid *our_oid, + int their_mode, const git_oid *their_oid) { git_index_reuc_entry *reuc = NULL; @@ -692,7 +693,7 @@ static int index_conflict_to_reuc(git_index *index, const char *path) { git_index_entry *conflict_entries[3]; int ancestor_mode, our_mode, their_mode; - git_oid *ancestor_oid, *our_oid, *their_oid; + git_oid const *ancestor_oid, *our_oid, *their_oid; int ret; if ((ret = git_index_conflict_get(&conflict_entries[0], @@ -946,7 +947,6 @@ int git_index_conflict_get(git_index_entry **ancestor_out, return GIT_ENOTFOUND; for (posmax = git_index_entrycount(index); pos < posmax; ++pos) { - conflict_entry = git_vector_get(&index->entries, pos); if (index->entries_cmp_path(conflict_entry->path, path) != 0) @@ -1048,7 +1048,10 @@ unsigned int git_index_reuc_entrycount(git_index *index) return (unsigned int)index->reuc.length; } -static int index_reuc_insert(git_index *index, git_index_reuc_entry *reuc, int replace) +static int index_reuc_insert( + git_index *index, + git_index_reuc_entry *reuc, + int replace) { git_index_reuc_entry **existing = NULL; size_t position; @@ -1070,9 +1073,9 @@ static int index_reuc_insert(git_index *index, git_index_reuc_entry *reuc, int r } int git_index_reuc_add(git_index *index, const char *path, - int ancestor_mode, git_oid *ancestor_oid, - int our_mode, git_oid *our_oid, - int their_mode, git_oid *their_oid) + int ancestor_mode, const git_oid *ancestor_oid, + int our_mode, const git_oid *our_oid, + int their_mode, const git_oid *their_oid) { git_index_reuc_entry *reuc = NULL; int error = 0; diff --git a/src/merge.c b/src/merge.c index e0010d6a4..8df156abe 100644 --- a/src/merge.c +++ b/src/merge.c @@ -5,15 +5,52 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "common.h" +#include "posix.h" +#include "buffer.h" #include "repository.h" #include "revwalk.h" -#include "buffer.h" -#include "merge.h" -#include "refs.h" -#include "git2/repository.h" -#include "git2/merge.h" -#include "git2/reset.h" #include "commit_list.h" +#include "merge.h" +#include "path.h" +#include "refs.h" +#include "object.h" +#include "iterator.h" +#include "refs.h" +#include "diff.h" +#include "checkout.h" +#include "tree.h" +#include "merge_file.h" +#include "blob.h" +#include "hashsig.h" + +#include "git2/types.h" +#include "git2/repository.h" +#include "git2/object.h" +#include "git2/commit.h" +#include "git2/merge.h" +#include "git2/refs.h" +#include "git2/reset.h" +#include "git2/checkout.h" +#include "git2/signature.h" +#include "git2/config.h" +#include "git2/tree.h" + +#define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0) + +typedef enum { + TREE_IDX_ANCESTOR = 0, + TREE_IDX_OURS = 1, + TREE_IDX_THEIRS = 2 +} merge_tree_index_t; + +/* Tracks D/F conflicts */ +struct merge_diff_df_data { + const char *df_path; + const char *prev_path; + git_merge_diff *prev_conflict; +}; + int git_repository_merge_cleanup(git_repository *repo) { @@ -48,6 +85,8 @@ cleanup: return error; } +/* Merge base computation */ + int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_array[], size_t length) { git_revwalk *walk; @@ -177,7 +216,7 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l return -1; if (git_commit_list_parse(walk, one) < 0) - return -1; + return -1; one->flags |= PARENT1; if (git_pqueue_insert(&list, one) < 0) @@ -294,3 +333,804 @@ cleanup: return error; } + +GIT_INLINE(int) index_entry_cmp(const git_index_entry *a, const git_index_entry *b) +{ + int value = 0; + + if (a->path == NULL) + return (b->path == NULL) ? 0 : 1; + + if ((value = a->mode - b->mode) == 0 && + (value = git_oid__cmp(&a->oid, &b->oid)) == 0) + value = strcmp(a->path, b->path); + + return value; +} + +/* Conflict resolution */ + +static int merge_conflict_resolve_trivial( + int *resolved, + git_merge_diff_list *diff_list, + const git_merge_diff *conflict) +{ + int ancestor_empty, ours_empty, theirs_empty; + int ours_changed, theirs_changed, ours_theirs_differ; + git_index_entry const *result = NULL; + int error = 0; + + assert(resolved && diff_list && conflict); + + *resolved = 0; + + if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE || + conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED) + return 0; + + if (conflict->our_status == GIT_DELTA_RENAMED || + conflict->their_status == GIT_DELTA_RENAMED) + return 0; + + ancestor_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry); + ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry); + theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry); + + ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED); + theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED); + ours_theirs_differ = ours_changed && theirs_changed && + index_entry_cmp(&conflict->our_entry, &conflict->their_entry); + + /* + * Note: with only one ancestor, some cases are not distinct: + * + * 16: ancest:anc1/anc2, head:anc1, remote:anc2 = result:no merge + * 3: ancest:(empty)^, head:head, remote:(empty) = result:no merge + * 2: ancest:(empty)^, head:(empty), remote:remote = result:no merge + * + * Note that the two cases that take D/F conflicts into account + * specifically do not need to be explicitly tested, as D/F conflicts + * would fail the *empty* test: + * + * 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head + * 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote + * + * Note that many of these cases need not be explicitly tested, as + * they simply degrade to "all different" cases (eg, 11): + * + * 4: ancest:(empty)^, head:head, remote:remote = result:no merge + * 7: ancest:ancest+, head:(empty), remote:remote = result:no merge + * 9: ancest:ancest+, head:head, remote:(empty) = result:no merge + * 11: ancest:ancest+, head:head, remote:remote = result:no merge + */ + + /* 5ALT: ancest:*, head:head, remote:head = result:head */ + if (ours_changed && !ours_empty && !ours_theirs_differ) + result = &conflict->our_entry; + /* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */ + else if (ours_changed && ours_empty && theirs_empty) + *resolved = 0; + /* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */ + else if (ours_empty && !theirs_changed) + *resolved = 0; + /* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */ + else if (!ours_changed && theirs_empty) + *resolved = 0; + /* 13: ancest:ancest+, head:head, remote:ancest = result:head */ + else if (ours_changed && !theirs_changed) + result = &conflict->our_entry; + /* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */ + else if (!ours_changed && theirs_changed) + result = &conflict->their_entry; + else + *resolved = 0; + + if (result != NULL && + GIT_MERGE_INDEX_ENTRY_EXISTS(*result) && + (error = git_vector_insert(&diff_list->staged, (void *)result)) >= 0) + *resolved = 1; + + /* Note: trivial resolution does not update the REUC. */ + + return error; +} + +static int merge_conflict_resolve_one_removed( + int *resolved, + git_merge_diff_list *diff_list, + const git_merge_diff *conflict) +{ + int ours_empty, theirs_empty; + int ours_changed, theirs_changed; + int error = 0; + + assert(resolved && diff_list && conflict); + + *resolved = 0; + + if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE || + conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED) + return 0; + + ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry); + theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry); + + ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED); + theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED); + + /* Removed in both */ + if (ours_changed && ours_empty && theirs_empty) + *resolved = 1; + /* Removed in ours */ + else if (ours_empty && !theirs_changed) + *resolved = 1; + /* Removed in theirs */ + else if (!ours_changed && theirs_empty) + *resolved = 1; + + if (*resolved) + git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict); + + return error; +} + + +static int merge_conflict_resolve_one_renamed( + int *resolved, + git_merge_diff_list *diff_list, + const git_merge_diff *conflict) +{ + int ours_renamed, theirs_renamed; + int ours_changed, theirs_changed; + git_index_entry *merged; + int error = 0; + + assert(resolved && diff_list && conflict); + + *resolved = 0; + + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) || + !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)) + return 0; + + ours_renamed = (conflict->our_status == GIT_DELTA_RENAMED); + theirs_renamed = (conflict->their_status == GIT_DELTA_RENAMED); + + if (!ours_renamed && !theirs_renamed) + return 0; + + /* Reject one file in a 2->1 conflict */ + if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 || + conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 || + conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED) + return 0; + + ours_changed = (git_oid__cmp(&conflict->ancestor_entry.oid, &conflict->our_entry.oid) != 0); + theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.oid, &conflict->their_entry.oid) != 0); + + /* if both are modified (and not to a common target) require a merge */ + if (ours_changed && theirs_changed && + git_oid__cmp(&conflict->our_entry.oid, &conflict->their_entry.oid) != 0) + return 0; + + if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) + return -1; + + if (ours_changed) + memcpy(merged, &conflict->our_entry, sizeof(git_index_entry)); + else + memcpy(merged, &conflict->their_entry, sizeof(git_index_entry)); + + if (ours_renamed) + merged->path = conflict->our_entry.path; + else + merged->path = conflict->their_entry.path; + + *resolved = 1; + + git_vector_insert(&diff_list->staged, merged); + git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict); + + return error; +} + +static int merge_conflict_resolve_automerge( + int *resolved, + git_merge_diff_list *diff_list, + const git_merge_diff *conflict, + unsigned int automerge_flags) +{ + git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT, + ours = GIT_MERGE_FILE_INPUT_INIT, + theirs = GIT_MERGE_FILE_INPUT_INIT; + git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT; + git_index_entry *index_entry; + git_odb *odb = NULL; + git_oid automerge_oid; + int error = 0; + + assert(resolved && diff_list && conflict); + + *resolved = 0; + + if (automerge_flags == GIT_MERGE_AUTOMERGE_NONE) + return 0; + + /* Reject D/F conflicts */ + if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE) + return 0; + + /* Reject link/file conflicts. */ + if ((S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->our_entry.mode)) || + (S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->their_entry.mode))) + return 0; + + /* Reject name conflicts */ + if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 || + conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED) + return 0; + + if ((conflict->our_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED && + (conflict->their_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED && + strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0) + return 0; + + if ((error = git_repository_odb(&odb, diff_list->repo)) < 0 || + (error = git_merge_file_input_from_index_entry(&ancestor, diff_list->repo, &conflict->ancestor_entry)) < 0 || + (error = git_merge_file_input_from_index_entry(&ours, diff_list->repo, &conflict->our_entry)) < 0 || + (error = git_merge_file_input_from_index_entry(&theirs, diff_list->repo, &conflict->their_entry)) < 0 || + (error = git_merge_files(&result, &ancestor, &ours, &theirs, automerge_flags)) < 0 || + !result.automergeable || + (error = git_odb_write(&automerge_oid, odb, result.data, result.len, GIT_OBJ_BLOB)) < 0) + goto done; + + if ((index_entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) + GITERR_CHECK_ALLOC(index_entry); + + index_entry->path = git_pool_strdup(&diff_list->pool, result.path); + GITERR_CHECK_ALLOC(index_entry->path); + + index_entry->file_size = result.len; + index_entry->mode = result.mode; + git_oid_cpy(&index_entry->oid, &automerge_oid); + + git_vector_insert(&diff_list->staged, index_entry); + git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict); + + *resolved = 1; + +done: + git_merge_file_input_free(&ancestor); + git_merge_file_input_free(&ours); + git_merge_file_input_free(&theirs); + git_merge_file_result_free(&result); + git_odb_free(odb); + + return error; +} + +static int merge_conflict_resolve( + int *out, + git_merge_diff_list *diff_list, + const git_merge_diff *conflict, + unsigned int automerge_flags) +{ + int resolved = 0; + int error = 0; + + *out = 0; + + if ((error = merge_conflict_resolve_trivial(&resolved, diff_list, conflict)) < 0) + goto done; + + if (automerge_flags != GIT_MERGE_AUTOMERGE_NONE) { + if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0) + goto done; + + if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0) + goto done; + + if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, automerge_flags)) < 0) + goto done; + } + + *out = resolved; + +done: + return error; +} + +/* Directory/file conflict handling */ + +GIT_INLINE(const char *) merge_diff_path( + const git_merge_diff *conflict) +{ + if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry)) + return conflict->ancestor_entry.path; + else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry)) + return conflict->our_entry.path; + else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)) + return conflict->their_entry.path; + + return NULL; +} + +GIT_INLINE(bool) merge_diff_any_side_added_or_modified( + const git_merge_diff *conflict) +{ + if (conflict->our_status == GIT_DELTA_ADDED || + conflict->our_status == GIT_DELTA_MODIFIED || + conflict->their_status == GIT_DELTA_ADDED || + conflict->their_status == GIT_DELTA_MODIFIED) + return true; + + return false; +} + +GIT_INLINE(bool) path_is_prefixed(const char *parent, const char *child) +{ + size_t child_len = strlen(child); + size_t parent_len = strlen(parent); + + if (child_len < parent_len || + strncmp(parent, child, parent_len) != 0) + return 0; + + return (child[parent_len] == '/'); +} + +GIT_INLINE(int) merge_diff_detect_df_conflict( + struct merge_diff_df_data *df_data, + git_merge_diff *conflict) +{ + const char *cur_path = merge_diff_path(conflict); + + /* Determine if this is a D/F conflict or the child of one */ + if (df_data->df_path && + path_is_prefixed(df_data->df_path, cur_path)) + conflict->type = GIT_MERGE_DIFF_DF_CHILD; + else if(df_data->df_path) + df_data->df_path = NULL; + else if (df_data->prev_path && + merge_diff_any_side_added_or_modified(df_data->prev_conflict) && + merge_diff_any_side_added_or_modified(conflict) && + path_is_prefixed(df_data->prev_path, cur_path)) { + conflict->type = GIT_MERGE_DIFF_DF_CHILD; + + df_data->prev_conflict->type = GIT_MERGE_DIFF_DIRECTORY_FILE; + df_data->df_path = df_data->prev_path; + } + + df_data->prev_path = cur_path; + df_data->prev_conflict = conflict; + + return 0; +} + +/* Conflict handling */ + +GIT_INLINE(int) merge_diff_detect_type( + git_merge_diff *conflict) +{ + if (conflict->our_status == GIT_DELTA_ADDED && + conflict->their_status == GIT_DELTA_ADDED) + conflict->type = GIT_MERGE_DIFF_BOTH_ADDED; + else if (conflict->our_status == GIT_DELTA_MODIFIED && + conflict->their_status == GIT_DELTA_MODIFIED) + conflict->type = GIT_MERGE_DIFF_BOTH_MODIFIED; + else if (conflict->our_status == GIT_DELTA_DELETED && + conflict->their_status == GIT_DELTA_DELETED) + conflict->type = GIT_MERGE_DIFF_BOTH_DELETED; + else if (conflict->our_status == GIT_DELTA_MODIFIED && + conflict->their_status == GIT_DELTA_DELETED) + conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED; + else if (conflict->our_status == GIT_DELTA_DELETED && + conflict->their_status == GIT_DELTA_MODIFIED) + conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED; + else + conflict->type = GIT_MERGE_DIFF_NONE; + + return 0; +} + +GIT_INLINE(int) index_entry_dup( + git_index_entry *out, + git_pool *pool, + const git_index_entry *src) +{ + if (src != NULL) { + memcpy(out, src, sizeof(git_index_entry)); + + if ((out->path = git_pool_strdup(pool, src->path)) == NULL) + return -1; + } + + return 0; +} + +GIT_INLINE(int) merge_delta_type_from_index_entries( + const git_index_entry *ancestor, + const git_index_entry *other) +{ + if (ancestor == NULL && other == NULL) + return GIT_DELTA_UNMODIFIED; + else if (ancestor == NULL && other != NULL) + return GIT_DELTA_ADDED; + else if (ancestor != NULL && other == NULL) + return GIT_DELTA_DELETED; + else if (S_ISDIR(ancestor->mode) ^ S_ISDIR(other->mode)) + return GIT_DELTA_TYPECHANGE; + else if(S_ISLNK(ancestor->mode) ^ S_ISLNK(other->mode)) + return GIT_DELTA_TYPECHANGE; + else if (git_oid__cmp(&ancestor->oid, &other->oid) || + ancestor->mode != other->mode) + return GIT_DELTA_MODIFIED; + + return GIT_DELTA_UNMODIFIED; +} + +static git_merge_diff *merge_diff_from_index_entries( + git_merge_diff_list *diff_list, + const git_index_entry **entries) +{ + git_merge_diff *conflict; + git_pool *pool = &diff_list->pool; + + if ((conflict = git_pool_malloc(pool, sizeof(git_merge_diff))) == NULL) + return NULL; + + if (index_entry_dup(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 || + index_entry_dup(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 || + index_entry_dup(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0) + return NULL; + + conflict->our_status = merge_delta_type_from_index_entries( + entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_OURS]); + conflict->their_status = merge_delta_type_from_index_entries( + entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_THEIRS]); + + return conflict; +} + +/* Merge trees */ + +static int merge_index_insert_conflict( + git_merge_diff_list *diff_list, + struct merge_diff_df_data *merge_df_data, + const git_index_entry *tree_items[3]) +{ + git_merge_diff *conflict; + + if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL || + merge_diff_detect_type(conflict) < 0 || + merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 || + git_vector_insert(&diff_list->conflicts, conflict) < 0) + return -1; + + return 0; +} + +static int merge_index_insert_unmodified( + git_merge_diff_list *diff_list, + const git_index_entry *tree_items[3]) +{ + int error = 0; + git_index_entry *entry; + + entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry)); + GITERR_CHECK_ALLOC(entry); + + if ((error = index_entry_dup(entry, &diff_list->pool, tree_items[0])) >= 0) + error = git_vector_insert(&diff_list->staged, entry); + + return error; +} + +int git_merge_diff_list__find_differences( + git_merge_diff_list *diff_list, + const git_tree *ancestor_tree, + const git_tree *our_tree, + const git_tree *their_tree) +{ + git_iterator *iterators[3] = {0}; + git_index_entry *items[3] = {0}, *best_cur_item, *cur_items[3]; + git_vector_cmp entry_compare = git_index_entry__cmp; + struct merge_diff_df_data df_data = {0}; + int cur_item_modified; + size_t i; + int error = 0; + + assert(diff_list && our_tree && their_tree); + + if ((error = git_iterator_for_tree(&iterators[TREE_IDX_ANCESTOR], (git_tree *)ancestor_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || + (error = git_iterator_for_tree(&iterators[TREE_IDX_OURS], (git_tree *)our_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || + (error = git_iterator_for_tree(&iterators[TREE_IDX_THEIRS], (git_tree *)their_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0) + goto done; + + /* Set up the iterators */ + for (i = 0; i < 3; i++) { + if ((error = git_iterator_current(&items[i], iterators[i])) < 0) + goto done; + } + + while (true) { + memset(cur_items, 0x0, sizeof(git_index_entry *) * 3); + best_cur_item = NULL; + cur_item_modified = 0; + + /* Find the next path(s) to consume from each iterator */ + for (i = 0; i < 3; i++) { + if (items[i] == NULL) { + cur_item_modified = 1; + continue; + } + + if (best_cur_item == NULL) { + best_cur_item = items[i]; + cur_items[i] = items[i]; + } else { + int path_diff = entry_compare(items[i], best_cur_item); + + if (path_diff < 0) { + /* + * Found an item that sorts before our current item, make + * our current item this one. + */ + memset(cur_items, 0x0, sizeof(git_index_entry *) * 3); + cur_item_modified = 1; + best_cur_item = items[i]; + cur_items[i] = items[i]; + } else if (path_diff > 0) { + /* No entry for the current item, this is modified */ + cur_item_modified = 1; + } else if (path_diff == 0) { + cur_items[i] = items[i]; + + if (!cur_item_modified) + cur_item_modified = index_entry_cmp(best_cur_item, items[i]); + } + } + } + + if (best_cur_item == NULL) + break; + + if (cur_item_modified) + error = merge_index_insert_conflict(diff_list, &df_data, cur_items); + else + error = merge_index_insert_unmodified(diff_list, cur_items); + + /* Advance each iterator that participated */ + for (i = 0; i < 3; i++) { + if (cur_items[i] != NULL && + (error = git_iterator_advance(&items[i], iterators[i])) < 0) + goto done; + } + } + +done: + for (i = 0; i < 3; i++) + git_iterator_free(iterators[i]); + + return error; +} + +git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo) +{ + git_merge_diff_list *diff_list = git__calloc(1, sizeof(git_merge_diff_list)); + + if (diff_list == NULL) + return NULL; + + diff_list->repo = repo; + + if (git_vector_init(&diff_list->staged, 0, NULL) < 0 || + git_vector_init(&diff_list->conflicts, 0, NULL) < 0 || + git_vector_init(&diff_list->resolved, 0, NULL) < 0 || + git_pool_init(&diff_list->pool, 1, 0) < 0) + return NULL; + + return diff_list; +} + +static int merge_tree_normalize_opts( + git_repository *repo, + git_merge_tree_opts *opts, + const git_merge_tree_opts *given) +{ + git_config *cfg = NULL; + int error = 0; + + assert(repo && opts); + + if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) + return error; + + if (given != NULL) + memcpy(opts, given, sizeof(git_merge_tree_opts)); + else { + git_merge_tree_opts init = GIT_MERGE_TREE_OPTS_INIT; + memcpy(opts, &init, sizeof(init)); + } + + return 0; +} + + +static int merge_index_insert_reuc( + git_index *index, + size_t idx, + const git_index_entry *entry) +{ + const git_index_reuc_entry *reuc; + int mode[3] = { 0, 0, 0 }; + git_oid const *oid[3] = { NULL, NULL, NULL }; + size_t i; + + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(*entry)) + return 0; + + if ((reuc = git_index_reuc_get_bypath(index, entry->path)) != NULL) { + for (i = 0; i < 3; i++) { + mode[i] = reuc->mode[i]; + oid[i] = &reuc->oid[i]; + } + } + + mode[idx] = entry->mode; + oid[idx] = &entry->oid; + + return git_index_reuc_add(index, entry->path, + mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]); +} + +int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) +{ + git_index *index; + size_t i; + git_index_entry *entry; + git_merge_diff *conflict; + int error = 0; + + *out = NULL; + + if ((error = git_index_new(&index)) < 0) + return error; + + git_vector_foreach(&diff_list->staged, i, entry) { + if ((error = git_index_add(index, entry)) < 0) + goto on_error; + } + + git_vector_foreach(&diff_list->conflicts, i, conflict) { + const git_index_entry *ancestor = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? + &conflict->ancestor_entry : NULL; + + const git_index_entry *ours = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? + &conflict->our_entry : NULL; + + const git_index_entry *theirs = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? + &conflict->their_entry : NULL; + + if ((error = git_index_conflict_add(index, ancestor, ours, theirs)) < 0) + goto on_error; + } + + /* Add each rename entry to the rename portion of the index. */ + git_vector_foreach(&diff_list->conflicts, i, conflict) { + const char *ancestor_path, *our_path, *their_path; + + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry)) + continue; + + ancestor_path = conflict->ancestor_entry.path; + + our_path = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? + conflict->our_entry.path : NULL; + + their_path = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? + conflict->their_entry.path : NULL; + } + + /* Add each entry in the resolved conflict to the REUC independently, since + * the paths may differ due to renames. */ + git_vector_foreach(&diff_list->resolved, i, conflict) { + const git_index_entry *ancestor = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? + &conflict->ancestor_entry : NULL; + + const git_index_entry *ours = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? + &conflict->our_entry : NULL; + + const git_index_entry *theirs = + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? + &conflict->their_entry : NULL; + + if (ancestor != NULL && + (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0) + goto on_error; + + if (ours != NULL && + (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0) + goto on_error; + + if (theirs != NULL && + (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0) + goto on_error; + } + + *out = index; + return 0; + +on_error: + git_index_free(index); + + return error; +} + +int git_merge_trees( + git_index **out, + git_repository *repo, + const git_tree *ancestor_tree, + const git_tree *our_tree, + const git_tree *their_tree, + const git_merge_tree_opts *given_opts) +{ + git_merge_diff_list *diff_list; + git_merge_tree_opts opts; + git_merge_diff *conflict; + git_vector changes; + size_t i; + int error = 0; + + assert(out && repo && our_tree && their_tree); + + *out = NULL; + + if ((error = merge_tree_normalize_opts(repo, &opts, given_opts)) < 0) + return error; + + diff_list = git_merge_diff_list__alloc(repo); + GITERR_CHECK_ALLOC(diff_list); + + if ((error = git_merge_diff_list__find_differences(diff_list, ancestor_tree, our_tree, their_tree)) < 0) + goto done; + + memcpy(&changes, &diff_list->conflicts, sizeof(git_vector)); + git_vector_clear(&diff_list->conflicts); + + git_vector_foreach(&changes, i, conflict) { + int resolved = 0; + + if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.automerge_flags)) < 0) + goto done; + + if (!resolved) + git_vector_insert(&diff_list->conflicts, conflict); + } + + error = index_from_diff_list(out, diff_list); + +done: + git_merge_diff_list__free(diff_list); + + return error; +} + +void git_merge_diff_list__free(git_merge_diff_list *diff_list) +{ + if (!diff_list) + return; + + git_vector_free(&diff_list->staged); + git_vector_free(&diff_list->conflicts); + git_vector_free(&diff_list->resolved); + git_pool_clear(&diff_list->pool); + git__free(diff_list); +} diff --git a/src/merge.h b/src/merge.h index 22c644270..6307d1569 100644 --- a/src/merge.h +++ b/src/merge.h @@ -7,16 +7,121 @@ #ifndef INCLUDE_merge_h__ #define INCLUDE_merge_h__ -#include "git2/types.h" -#include "git2/merge.h" -#include "commit_list.h" #include "vector.h" +#include "commit_list.h" +#include "pool.h" + +#include "git2/merge.h" +#include "git2/types.h" #define GIT_MERGE_MSG_FILE "MERGE_MSG" #define GIT_MERGE_MODE_FILE "MERGE_MODE" -#define MERGE_CONFIG_FILE_MODE 0666 +#define GIT_MERGE_TREE_RENAME_THRESHOLD 50 +#define GIT_MERGE_TREE_TARGET_LIMIT 1000 -int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos); +/** Types of changes when files are merged from branch to branch. */ +typedef enum { + /* No conflict - a change only occurs in one branch. */ + GIT_MERGE_DIFF_NONE = 0, + + /* Occurs when a file is modified in both branches. */ + GIT_MERGE_DIFF_BOTH_MODIFIED = (1 << 0), + + /* Occurs when a file is added in both branches. */ + GIT_MERGE_DIFF_BOTH_ADDED = (1 << 1), + + /* Occurs when a file is deleted in both branches. */ + GIT_MERGE_DIFF_BOTH_DELETED = (1 << 2), + + /* Occurs when a file is modified in one branch and deleted in the other. */ + GIT_MERGE_DIFF_MODIFIED_DELETED = (1 << 3), + + /* Occurs when a file is renamed in one branch and modified in the other. */ + GIT_MERGE_DIFF_RENAMED_MODIFIED = (1 << 4), + + /* Occurs when a file is renamed in one branch and deleted in the other. */ + GIT_MERGE_DIFF_RENAMED_DELETED = (1 << 5), + + /* Occurs when a file is renamed in one branch and a file with the same + * name is added in the other. Eg, A->B and new file B. Core git calls + * this a "rename/delete". */ + GIT_MERGE_DIFF_RENAMED_ADDED = (1 << 6), + + /* Occurs when both a file is renamed to the same name in the ours and + * theirs branches. Eg, A->B and A->B in both. Automergeable. */ + GIT_MERGE_DIFF_BOTH_RENAMED = (1 << 7), + + /* Occurs when a file is renamed to different names in the ours and theirs + * branches. Eg, A->B and A->C. */ + GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 = (1 << 8), + + /* Occurs when two files are renamed to the same name in the ours and + * theirs branches. Eg, A->C and B->C. */ + GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 = (1 << 9), + + /* Occurs when an item at a path in one branch is a directory, and an + * item at the same path in a different branch is a file. */ + GIT_MERGE_DIFF_DIRECTORY_FILE = (1 << 10), + + /* The child of a folder that is in a directory/file conflict. */ + GIT_MERGE_DIFF_DF_CHILD = (1 << 11), +} git_merge_diff_type_t; + + +typedef struct { + git_repository *repo; + git_pool pool; + + /* Vector of git_index_entry that represent the merged items that + * have been staged, either because only one side changed, or because + * the two changes were non-conflicting and mergeable. These items + * will be written as staged entries in the main index. + */ + git_vector staged; + + /* Vector of git_merge_diff entries that represent the conflicts that + * have not been automerged. These items will be written to high-stage + * entries in the main index. + */ + git_vector conflicts; + + /* Vector of git_merge_diff that have been automerged. These items + * will be written to the REUC when the index is produced. + */ + git_vector resolved; +} git_merge_diff_list; + +/** + * Description of changes to one file across three trees. + */ +typedef struct { + git_merge_diff_type_t type; + + git_index_entry ancestor_entry; + + git_index_entry our_entry; + git_delta_t our_status; + + git_index_entry their_entry; + git_delta_t their_status; +} git_merge_diff; + +int git_merge__bases_many( + git_commit_list **out, + git_revwalk *walk, + git_commit_list_node *one, + git_vector *twos); + +git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo); + +int git_merge_diff_list__find_differences(git_merge_diff_list *merge_diff_list, + const git_tree *ancestor_tree, + const git_tree *ours_tree, + const git_tree *theirs_tree); + +int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_tree_opts *opts); + +void git_merge_diff_list__free(git_merge_diff_list *diff_list); #endif diff --git a/src/merge_file.c b/src/merge_file.c new file mode 100644 index 000000000..4b3f3730b --- /dev/null +++ b/src/merge_file.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "common.h" +#include "repository.h" +#include "merge_file.h" + +#include "git2/repository.h" +#include "git2/object.h" +#include "git2/index.h" + +#include "xdiff/xdiff.h" + +#define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0) + +GIT_INLINE(const char *) merge_file_best_path( + const git_merge_file_input *ancestor, + const git_merge_file_input *ours, + const git_merge_file_input *theirs) +{ + if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) { + if (strcmp(ours->path, theirs->path) == 0) + return ours->path; + + return NULL; + } + + if (strcmp(ancestor->path, ours->path) == 0) + return theirs->path; + else if(strcmp(ancestor->path, theirs->path) == 0) + return ours->path; + + return NULL; +} + +GIT_INLINE(int) merge_file_best_mode( + const git_merge_file_input *ancestor, + const git_merge_file_input *ours, + const git_merge_file_input *theirs) +{ + /* + * If ancestor didn't exist and either ours or theirs is executable, + * assume executable. Otherwise, if any mode changed from the ancestor, + * use that one. + */ + if (GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) { + if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE || + theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE) + return GIT_FILEMODE_BLOB_EXECUTABLE; + + return GIT_FILEMODE_BLOB; + } + + if (ancestor->mode == ours->mode) + return theirs->mode; + else if(ancestor->mode == theirs->mode) + return ours->mode; + + return 0; +} + +int git_merge_file_input_from_index_entry( + git_merge_file_input *input, + git_repository *repo, + const git_index_entry *entry) +{ + git_odb *odb = NULL; + int error = 0; + + assert(input && repo && entry); + + if (entry->mode == 0) + return 0; + + if ((error = git_repository_odb(&odb, repo)) < 0 || + (error = git_odb_read(&input->odb_object, odb, &entry->oid)) < 0) + goto done; + + input->mode = entry->mode; + input->path = git__strdup(entry->path); + input->mmfile.size = git_odb_object_size(input->odb_object); + input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object); + + if (input->label == NULL) + input->label = entry->path; + +done: + git_odb_free(odb); + + return error; +} + +int git_merge_file_input_from_diff_file( + git_merge_file_input *input, + git_repository *repo, + const git_diff_file *file) +{ + git_odb *odb = NULL; + int error = 0; + + assert(input && repo && file); + + if (file->mode == 0) + return 0; + + if ((error = git_repository_odb(&odb, repo)) < 0 || + (error = git_odb_read(&input->odb_object, odb, &file->oid)) < 0) + goto done; + + input->mode = file->mode; + input->path = git__strdup(file->path); + input->mmfile.size = git_odb_object_size(input->odb_object); + input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object); + + if (input->label == NULL) + input->label = file->path; + +done: + git_odb_free(odb); + + return error; +} + +int git_merge_files( + git_merge_file_result *out, + git_merge_file_input *ancestor, + git_merge_file_input *ours, + git_merge_file_input *theirs, + git_merge_automerge_flags flags) +{ + xmparam_t xmparam; + mmbuffer_t mmbuffer; + int xdl_result; + int error = 0; + + assert(out && ancestor && ours && theirs); + + memset(out, 0x0, sizeof(git_merge_file_result)); + + if (!GIT_MERGE_FILE_SIDE_EXISTS(ours) || !GIT_MERGE_FILE_SIDE_EXISTS(theirs)) + return 0; + + memset(&xmparam, 0x0, sizeof(xmparam_t)); + xmparam.ancestor = ancestor->label; + xmparam.file1 = ours->label; + xmparam.file2 = theirs->label; + + out->path = merge_file_best_path(ancestor, ours, theirs); + out->mode = merge_file_best_mode(ancestor, ours, theirs); + + if (flags == GIT_MERGE_AUTOMERGE_FAVOR_OURS) + xmparam.favor = XDL_MERGE_FAVOR_OURS; + + if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS) + xmparam.favor = XDL_MERGE_FAVOR_THEIRS; + + if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile, + &theirs->mmfile, &xmparam, &mmbuffer)) < 0) { + giterr_set(GITERR_MERGE, "Failed to merge files."); + error = -1; + goto done; + } + + out->automergeable = (xdl_result == 0); + out->data = (unsigned char *)mmbuffer.ptr; + out->len = mmbuffer.size; + +done: + return error; +} + diff --git a/src/merge_file.h b/src/merge_file.h new file mode 100644 index 000000000..1aa34893d --- /dev/null +++ b/src/merge_file.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_filediff_h__ +#define INCLUDE_filediff_h__ + +#include "xdiff/xdiff.h" + +#include "git2/merge.h" + +typedef struct { + const char *label; + char *path; + unsigned int mode; + mmfile_t mmfile; + + git_odb_object *odb_object; +} git_merge_file_input; + +#define GIT_MERGE_FILE_INPUT_INIT {0} + +typedef struct { + bool automergeable; + + const char *path; + int mode; + + unsigned char *data; + size_t len; +} git_merge_file_result; + +#define GIT_MERGE_FILE_RESULT_INIT {0} + +int git_merge_file_input_from_index_entry( + git_merge_file_input *input, + git_repository *repo, + const git_index_entry *entry); + +int git_merge_file_input_from_diff_file( + git_merge_file_input *input, + git_repository *repo, + const git_diff_file *file); + +int git_merge_files( + git_merge_file_result *out, + git_merge_file_input *ancestor, + git_merge_file_input *ours, + git_merge_file_input *theirs, + git_merge_automerge_flags flags); + +GIT_INLINE(void) git_merge_file_input_free(git_merge_file_input *input) +{ + assert(input); + git__free(input->path); + git_odb_object_free(input->odb_object); +} + +GIT_INLINE(void) git_merge_file_result_free(git_merge_file_result *filediff) +{ + if (filediff == NULL) + return; + + /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */ + if (filediff->data != NULL) + free(filediff->data); +} + +#endif diff --git a/tests-clar/merge/merge_helpers.c b/tests-clar/merge/merge_helpers.c new file mode 100644 index 000000000..b10e9ec32 --- /dev/null +++ b/tests-clar/merge/merge_helpers.c @@ -0,0 +1,224 @@ +#include "clar_libgit2.h" +#include "buffer.h" +#include "refs.h" +#include "tree.h" +#include "merge_helpers.h" +#include "merge.h" +#include "git2/merge.h" + +int merge_trees_from_branches( + git_index **index, git_repository *repo, + const char *ours_name, const char *theirs_name, + git_merge_tree_opts *opts) +{ + git_commit *our_commit, *their_commit, *ancestor_commit = NULL; + git_tree *our_tree, *their_tree, *ancestor_tree = NULL; + git_oid our_oid, their_oid, ancestor_oid; + git_buf branch_buf = GIT_BUF_INIT; + int error; + + git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name); + cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr)); + cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid)); + + git_buf_clear(&branch_buf); + git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name); + cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr)); + cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid)); + + error = git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit)); + + if (error != GIT_ENOTFOUND) { + cl_git_pass(error); + + cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid)); + cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit)); + } + + cl_git_pass(git_commit_tree(&our_tree, our_commit)); + cl_git_pass(git_commit_tree(&their_tree, their_commit)); + + cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts)); + + git_buf_free(&branch_buf); + git_tree_free(our_tree); + git_tree_free(their_tree); + git_tree_free(ancestor_tree); + git_commit_free(our_commit); + git_commit_free(their_commit); + git_commit_free(ancestor_commit); + + return 0; +} + +static int index_entry_eq_merge_index_entry(const struct merge_index_entry *expected, const git_index_entry *actual) +{ + git_oid expected_oid; + bool test_oid; + + if (strlen(expected->oid_str) != 0) { + cl_git_pass(git_oid_fromstr(&expected_oid, expected->oid_str)); + test_oid = 1; + } else + test_oid = 0; + + if (actual->mode != expected->mode || + (test_oid && git_oid_cmp(&actual->oid, &expected_oid) != 0) || + git_index_entry_stage(actual) != expected->stage) + return 0; + + if (actual->mode == 0 && (actual->path != NULL || strlen(expected->path) > 0)) + return 0; + + if (actual->mode != 0 && (strcmp(actual->path, expected->path) != 0)) + return 0; + + return 1; +} + +static int name_entry_eq(const char *expected, const char *actual) +{ + if (strlen(expected) == 0) + return (actual == NULL) ? 1 : 0; + + return (strcmp(expected, actual) == 0) ? 1 : 0; +} + +static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual) +{ + if (!index_entry_eq_merge_index_entry((const struct merge_index_entry *)&expected->ancestor, &actual->ancestor_entry) || + !index_entry_eq_merge_index_entry((const struct merge_index_entry *)&expected->ours, &actual->our_entry) || + !index_entry_eq_merge_index_entry((const struct merge_index_entry *)&expected->theirs, &actual->their_entry)) + return 0; + + if (expected->ours.status != actual->our_status || + expected->theirs.status != actual->their_status) + return 0; + + return 1; +} + +int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len) +{ + git_merge_diff *actual; + size_t i; + + if (conflicts->length != expected_len) + return 0; + + for (i = 0; i < expected_len; i++) { + actual = conflicts->contents[i]; + + if (!index_conflict_data_eq_merge_diff(&expected[i], actual)) + return 0; + } + + return 1; +} + +int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len) +{ + size_t i; + const git_index_entry *index_entry; + + /* + dump_index_entries(&index->entries); + */ + + if (git_index_entrycount(index) != expected_len) + return 0; + + for (i = 0; i < expected_len; i++) { + if ((index_entry = git_index_get_byindex(index, i)) == NULL) + return 0; + + if (!index_entry_eq_merge_index_entry(&expected[i], index_entry)) + return 0; + } + + return 1; +} + +int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len) +{ + size_t i; + const git_index_reuc_entry *reuc_entry; + git_oid expected_oid; + + /* + dump_reuc(index); + */ + + if (git_index_reuc_entrycount(index) != expected_len) + return 0; + + for (i = 0; i < expected_len; i++) { + if ((reuc_entry = git_index_reuc_get_byindex(index, i)) == NULL) + return 0; + + if (strcmp(reuc_entry->path, expected[i].path) != 0 || + reuc_entry->mode[0] != expected[i].ancestor_mode || + reuc_entry->mode[1] != expected[i].our_mode || + reuc_entry->mode[2] != expected[i].their_mode) + return 0; + + if (expected[i].ancestor_mode > 0) { + cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].ancestor_oid_str)); + + if (git_oid_cmp(&reuc_entry->oid[0], &expected_oid) != 0) + return 0; + } + + if (expected[i].our_mode > 0) { + cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].our_oid_str)); + + if (git_oid_cmp(&reuc_entry->oid[1], &expected_oid) != 0) + return 0; + } + + if (expected[i].their_mode > 0) { + cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].their_oid_str)); + + if (git_oid_cmp(&reuc_entry->oid[2], &expected_oid) != 0) + return 0; + } + } + + return 1; +} + +int dircount(void *payload, git_buf *pathbuf) +{ + int *entries = payload; + size_t len = git_buf_len(pathbuf); + + if (len < 5 || strcmp(pathbuf->ptr + (git_buf_len(pathbuf) - 5), "/.git") != 0) + (*entries)++; + + return 0; +} + +int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len) +{ + size_t actual_len = 0, i; + git_oid actual_oid, expected_oid; + git_buf wd = GIT_BUF_INIT; + + git_buf_puts(&wd, repo->workdir); + git_path_direach(&wd, dircount, &actual_len); + + if (actual_len != expected_len) + return 0; + + for (i = 0; i < expected_len; i++) { + git_blob_create_fromworkdir(&actual_oid, repo, expected[i].path); + git_oid_fromstr(&expected_oid, expected[i].oid_str); + + if (git_oid_cmp(&actual_oid, &expected_oid) != 0) + return 0; + } + + git_buf_free(&wd); + + return 1; +} diff --git a/tests-clar/merge/merge_helpers.h b/tests-clar/merge/merge_helpers.h new file mode 100644 index 000000000..1a0b8921b --- /dev/null +++ b/tests-clar/merge/merge_helpers.h @@ -0,0 +1,62 @@ +#ifndef INCLUDE_cl_merge_helpers_h__ +#define INCLUDE_cl_merge_helpers_h__ + +#include "merge.h" +#include "git2/merge.h" + +struct merge_index_entry { + uint16_t mode; + char oid_str[41]; + int stage; + char path[128]; +}; + +struct merge_name_entry { + char ancestor_path[128]; + char our_path[128]; + char their_path[128]; +}; + +struct merge_index_with_status { + uint16_t mode; + char oid_str[41]; + int stage; + char path[128]; + unsigned int status; +}; + +struct merge_reuc_entry { + char path[128]; + unsigned int ancestor_mode; + unsigned int our_mode; + unsigned int their_mode; + char ancestor_oid_str[41]; + char our_oid_str[41]; + char their_oid_str[41]; +}; + +struct merge_index_conflict_data { + struct merge_index_with_status ancestor; + struct merge_index_with_status ours; + struct merge_index_with_status theirs; + git_merge_diff_type_t change_type; +}; + +int merge_trees_from_branches( + git_index **index, git_repository *repo, + const char *ours_name, const char *theirs_name, + git_merge_tree_opts *opts); + +int merge_test_diff_list(git_merge_diff_list *diff_list, const struct merge_index_entry expected[], size_t expected_len); + +int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len); + +int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len); + +int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len); + +int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len); + +int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len); + +#endif diff --git a/tests-clar/merge/trees/automerge.c b/tests-clar/merge/trees/automerge.c new file mode 100644 index 000000000..7592a926e --- /dev/null +++ b/tests-clar/merge/trees/automerge.c @@ -0,0 +1,217 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "buffer.h" +#include "merge.h" +#include "../merge_helpers.h" +#include "fileops.h" + +static git_repository *repo; + +#define TEST_REPO_PATH "merge-resolve" +#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" + +#define THEIRS_AUTOMERGE_BRANCH "branch" + +#define THEIRS_UNRELATED_BRANCH "unrelated" +#define THEIRS_UNRELATED_OID "55b4e4687e7a0d9ca367016ed930f385d4022e6f" +#define THEIRS_UNRELATED_PARENT "d6cf6c7741b3316826af1314042550c97ded1d50" + +#define OURS_DIRECTORY_FILE "df_side1" +#define THEIRS_DIRECTORY_FILE "df_side2" + +/* Non-conflicting files, index entries are common to every merge operation */ +#define ADDED_IN_MASTER_INDEX_ENTRY \ + { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" } +#define AUTOMERGEABLE_INDEX_ENTRY \ + { 0100644, "f2e1550a0c9e53d5811175864a29536642ae3821", 0, "automergeable.txt" } +#define CHANGED_IN_BRANCH_INDEX_ENTRY \ + { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt" } +#define CHANGED_IN_MASTER_INDEX_ENTRY \ + { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" } +#define UNCHANGED_INDEX_ENTRY \ + { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" } + +/* Expected REUC entries */ +#define AUTOMERGEABLE_REUC_ENTRY \ + { "automergeable.txt", 0100644, 0100644, 0100644, \ + "6212c31dab5e482247d7977e4f0dd3601decf13b", \ + "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", \ + "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe" } +#define CONFLICTING_REUC_ENTRY \ + { "conflicting.txt", 0100644, 0100644, 0100644, \ + "d427e0b2e138501a3d15cc376077a3631e15bd46", \ + "4e886e602529caa9ab11d71f86634bd1b6e0de10", \ + "2bd0a343aeef7a2cf0d158478966a6e587ff3863" } +#define REMOVED_IN_BRANCH_REUC_ENTRY \ + { "removed-in-branch.txt", 0100644, 0100644, 0, \ + "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \ + "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \ + "" } +#define REMOVED_IN_MASTER_REUC_ENTRY \ + { "removed-in-master.txt", 0100644, 0, 0100644, \ + "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", \ + "", \ + "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" } + +#define AUTOMERGEABLE_MERGED_FILE \ + "this file is changed in master\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is changed in branch\n" + +#define AUTOMERGEABLE_MERGED_FILE_CRLF \ + "this file is changed in master\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is changed in branch\r\n" + +// Fixture setup and teardown +void test_merge_trees_automerge__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_merge_trees_automerge__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_merge_trees_automerge__automerge(void) +{ + git_index *index; + const git_index_entry *entry; + git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + git_blob *blob; + + struct merge_index_entry merge_index_entries[] = { + ADDED_IN_MASTER_INDEX_ENTRY, + AUTOMERGEABLE_INDEX_ENTRY, + CHANGED_IN_BRANCH_INDEX_ENTRY, + CHANGED_IN_MASTER_INDEX_ENTRY, + + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" }, + { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" }, + + UNCHANGED_INDEX_ENTRY, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + AUTOMERGEABLE_REUC_ENTRY, + REMOVED_IN_BRANCH_REUC_ENTRY, + REMOVED_IN_MASTER_REUC_ENTRY + }; + + cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 8)); + cl_assert(merge_test_reuc(index, merge_reuc_entries, 3)); + + cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL); + cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE)); + + cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->oid, GIT_OBJ_BLOB)); + cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, entry->file_size) == 0); + + git_index_free(index); + git_blob_free(blob); +} + +void test_merge_trees_automerge__favor_ours(void) +{ + git_index *index; + git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + + struct merge_index_entry merge_index_entries[] = { + ADDED_IN_MASTER_INDEX_ENTRY, + AUTOMERGEABLE_INDEX_ENTRY, + CHANGED_IN_BRANCH_INDEX_ENTRY, + CHANGED_IN_MASTER_INDEX_ENTRY, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, + UNCHANGED_INDEX_ENTRY, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + AUTOMERGEABLE_REUC_ENTRY, + CONFLICTING_REUC_ENTRY, + REMOVED_IN_BRANCH_REUC_ENTRY, + REMOVED_IN_MASTER_REUC_ENTRY, + }; + + opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_OURS; + + cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 6)); + cl_assert(merge_test_reuc(index, merge_reuc_entries, 4)); + + git_index_free(index); +} + +void test_merge_trees_automerge__favor_theirs(void) +{ + git_index *index; + git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + + struct merge_index_entry merge_index_entries[] = { + ADDED_IN_MASTER_INDEX_ENTRY, + AUTOMERGEABLE_INDEX_ENTRY, + CHANGED_IN_BRANCH_INDEX_ENTRY, + CHANGED_IN_MASTER_INDEX_ENTRY, + { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" }, + UNCHANGED_INDEX_ENTRY, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + AUTOMERGEABLE_REUC_ENTRY, + CONFLICTING_REUC_ENTRY, + REMOVED_IN_BRANCH_REUC_ENTRY, + REMOVED_IN_MASTER_REUC_ENTRY, + }; + + opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_THEIRS; + + cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 6)); + cl_assert(merge_test_reuc(index, merge_reuc_entries, 4)); + + git_index_free(index); +} + +void test_merge_trees_automerge__unrelated(void) +{ + git_index *index; + git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, + { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" }, + { 0100644, "d07ec190c306ec690bac349e87d01c4358e49bb2", 3, "automergeable.txt" }, + { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, + { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" }, + { 0100644, "4b253da36a0ae8bfce63aeabd8c5b58429925594", 3, "conflicting.txt" }, + { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" }, + { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" }, + { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, + { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, + }; + + cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_UNRELATED_BRANCH, &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 11)); + + git_index_free(index); +} diff --git a/tests-clar/merge/trees/modeconflict.c b/tests-clar/merge/trees/modeconflict.c new file mode 100644 index 000000000..0661ce5b3 --- /dev/null +++ b/tests-clar/merge/trees/modeconflict.c @@ -0,0 +1,59 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "buffer.h" +#include "merge.h" +#include "../merge_helpers.h" +#include "fileops.h" + +static git_repository *repo; + +#define TEST_REPO_PATH "merge-resolve" + +#define DF_SIDE1_BRANCH "df_side1" +#define DF_SIDE2_BRANCH "df_side2" + +// Fixture setup and teardown +void test_merge_trees_modeconflict__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_merge_trees_modeconflict__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_merge_trees_modeconflict__df_conflict(void) +{ + git_index *index; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 2, "dir-10" }, + { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 3, "dir-10" }, + { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" }, + { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 3, "dir-7" }, + { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 1, "dir-7/file.txt" }, + { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 2, "dir-7/file.txt" }, + { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" }, + { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 2, "dir-9" }, + { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 1, "dir-9/file.txt" }, + { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 3, "dir-9/file.txt" }, + { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" }, + { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 1, "file-2" }, + { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 2, "file-2" }, + { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" }, + { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" }, + { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 1, "file-4" }, + { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 3, "file-4" }, + { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" }, + { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 2, "file-5/new" }, + { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" }, + }; + + cl_git_pass(merge_trees_from_branches(&index, repo, DF_SIDE1_BRANCH, DF_SIDE2_BRANCH, NULL)); + + cl_assert(merge_test_index(index, merge_index_entries, 20)); + + git_index_free(index); +} diff --git a/tests-clar/merge/trees/treediff.c b/tests-clar/merge/trees/treediff.c new file mode 100644 index 000000000..b2554f8be --- /dev/null +++ b/tests-clar/merge/trees/treediff.c @@ -0,0 +1,279 @@ +#include "clar_libgit2.h" +#include "git2/tree.h" +#include "merge.h" +#include "../merge_helpers.h" +#include "diff.h" +#include "hashsig.h" + +static git_repository *repo; + +#define TEST_REPO_PATH "merge-resolve" + +#define TREE_OID_ANCESTOR "0d52e3a556e189ba0948ae56780918011c1b167d" +#define TREE_OID_MASTER "1f81433e3161efbf250576c58fede7f6b836f3d3" +#define TREE_OID_BRANCH "eea9286df54245fea72c5b557291470eb825f38f" + +#define TREE_OID_DF_ANCESTOR "b8a3a806d3950e8c0a03a34f234a92eff0e2c68d" +#define TREE_OID_DF_SIDE1 "ee1d6f164893c1866a323f072eeed36b855656be" +#define TREE_OID_DF_SIDE2 "6178885b38fe96e825ac0f492c0a941f288b37f6" + +void test_merge_trees_treediff__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_merge_trees_treediff__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +struct treediff_cb_data { + struct merge_index_conflict_data *conflict_data; + size_t conflict_data_len; + + size_t idx; +}; + +static void test_find_differences( + const char *ancestor_oidstr, + const char *ours_oidstr, + const char *theirs_oidstr, + struct merge_index_conflict_data *treediff_conflict_data, + size_t treediff_conflict_data_len) +{ + git_merge_diff_list *merge_diff_list = git_merge_diff_list__alloc(repo); + git_oid ancestor_oid, ours_oid, theirs_oid; + git_tree *ancestor_tree, *ours_tree, *theirs_tree; + struct treediff_cb_data treediff_cb_data = {0}; + + cl_git_pass(git_oid_fromstr(&ancestor_oid, ancestor_oidstr)); + cl_git_pass(git_oid_fromstr(&ours_oid, ours_oidstr)); + cl_git_pass(git_oid_fromstr(&theirs_oid, theirs_oidstr)); + + cl_git_pass(git_tree_lookup(&ancestor_tree, repo, &ancestor_oid)); + cl_git_pass(git_tree_lookup(&ours_tree, repo, &ours_oid)); + cl_git_pass(git_tree_lookup(&theirs_tree, repo, &theirs_oid)); + + cl_git_pass(git_merge_diff_list__find_differences(merge_diff_list, ancestor_tree, ours_tree, theirs_tree)); + + /* + dump_merge_index(merge_index); + */ + + cl_assert(treediff_conflict_data_len == merge_diff_list->conflicts.length); + + treediff_cb_data.conflict_data = treediff_conflict_data; + treediff_cb_data.conflict_data_len = treediff_conflict_data_len; + + cl_assert(merge_test_merge_conflicts(&merge_diff_list->conflicts, treediff_conflict_data, treediff_conflict_data_len)); + + git_tree_free(ancestor_tree); + git_tree_free(ours_tree); + git_tree_free(theirs_tree); + + git_merge_diff_list__free(merge_diff_list); +} + +void test_merge_trees_treediff__simple(void) +{ + struct merge_index_conflict_data treediff_conflict_data[] = { + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt", GIT_DELTA_ADDED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE + }, + + { + { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt", GIT_DELTA_MODIFIED }, + { 0100644, "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe", 0, "automergeable.txt", GIT_DELTA_MODIFIED }, + GIT_MERGE_DIFF_BOTH_MODIFIED + }, + + { + { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt", GIT_DELTA_MODIFIED }, + GIT_MERGE_DIFF_NONE + }, + + { + { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt", GIT_DELTA_MODIFIED }, + { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE + }, + + { + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt", GIT_DELTA_MODIFIED }, + { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt", GIT_DELTA_MODIFIED }, + GIT_MERGE_DIFF_BOTH_MODIFIED + }, + + { + { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + GIT_MERGE_DIFF_NONE + }, + + { + { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE + }, + }; + + test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_BRANCH, treediff_conflict_data, 7); +} + +void test_merge_trees_treediff__df_conflicts(void) +{ + struct merge_index_conflict_data treediff_conflict_data[] = { + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 0, "dir-10", GIT_DELTA_ADDED }, + { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 0, "dir-10", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_BOTH_ADDED, + }, + + { + { 0100644, "242591eb280ee9eeb2ce63524b9a8b9bc4cb515d", 0, "dir-10/file.txt", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + GIT_MERGE_DIFF_BOTH_DELETED, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 0, "dir-7", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_DIRECTORY_FILE, + }, + + { + { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 0, "dir-7/file.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 0, "dir-7/file.txt", GIT_DELTA_MODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + GIT_MERGE_DIFF_DF_CHILD, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8", GIT_DELTA_ADDED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 0, "dir-9", GIT_DELTA_ADDED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_DIRECTORY_FILE, + }, + + { + { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 0, "dir-9/file.txt", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 0, "dir-9/file.txt", GIT_DELTA_MODIFIED }, + GIT_MERGE_DIFF_DF_CHILD, + }, + + { + { 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1", GIT_DELTA_UNMODIFIED }, + { 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 0, "file-2", GIT_DELTA_UNMODIFIED }, + { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 0, "file-2", GIT_DELTA_MODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + GIT_MERGE_DIFF_DIRECTORY_FILE, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_DF_CHILD, + }, + + { + { 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new", GIT_DELTA_ADDED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 0, "file-4", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 0, "file-4", GIT_DELTA_MODIFIED }, + GIT_MERGE_DIFF_DIRECTORY_FILE, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new", GIT_DELTA_ADDED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_DF_CHILD, + }, + + { + { 0100644, "ac4045f965119e6998f4340ed0f411decfb3ec05", 0, "file-5", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + GIT_MERGE_DIFF_BOTH_DELETED, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 0, "file-5/new", GIT_DELTA_ADDED }, + { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 0, "file-5/new", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_BOTH_ADDED, + }, + }; + + test_find_differences(TREE_OID_DF_ANCESTOR, TREE_OID_DF_SIDE1, TREE_OID_DF_SIDE2, treediff_conflict_data, 20); +} + diff --git a/tests-clar/merge/trees/trivial.c b/tests-clar/merge/trees/trivial.c new file mode 100644 index 000000000..54b07e74a --- /dev/null +++ b/tests-clar/merge/trees/trivial.c @@ -0,0 +1,396 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "merge.h" +#include "../merge_helpers.h" +#include "refs.h" +#include "fileops.h" + +static git_repository *repo; + +#define TEST_REPO_PATH "merge-resolve" +#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" + + +// Fixture setup and teardown +void test_merge_trees_trivial__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_merge_trees_trivial__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + + +static int merge_trivial(git_index **index, const char *ours, const char *theirs, bool automerge) +{ + git_commit *our_commit, *their_commit, *ancestor_commit; + git_tree *our_tree, *their_tree, *ancestor_tree; + git_oid our_oid, their_oid, ancestor_oid; + git_buf branch_buf = GIT_BUF_INIT; + git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + + opts.automerge_flags |= automerge ? 0 : GIT_MERGE_AUTOMERGE_NONE; + + git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours); + cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr)); + cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid)); + + git_buf_clear(&branch_buf); + git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs); + cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr)); + cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid)); + + cl_git_pass(git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit))); + cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid)); + + cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit)); + cl_git_pass(git_commit_tree(&our_tree, our_commit)); + cl_git_pass(git_commit_tree(&their_tree, their_commit)); + + cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, &opts)); + + git_buf_free(&branch_buf); + git_tree_free(our_tree); + git_tree_free(their_tree); + git_tree_free(ancestor_tree); + git_commit_free(our_commit); + git_commit_free(their_commit); + git_commit_free(ancestor_commit); + + return 0; +} + +static int merge_trivial_conflict_entrycount(git_index *index) +{ + const git_index_entry *entry; + size_t count = 0; + size_t i; + + for (i = 0; i < git_index_entrycount(index); i++) { + cl_assert(entry = git_index_get_byindex(index, i)); + + if (git_index_entry_stage(entry) > 0) + count++; + } + + return count; +} + +/* 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote */ +void test_merge_trees_trivial__2alt(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-2alt", "trivial-2alt-branch", 0)); + + cl_assert(entry = git_index_get_bypath(result, "new-in-branch.txt", 0)); + cl_assert(git_index_reuc_entrycount(result) == 0); + cl_assert(merge_trivial_conflict_entrycount(result) == 0); + + git_index_free(result); +} + +/* 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head */ +void test_merge_trees_trivial__3alt(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-3alt", "trivial-3alt-branch", 0)); + + cl_assert(entry = git_index_get_bypath(result, "new-in-3alt.txt", 0)); + cl_assert(git_index_reuc_entrycount(result) == 0); + cl_assert(merge_trivial_conflict_entrycount(result) == 0); + + git_index_free(result); +} + +/* 4: ancest:(empty)^, head:head, remote:remote = result:no merge */ +void test_merge_trees_trivial__4(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-4", "trivial-4-branch", 0)); + + cl_assert((entry = git_index_get_bypath(result, "new-and-different.txt", 0)) == NULL); + cl_assert(git_index_reuc_entrycount(result) == 0); + + cl_assert(merge_trivial_conflict_entrycount(result) == 2); + cl_assert(entry = git_index_get_bypath(result, "new-and-different.txt", 2)); + cl_assert(entry = git_index_get_bypath(result, "new-and-different.txt", 3)); + + git_index_free(result); +} + +/* 5ALT: ancest:*, head:head, remote:head = result:head */ +void test_merge_trees_trivial__5alt_1(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-5alt-1", "trivial-5alt-1-branch", 0)); + + cl_assert(entry = git_index_get_bypath(result, "new-and-same.txt", 0)); + cl_assert(git_index_reuc_entrycount(result) == 0); + cl_assert(merge_trivial_conflict_entrycount(result) == 0); + + git_index_free(result); +} + +/* 5ALT: ancest:*, head:head, remote:head = result:head */ +void test_merge_trees_trivial__5alt_2(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-5alt-2", "trivial-5alt-2-branch", 0)); + + cl_assert(entry = git_index_get_bypath(result, "modified-to-same.txt", 0)); + cl_assert(git_index_reuc_entrycount(result) == 0); + cl_assert(merge_trivial_conflict_entrycount(result) == 0); + + git_index_free(result); +} + +/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */ +void test_merge_trees_trivial__6(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch", 0)); + + cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL); + cl_assert(git_index_reuc_entrycount(result) == 0); + + cl_assert(merge_trivial_conflict_entrycount(result) == 1); + cl_assert(entry = git_index_get_bypath(result, "removed-in-both.txt", 1)); + + git_index_free(result); +} + +/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */ +void test_merge_trees_trivial__6_automerge(void) +{ + git_index *result; + const git_index_entry *entry; + const git_index_reuc_entry *reuc; + + cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch", 1)); + + cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL); + cl_assert(git_index_reuc_entrycount(result) == 1); + cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-both.txt")); + + cl_assert(merge_trivial_conflict_entrycount(result) == 0); + + git_index_free(result); +} + +/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */ +void test_merge_trees_trivial__8(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch", 0)); + + cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL); + cl_assert(git_index_reuc_entrycount(result) == 0); + + cl_assert(merge_trivial_conflict_entrycount(result) == 2); + cl_assert(entry = git_index_get_bypath(result, "removed-in-8.txt", 1)); + cl_assert(entry = git_index_get_bypath(result, "removed-in-8.txt", 3)); + + git_index_free(result); +} + +/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */ +void test_merge_trees_trivial__8_automerge(void) +{ + git_index *result; + const git_index_entry *entry; + const git_index_reuc_entry *reuc; + + cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch", 1)); + + cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL); + + cl_assert(git_index_reuc_entrycount(result) == 1); + cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-8.txt")); + + cl_assert(merge_trivial_conflict_entrycount(result) == 0); + + git_index_free(result); +} + +/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */ +void test_merge_trees_trivial__7(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch", 0)); + + cl_assert((entry = git_index_get_bypath(result, "removed-in-7.txt", 0)) == NULL); + cl_assert(git_index_reuc_entrycount(result) == 0); + + cl_assert(merge_trivial_conflict_entrycount(result) == 2); + cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 1)); + cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 3)); + + git_index_free(result); +} + +/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */ +void test_merge_trees_trivial__7_automerge(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch", 0)); + + cl_assert((entry = git_index_get_bypath(result, "removed-in-7.txt", 0)) == NULL); + cl_assert(git_index_reuc_entrycount(result) == 0); + + cl_assert(merge_trivial_conflict_entrycount(result) == 2); + cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 1)); + cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 3)); + + git_index_free(result); +} + +/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */ +void test_merge_trees_trivial__10(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch", 0)); + + cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL); + cl_assert(git_index_reuc_entrycount(result) == 0); + + cl_assert(merge_trivial_conflict_entrycount(result) == 2); + cl_assert(entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 1)); + cl_assert(entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 2)); + + git_index_free(result); +} + +/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */ +void test_merge_trees_trivial__10_automerge(void) +{ + git_index *result; + const git_index_entry *entry; + const git_index_reuc_entry *reuc; + + cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch", 1)); + + cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL); + + cl_assert(git_index_reuc_entrycount(result) == 1); + cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-10-branch.txt")); + + cl_assert(merge_trivial_conflict_entrycount(result) == 0); + + git_index_free(result); +} + +/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */ +void test_merge_trees_trivial__9(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch", 0)); + + cl_assert((entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 0)) == NULL); + cl_assert(git_index_reuc_entrycount(result) == 0); + + cl_assert(merge_trivial_conflict_entrycount(result) == 2); + cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 1)); + cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 2)); + + git_index_free(result); +} + +/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */ +void test_merge_trees_trivial__9_automerge(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch", 1)); + + cl_assert((entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 0)) == NULL); + cl_assert(git_index_reuc_entrycount(result) == 0); + + cl_assert(merge_trivial_conflict_entrycount(result) == 2); + cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 1)); + cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 2)); + + git_index_free(result); +} + +/* 13: ancest:ancest+, head:head, remote:ancest = result:head */ +void test_merge_trees_trivial__13(void) +{ + git_index *result; + const git_index_entry *entry; + git_oid expected_oid; + + cl_git_pass(merge_trivial(&result, "trivial-13", "trivial-13-branch", 0)); + + cl_assert(entry = git_index_get_bypath(result, "modified-in-13.txt", 0)); + cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b")); + cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0); + + cl_assert(git_index_reuc_entrycount(result) == 0); + cl_assert(merge_trivial_conflict_entrycount(result) == 0); + + git_index_free(result); +} + +/* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */ +void test_merge_trees_trivial__14(void) +{ + git_index *result; + const git_index_entry *entry; + git_oid expected_oid; + + cl_git_pass(merge_trivial(&result, "trivial-14", "trivial-14-branch", 0)); + + cl_assert(entry = git_index_get_bypath(result, "modified-in-14-branch.txt", 0)); + cl_git_pass(git_oid_fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9")); + cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0); + + cl_assert(git_index_reuc_entrycount(result) == 0); + cl_assert(merge_trivial_conflict_entrycount(result) == 0); + + git_index_free(result); +} + +/* 11: ancest:ancest+, head:head, remote:remote = result:no merge */ +void test_merge_trees_trivial__11(void) +{ + git_index *result; + const git_index_entry *entry; + + cl_git_pass(merge_trivial(&result, "trivial-11", "trivial-11-branch", 0)); + + cl_assert((entry = git_index_get_bypath(result, "modified-in-both.txt", 0)) == NULL); + cl_assert(git_index_reuc_entrycount(result) == 0); + + cl_assert(merge_trivial_conflict_entrycount(result) == 3); + cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 1)); + cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 2)); + cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 3)); + + git_index_free(result); +} diff --git a/tests-clar/merge/setup.c b/tests-clar/merge/workdir/setup.c similarity index 85% rename from tests-clar/merge/setup.c rename to tests-clar/merge/workdir/setup.c index 946c67e7b..6ae9dbb14 100644 --- a/tests-clar/merge/setup.c +++ b/tests-clar/merge/workdir/setup.c @@ -32,15 +32,15 @@ static git_index *repo_index; #define OCTO5_OID "e4f618a2c3ed0669308735727df5ebf2447f022f" // Fixture setup and teardown -void test_merge_setup__initialize(void) +void test_merge_workdir_setup__initialize(void) { repo = cl_git_sandbox_init(TEST_REPO_PATH); - git_repository_index(&repo_index, repo); + git_repository_index(&repo_index, repo); } -void test_merge_setup__cleanup(void) +void test_merge_workdir_setup__cleanup(void) { - git_index_free(repo_index); + git_index_free(repo_index); cl_git_sandbox_cleanup(); } @@ -48,7 +48,8 @@ static void write_file_contents(const char *filename, const char *output) { git_buf file_path_buf = GIT_BUF_INIT; - git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo), filename); + git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo), + filename); cl_git_rewritefile(file_path_buf.ptr, output); git_buf_free(&file_path_buf); @@ -72,7 +73,7 @@ static int merge_head_foreach_cb(const git_oid *oid, void *payload) return 0; } -void test_merge_setup__head_notfound(void) +void test_merge_workdir_setup__head_notfound(void) { int error; @@ -81,7 +82,7 @@ void test_merge_setup__head_notfound(void) cl_assert(error == GIT_ENOTFOUND); } -void test_merge_setup__head_invalid_oid(void) +void test_merge_workdir_setup__head_invalid_oid(void) { int error; @@ -92,7 +93,7 @@ void test_merge_setup__head_invalid_oid(void) cl_assert(error == -1); } -void test_merge_setup__head_foreach_nonewline(void) +void test_merge_workdir_setup__head_foreach_nonewline(void) { int error; @@ -103,7 +104,7 @@ void test_merge_setup__head_foreach_nonewline(void) cl_assert(error == -1); } -void test_merge_setup__head_foreach_one(void) +void test_merge_workdir_setup__head_foreach_one(void) { const char *expected = THEIRS_SIMPLE_OID; @@ -117,7 +118,7 @@ void test_merge_setup__head_foreach_one(void) cl_assert(cb_data.i == cb_data.len); } -void test_merge_setup__head_foreach_octopus(void) +void test_merge_workdir_setup__head_foreach_octopus(void) { const char *expected[] = { THEIRS_SIMPLE_OID, OCTO1_OID, OCTO2_OID, OCTO3_OID, OCTO4_OID, OCTO5_OID }; diff --git a/tests-clar/resources/merge-resolve/.gitted/COMMIT_EDITMSG b/tests-clar/resources/merge-resolve/.gitted/COMMIT_EDITMSG new file mode 100644 index 000000000..245b18a2c --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/COMMIT_EDITMSG @@ -0,0 +1 @@ +rename conflict theirs diff --git a/tests-clar/resources/merge-resolve/.gitted/HEAD b/tests-clar/resources/merge-resolve/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests-clar/resources/merge-resolve/.gitted/ORIG_HEAD b/tests-clar/resources/merge-resolve/.gitted/ORIG_HEAD new file mode 100644 index 000000000..4092d428f --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/ORIG_HEAD @@ -0,0 +1 @@ +2392a2dacc9efb562b8635d6579fb458751c7c5b diff --git a/tests-clar/resources/merge-resolve/.gitted/config b/tests-clar/resources/merge-resolve/.gitted/config new file mode 100644 index 000000000..af107929f --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true diff --git a/tests-clar/resources/merge-resolve/.gitted/description b/tests-clar/resources/merge-resolve/.gitted/description new file mode 100644 index 000000000..498b267a8 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/tests-clar/resources/merge-resolve/.gitted/index b/tests-clar/resources/merge-resolve/.gitted/index new file mode 100644 index 0000000000000000000000000000000000000000..230eba9eb269af72a059d8017d293e3b375e751a GIT binary patch literal 624 zcmZ?q402{*U|<4b_P}(lB|w@1Ml&)nurmES{eyv_5h%|16(}VF#InjZoRTMJycOi~ z&i|ZmYW1eO(4?I~I58z9HAOcwPd7KQxFoemucV>`WEc>@%!AQTbAO_l`~98$!W{>= z{?|WHNd3E&BV{-5WS#R2f{CRi`MIe@>8XiHIjLY%VdlVSsCmE8%oALlGHC#i`gwZ z*0A#ER;PtNLNOOD1YqXDXsEe=(9G5H>&Qz`)jYLwwcvI6wq);%+aBB#VBk;A&r8e6 zOfJdHONY7?W)6&on)esYJgNJSKWVR$&p#L9K0(HyZP(+Bd)apxM2k{$^UFx_|35Ty z 1351563869 -0500 commit (initial): initial +c607fc30883e335def28cd686b51f6cfa02b06ec c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1351563886 -0500 checkout: moving from master to branch +c607fc30883e335def28cd686b51f6cfa02b06ec 7cb63eed597130ba4abb87b3e544b85021905520 Edward Thomson 1351563965 -0500 commit: branch +7cb63eed597130ba4abb87b3e544b85021905520 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1351563968 -0500 checkout: moving from branch to master +c607fc30883e335def28cd686b51f6cfa02b06ec 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351564033 -0500 commit: master +977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351605785 -0500 checkout: moving from master to ff_branch +977c696519c5a3004c5f1d15d60c89dbeb8f235f 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson 1351605830 -0500 commit: fastforward +33d500f588fbbe65901d82b4e6b008e549064be0 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351605889 -0500 checkout: moving from ff_branch to master +977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351874933 -0500 checkout: moving from master to octo1 +977c696519c5a3004c5f1d15d60c89dbeb8f235f 16f825815cfd20a07a75c71554e82d8eede0b061 Edward Thomson 1351874954 -0500 commit: octo1 +16f825815cfd20a07a75c71554e82d8eede0b061 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351874957 -0500 checkout: moving from octo1 to master +977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351874960 -0500 checkout: moving from master to octo2 +977c696519c5a3004c5f1d15d60c89dbeb8f235f 158dc7bedb202f5b26502bf3574faa7f4238d56c Edward Thomson 1351874974 -0500 commit: octo2 +158dc7bedb202f5b26502bf3574faa7f4238d56c 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351874976 -0500 checkout: moving from octo2 to master +977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351874980 -0500 checkout: moving from master to octo3 +977c696519c5a3004c5f1d15d60c89dbeb8f235f 50ce7d7d01217679e26c55939eef119e0c93e272 Edward Thomson 1351874998 -0500 commit: octo3 +50ce7d7d01217679e26c55939eef119e0c93e272 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875006 -0500 checkout: moving from octo3 to master +977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875010 -0500 checkout: moving from master to octo4 +977c696519c5a3004c5f1d15d60c89dbeb8f235f 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson 1351875023 -0500 commit: octo4 +54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875031 -0500 checkout: moving from octo4 to master +977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875031 -0500 checkout: moving from master to octo5 +977c696519c5a3004c5f1d15d60c89dbeb8f235f e4f618a2c3ed0669308735727df5ebf2447f022f Edward Thomson 1351875041 -0500 commit: octo5 +e4f618a2c3ed0669308735727df5ebf2447f022f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875046 -0500 checkout: moving from octo5 to master +977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875046 -0500 checkout: moving from master to octo6 +977c696519c5a3004c5f1d15d60c89dbeb8f235f 4ca408a8c88655f7586a1b580be6fad138121e98 Edward Thomson 1351875057 -0500 commit: octo5 +4ca408a8c88655f7586a1b580be6fad138121e98 b6f610aef53bd343e6c96227de874c66f00ee8e8 Edward Thomson 1351875065 -0500 commit (amend): octo6 +b6f610aef53bd343e6c96227de874c66f00ee8e8 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875071 -0500 checkout: moving from octo6 to master +977c696519c5a3004c5f1d15d60c89dbeb8f235f 4e0d9401aee78eb345a8685a859d37c8c3c0bbed Edward Thomson 1351875091 -0500 merge octo1 octo2 octo3 octo4: Merge made by the 'octopus' strategy. +4e0d9401aee78eb345a8685a859d37c8c3c0bbed 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson 1351875108 -0500 reset: moving to 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae +54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875584 -0500 reset: moving to 977c696519c5a3004c5f1d15d60c89dbeb8f235f +bd593285fc7fe4ca18ccdbabf027f5d689101452 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson 1351990193 -0500 checkout: moving from master to ff_branch +33d500f588fbbe65901d82b4e6b008e549064be0 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1351990202 -0500 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1351990205 -0500 merge master: Fast-forward +bd593285fc7fe4ca18ccdbabf027f5d689101452 fd89f8cffb663ac89095a0f9764902e93ceaca6a Edward Thomson 1351990229 -0500 commit: fastforward +fd89f8cffb663ac89095a0f9764902e93ceaca6a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1351990233 -0500 checkout: moving from ff_branch to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352091703 -0600 checkout: moving from master to trivial-2alt +c607fc30883e335def28cd686b51f6cfa02b06ec c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352092411 -0600 checkout: moving from trivial-2alt to trivial-2alt-branch +c607fc30883e335def28cd686b51f6cfa02b06ec c9174cef549ec94ecbc43ef03cdc775b4950becb Edward Thomson 1352092434 -0600 commit: 2alt-branch +c9174cef549ec94ecbc43ef03cdc775b4950becb c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352092440 -0600 checkout: moving from trivial-2alt-branch to trivial-2alt +c607fc30883e335def28cd686b51f6cfa02b06ec 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson 1352092452 -0600 commit: 2alt +bd593285fc7fe4ca18ccdbabf027f5d689101452 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson 1352094476 -0600 checkout: moving from master to trivial-3alt +566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352094547 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 5459c89aa0026d543ce8343bd89871bce543f9c2 Edward Thomson 1352094580 -0600 commit: 3alt +5459c89aa0026d543ce8343bd89871bce543f9c2 4c9fac0707f8d4195037ae5a681aa48626491541 Edward Thomson 1352094610 -0600 commit: 3alt-branch +4c9fac0707f8d4195037ae5a681aa48626491541 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352094620 -0600 checkout: moving from trivial-3alt to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson 1352094752 -0600 checkout: moving from master to trivial-4 +566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352094764 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson 1352094815 -0600 commit: trivial-4 +cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352094843 -0600 checkout: moving from trivial-4 to trivial-4-branch +c607fc30883e335def28cd686b51f6cfa02b06ec 183310e30fb1499af8c619108ffea4d300b5e778 Edward Thomson 1352094856 -0600 commit: trivial-4-branch +183310e30fb1499af8c619108ffea4d300b5e778 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352094860 -0600 checkout: moving from trivial-4-branch to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson 1352096588 -0600 checkout: moving from master to trivial-4 +cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352096612 -0600 checkout: moving from trivial-4 to trivial-5alt-1 +c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson 1352096643 -0600 commit: 5alt-1 +4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352096661 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-1-branch +c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson 1352096671 -0600 checkout: moving from trivial-5alt-1-branch to trivial-5alt-1 +4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352096678 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-1-branch +c607fc30883e335def28cd686b51f6cfa02b06ec 478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 Edward Thomson 1352096689 -0600 commit: 5alt-1-branch +478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson 1352096701 -0600 checkout: moving from trivial-5alt-1-branch to trivial-5alt-1 +4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352096715 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-2 +c607fc30883e335def28cd686b51f6cfa02b06ec ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson 1352096764 -0600 commit: existing file +ebc09d0137cfb0c26697aed0109fb943ad906f3f 3b47b031b3e55ae11e14a05260b1c3ffd6838d55 Edward Thomson 1352096815 -0600 commit: 5alt-2 +3b47b031b3e55ae11e14a05260b1c3ffd6838d55 ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson 1352096840 -0600 checkout: moving from trivial-5alt-2 to trivial-5alt-2-branch +ebc09d0137cfb0c26697aed0109fb943ad906f3f f48097eb340dc5a7cae55aabcf1faf4548aa821f Edward Thomson 1352096855 -0600 commit: 5alt-2-branch +f48097eb340dc5a7cae55aabcf1faf4548aa821f bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352096858 -0600 checkout: moving from trivial-5alt-2-branch to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352097377 -0600 checkout: moving from master to trivial-6 +c607fc30883e335def28cd686b51f6cfa02b06ec f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson 1352097389 -0600 commit: 6 +f7c332bd4d4d4b777366cae4d24d1687477576bf 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson 1352097404 -0600 commit: trivial-6 +99b4f7e4f24470fa06b980bc21f1095c2a9425c0 f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson 1352097420 -0600 checkout: moving from trivial-6 to trivial-6-branch +f7c332bd4d4d4b777366cae4d24d1687477576bf a43150a738849c59376cf30bb2a68348a83c8f48 Edward Thomson 1352097431 -0600 commit: 6-branch +a43150a738849c59376cf30bb2a68348a83c8f48 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352097442 -0600 checkout: moving from trivial-6-branch to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson 1352098040 -0600 checkout: moving from master to trivial-6 +99b4f7e4f24470fa06b980bc21f1095c2a9425c0 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352098057 -0600 checkout: moving from trivial-6 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson 1352098792 -0600 checkout: moving from master to trivial-4 +cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352098818 -0600 checkout: moving from trivial-4 to trivial-8 +c607fc30883e335def28cd686b51f6cfa02b06ec 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson 1352098884 -0600 commit: trivial-8 +75a811bf6bc57694adb3fe604786f3a4efd1cd1b 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson 1352098947 -0600 checkout: moving from trivial-8 to trivial-8-branch +75a811bf6bc57694adb3fe604786f3a4efd1cd1b 52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 Edward Thomson 1352098979 -0600 commit: trivial-8-branch +52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson 1352098982 -0600 checkout: moving from trivial-8-branch to trivial-8 +75a811bf6bc57694adb3fe604786f3a4efd1cd1b 3575826c96a975031d2c14368529cc5c4353a8fd Edward Thomson 1352099000 -0600 commit: trivial-8 +3575826c96a975031d2c14368529cc5c4353a8fd bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352099008 -0600 checkout: moving from trivial-8 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352099776 -0600 checkout: moving from master to trivial-7 +c607fc30883e335def28cd686b51f6cfa02b06ec 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson 1352099790 -0600 commit: trivial-7 +092ce8682d7f3a2a3a769a6daca58950168ba5c4 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson 1352099799 -0600 checkout: moving from trivial-7 to trivial-7-branch +092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson 1352099812 -0600 commit: trivial-7-branch +73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson 1352099815 -0600 checkout: moving from trivial-7-branch to trivial-7 +092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson 1352099838 -0600 checkout: moving from trivial-7 to trivial-7-branch +73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson 1352099874 -0600 reset: moving to 092ce8682d7f3a2a3a769a6daca58950168ba5c4 +092ce8682d7f3a2a3a769a6daca58950168ba5c4 009b9cab6fdac02915a88ecd078b7a792ed802d8 Edward Thomson 1352099921 -0600 commit: removed in 7 +009b9cab6fdac02915a88ecd078b7a792ed802d8 5195a1b480f66691b667f10a9e41e70115a78351 Edward Thomson 1352099927 -0600 commit (amend): trivial-7-branch +5195a1b480f66691b667f10a9e41e70115a78351 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson 1352099937 -0600 checkout: moving from trivial-7-branch to trivial-7 +092ce8682d7f3a2a3a769a6daca58950168ba5c4 d874671ef5b20184836cb983bb273e5280384d0b Edward Thomson 1352099947 -0600 commit: trivial-7 +d874671ef5b20184836cb983bb273e5280384d0b bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352099949 -0600 checkout: moving from trivial-7 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352100174 -0600 checkout: moving from master to trivial-10 +c607fc30883e335def28cd686b51f6cfa02b06ec 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson 1352100193 -0600 commit: trivial-10 +53825f41ac8d640612f9423a2f03a69f3d96809a 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson 1352100200 -0600 checkout: moving from trivial-10 to trivial-10-branch +53825f41ac8d640612f9423a2f03a69f3d96809a 11f4f3c08b737f5fd896cbefa1425ee63b21b2fa Edward Thomson 1352100211 -0600 commit: trivial-10-branch +11f4f3c08b737f5fd896cbefa1425ee63b21b2fa 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson 1352100214 -0600 checkout: moving from trivial-10-branch to trivial-10 +53825f41ac8d640612f9423a2f03a69f3d96809a 0ec5f433959cd46177f745903353efb5be08d151 Edward Thomson 1352100223 -0600 commit: trivial-10 +0ec5f433959cd46177f745903353efb5be08d151 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352100225 -0600 checkout: moving from trivial-10 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352100270 -0600 checkout: moving from master to trivial-9 +c607fc30883e335def28cd686b51f6cfa02b06ec f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson 1352100304 -0600 commit: trivial-9 +f0053b8060bb3f0be5cbcc3147a07ece26bf097e f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson 1352100310 -0600 checkout: moving from trivial-9 to trivial-9-branch +f0053b8060bb3f0be5cbcc3147a07ece26bf097e 13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e Edward Thomson 1352100317 -0600 commit: trivial-9-branch +13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson 1352100319 -0600 checkout: moving from trivial-9-branch to trivial-9 +f0053b8060bb3f0be5cbcc3147a07ece26bf097e c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a Edward Thomson 1352100333 -0600 commit: trivial-9 +c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352100335 -0600 checkout: moving from trivial-9 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352100576 -0600 checkout: moving from master to trivial-13 +c607fc30883e335def28cd686b51f6cfa02b06ec 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson 1352100589 -0600 commit: trivial-13 +8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson 1352100604 -0600 checkout: moving from trivial-13 to trivial-13-branch +8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c Edward Thomson 1352100610 -0600 commit: trivial-13-branch +05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson 1352100612 -0600 checkout: moving from trivial-13-branch to trivial-13 +8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa a3fabece9eb8748da810e1e08266fef9b7136ad4 Edward Thomson 1352100625 -0600 commit: trivial-13 +a3fabece9eb8748da810e1e08266fef9b7136ad4 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352100627 -0600 checkout: moving from trivial-13 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352100936 -0600 checkout: moving from master to trivial-11 +c607fc30883e335def28cd686b51f6cfa02b06ec 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson 1352100958 -0600 commit: trivial-11 +35632e43612c06a3ea924bfbacd48333da874c29 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson 1352100964 -0600 checkout: moving from trivial-11 to trivial-11-branch +35632e43612c06a3ea924bfbacd48333da874c29 6718a45909532d1fcf5600d0877f7fe7e78f0b86 Edward Thomson 1352100978 -0600 commit: trivial-11-branch +6718a45909532d1fcf5600d0877f7fe7e78f0b86 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson 1352100981 -0600 checkout: moving from trivial-11-branch to trivial-11 +35632e43612c06a3ea924bfbacd48333da874c29 3168dca1a561889b045a6441909f4c56145e666d Edward Thomson 1352100992 -0600 commit: trivial-11 +3168dca1a561889b045a6441909f4c56145e666d bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352100996 -0600 checkout: moving from trivial-11 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352101098 -0600 checkout: moving from master to trivial-14 +c607fc30883e335def28cd686b51f6cfa02b06ec 596803b523203a4851c824c07366906f8353f4ad Edward Thomson 1352101113 -0600 commit: trivial-14 +596803b523203a4851c824c07366906f8353f4ad 596803b523203a4851c824c07366906f8353f4ad Edward Thomson 1352101117 -0600 checkout: moving from trivial-14 to trivial-14-branch +596803b523203a4851c824c07366906f8353f4ad 8187117062b750eed4f93fd7e899f17b52ce554d Edward Thomson 1352101132 -0600 commit: trivial-14-branch +8187117062b750eed4f93fd7e899f17b52ce554d 596803b523203a4851c824c07366906f8353f4ad Edward Thomson 1352101135 -0600 checkout: moving from trivial-14-branch to trivial-14 +596803b523203a4851c824c07366906f8353f4ad 7e2d058d5fedf8329db44db4fac610d6b1a89159 Edward Thomson 1352101141 -0600 commit: trivial-14 +7e2d058d5fedf8329db44db4fac610d6b1a89159 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1352101145 -0600 checkout: moving from trivial-14 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1353177749 -0600 checkout: moving from master to renames1 +c607fc30883e335def28cd686b51f6cfa02b06ec 412b32fb66137366147f1801ecc962452757d48a Edward Thomson 1353177886 -0600 commit: renames +412b32fb66137366147f1801ecc962452757d48a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1353794607 -0600 checkout: moving from renames1 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1353794647 -0600 checkout: moving from master to renames2 +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1353794677 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec ab40af3cb8a3ed2e2843e96d9aa7871336b94573 Edward Thomson 1353794852 -0600 commit: renames2 +ab40af3cb8a3ed2e2843e96d9aa7871336b94573 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1353794883 -0600 checkout: moving from renames2 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1354574697 -0600 checkout: moving from master to df_side1 +bd593285fc7fe4ca18ccdbabf027f5d689101452 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1354574962 -0600 commit: df_ancestor +d4207f77243500bec335ab477f9227fcdb1e271a c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson 1354575027 -0600 commit: df_side1 +c94b27e41064c521120627e07e2035cca1d24ffa d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1354575070 -0600 checkout: moving from df_side1 to df_side2 +d4207f77243500bec335ab477f9227fcdb1e271a f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson 1354575206 -0600 commit: df_side2 +f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1354575381 -0600 checkout: moving from df_side2 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson 1355017614 -0600 checkout: moving from master to df_side1 +c94b27e41064c521120627e07e2035cca1d24ffa a90bc3fb6f15181972a2959a921429efbd81a473 Edward Thomson 1355017650 -0600 commit: df_added +a90bc3fb6f15181972a2959a921429efbd81a473 c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson 1355017673 -0600 checkout: moving from df_side1 to c94b27e +c94b27e41064c521120627e07e2035cca1d24ffa d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1355017673 -0600 rebase -i (squash): updating HEAD +d4207f77243500bec335ab477f9227fcdb1e271a 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson 1355017673 -0600 rebase -i (squash): df_side1 +005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson 1355017676 -0600 rebase -i (finish): returning to refs/heads/df_side1 +005b6fcc8fec71d2550bef8462d169b3c26aa14b f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson 1355017715 -0600 reset: moving to df_side2 +f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 8c749d9968d4b10dcfb06c9f97d0e5d92d337071 Edward Thomson 1355017744 -0600 commit: df_added +8c749d9968d4b10dcfb06c9f97d0e5d92d337071 f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson 1355017754 -0600 checkout: moving from df_side1 to f8958bd +f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1355017754 -0600 rebase -i (squash): updating HEAD +d4207f77243500bec335ab477f9227fcdb1e271a 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson 1355017754 -0600 rebase -i (squash): df_side2 +0204a84f822acbf6386b36d33f1f6bc68bbbf858 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson 1355017756 -0600 rebase -i (finish): returning to refs/heads/df_side1 +0204a84f822acbf6386b36d33f1f6bc68bbbf858 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson 1355017793 -0600 reset: moving to 005b6fcc8fec71d2550bef8462d169b3c26aa14b +005b6fcc8fec71d2550bef8462d169b3c26aa14b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson 1355017826 -0600 reset: moving to 0204a84 +0204a84f822acbf6386b36d33f1f6bc68bbbf858 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1355017847 -0600 checkout: moving from df_side1 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson 1355168677 -0600 checkout: moving from master to df_side1 +005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson 1355168829 -0600 checkout: moving from df_side1 to df_side1 +005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson 1355168838 -0600 checkout: moving from df_side1 to df_side1 +005b6fcc8fec71d2550bef8462d169b3c26aa14b e8107f24196736b870a318a0e28f048e29f6feff Edward Thomson 1355169065 -0600 commit: df_side1 +e8107f24196736b870a318a0e28f048e29f6feff 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson 1355169081 -0600 checkout: moving from df_side1 to 005b6fc +005b6fcc8fec71d2550bef8462d169b3c26aa14b d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1355169081 -0600 rebase -i (squash): updating HEAD +d4207f77243500bec335ab477f9227fcdb1e271a 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson 1355169081 -0600 rebase -i (squash): df_side1 +80a8fbb3abb1ba423d554e9630b8fc2e5698f86b 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson 1355169084 -0600 rebase -i (finish): returning to refs/heads/df_side1 +80a8fbb3abb1ba423d554e9630b8fc2e5698f86b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson 1355169141 -0600 checkout: moving from df_side1 to df_side2 +0204a84f822acbf6386b36d33f1f6bc68bbbf858 944f5dd1a867cab4c2bbcb896493435cae1dcc1a Edward Thomson 1355169174 -0600 commit: both +944f5dd1a867cab4c2bbcb896493435cae1dcc1a 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson 1355169182 -0600 checkout: moving from df_side2 to 0204a84 +0204a84f822acbf6386b36d33f1f6bc68bbbf858 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1355169182 -0600 rebase -i (squash): updating HEAD +d4207f77243500bec335ab477f9227fcdb1e271a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson 1355169182 -0600 rebase -i (squash): df_side2 +57079a46233ae2b6df62e9ade71c4948512abefb 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson 1355169185 -0600 rebase -i (finish): returning to refs/heads/df_side2 +57079a46233ae2b6df62e9ade71c4948512abefb 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson 1355169241 -0600 checkout: moving from df_side2 to df_side1 +80a8fbb3abb1ba423d554e9630b8fc2e5698f86b e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 Edward Thomson 1355169419 -0600 commit: side1 +e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson 1355169431 -0600 checkout: moving from df_side1 to 80a8fbb +80a8fbb3abb1ba423d554e9630b8fc2e5698f86b d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1355169431 -0600 rebase -i (squash): updating HEAD +d4207f77243500bec335ab477f9227fcdb1e271a 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson 1355169431 -0600 rebase -i (squash): df_side1 +5dc1018e90b19654bee986b7a0c268804d39659d 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson 1355169435 -0600 rebase -i (finish): returning to refs/heads/df_side1 +5dc1018e90b19654bee986b7a0c268804d39659d 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson 1355169439 -0600 checkout: moving from df_side1 to df_side2 +57079a46233ae2b6df62e9ade71c4948512abefb 58e853f66699fd02629fd50bde08082bc005933a Edward Thomson 1355169460 -0600 commit: side2 +58e853f66699fd02629fd50bde08082bc005933a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson 1355169469 -0600 checkout: moving from df_side2 to 57079a4 +57079a46233ae2b6df62e9ade71c4948512abefb d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1355169469 -0600 rebase -i (squash): updating HEAD +d4207f77243500bec335ab477f9227fcdb1e271a fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson 1355169469 -0600 rebase -i (squash): df_side2 +fada9356aa3f74622327a3038ae9c6f92e1c5c1d fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson 1355169471 -0600 rebase -i (finish): returning to refs/heads/df_side2 +fada9356aa3f74622327a3038ae9c6f92e1c5c1d 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson 1355169494 -0600 checkout: moving from df_side2 to df_side1 +5dc1018e90b19654bee986b7a0c268804d39659d d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1355169663 -0600 checkout: moving from df_side1 to d4207f77243500bec335ab477f9227fcdb1e271a +d4207f77243500bec335ab477f9227fcdb1e271a 849619b03ae540acee4d1edec96b86993da6b497 Edward Thomson 1355169683 -0600 commit: both_dirs +849619b03ae540acee4d1edec96b86993da6b497 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1355169691 -0600 checkout: moving from 849619b03ae540acee4d1edec96b86993da6b497 to d4207f7 +d4207f77243500bec335ab477f9227fcdb1e271a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1355169691 -0600 rebase -i (squash): updating HEAD +bd593285fc7fe4ca18ccdbabf027f5d689101452 a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson 1355169691 -0600 rebase -i (squash): df_ancestor +a765fb87eb2f7a1920b73b2d5a057f8f8476a42b 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson 1355169706 -0600 checkout: moving from a765fb87eb2f7a1920b73b2d5a057f8f8476a42b to df_side1 +5dc1018e90b19654bee986b7a0c268804d39659d a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson 1355169715 -0600 checkout: moving from df_side1 to a765fb87eb2f7a1920b73b2d5a057f8f8476a42b^0 +a765fb87eb2f7a1920b73b2d5a057f8f8476a42b bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson 1355169801 -0600 commit: df_side1 +bc744705e1d8a019993cf88f62bc4020f1b80919 bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson 1355169822 -0600 checkout: moving from bc744705e1d8a019993cf88f62bc4020f1b80919 to df_side1 +bc744705e1d8a019993cf88f62bc4020f1b80919 fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson 1355169826 -0600 checkout: moving from df_side1 to df_side2 +fada9356aa3f74622327a3038ae9c6f92e1c5c1d a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson 1355169866 -0600 checkout: moving from df_side2 to a765fb87eb2f7a1920b73b2d5a057f8f8476a42b^0 +a765fb87eb2f7a1920b73b2d5a057f8f8476a42b 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson 1355169897 -0600 rebase: df_side2 +95646149ab6b6ba6edc83cff678582538b457b2b 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson 1355169897 -0600 rebase finished: returning to refs/heads/df_side2 +95646149ab6b6ba6edc83cff678582538b457b2b bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson 1355169949 -0600 checkout: moving from df_side2 to df_side1 +bc744705e1d8a019993cf88f62bc4020f1b80919 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1355170046 -0600 checkout: moving from df_side1 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1355181639 -0600 checkout: moving from master to df_ancestor +bd593285fc7fe4ca18ccdbabf027f5d689101452 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson 1355181673 -0600 commit: df_ancestor +2da538570bc1e5b2c3e855bf702f35248ad0735f a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson 1355181715 -0600 commit: df_side1 +a7dbfcbfc1a60709cb80b5ca24539008456531d0 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson 1355181743 -0600 checkout: moving from df_ancestor to df_ancestor +a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson 1355181775 -0600 commit: df_side2 +9a301fbe6fada7dcb74fcd7c20269b5c743459a7 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson 1355181793 -0600 checkout: moving from df_ancestor to df_side1 +a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson 1355181797 -0600 checkout: moving from df_side1 to df_side2 +9a301fbe6fada7dcb74fcd7c20269b5c743459a7 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson 1355182062 -0600 checkout: moving from df_side2 to df_ancestor +9a301fbe6fada7dcb74fcd7c20269b5c743459a7 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson 1355182067 -0600 reset: moving to 2da538570bc1e5b2c3e855bf702f35248ad0735f +2da538570bc1e5b2c3e855bf702f35248ad0735f 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson 1355182087 -0600 checkout: moving from df_ancestor to df_side2 +2da538570bc1e5b2c3e855bf702f35248ad0735f fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson 1355182104 -0600 commit: df_side2 +fc90237dc4891fa6c69827fc465632225e391618 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson 1355182111 -0600 checkout: moving from df_side2 to df_side1 +a7dbfcbfc1a60709cb80b5ca24539008456531d0 fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson 1355182115 -0600 checkout: moving from df_side1 to df_side2 +fc90237dc4891fa6c69827fc465632225e391618 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1355182122 -0600 checkout: moving from df_side2 to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 d6cf6c7741b3316826af1314042550c97ded1d50 Edward Thomson 1358997543 -0600 checkout: moving from master to unrelated +d6cf6c7741b3316826af1314042550c97ded1d50 55b4e4687e7a0d9ca367016ed930f385d4022e6f Edward Thomson 1358997664 -0600 commit: conflicting changes +55b4e4687e7a0d9ca367016ed930f385d4022e6f bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1358997675 -0600 checkout: moving from unrelated to master +bd593285fc7fe4ca18ccdbabf027f5d689101452 88e185910a15cd13bdf44854ad037f4842b03b29 Edward Thomson 1365714471 -0500 checkout: moving from master to rename_conflict_ours +88e185910a15cd13bdf44854ad037f4842b03b29 bef6e37b3ee632ba74159168836f382fed21d77d Edward Thomson 1365714516 -0500 checkout: moving from rename_conflict_ours to bef6e37b3ee632ba74159168836f382fed21d77d +bef6e37b3ee632ba74159168836f382fed21d77d 01f149e1b8f84bd8896aaff6d6b22af88459ded0 Edward Thomson 1365714831 -0500 commit: rename ancestor +0000000000000000000000000000000000000000 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson 1365714958 -0500 commit (initial): rename conflict ancestor +2392a2dacc9efb562b8635d6579fb458751c7c5b 88e185910a15cd13bdf44854ad037f4842b03b29 Edward Thomson 1365714980 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours +88e185910a15cd13bdf44854ad037f4842b03b29 7c2c5228c9e90170d4a35e6558e47163daf092e5 Edward Thomson 1365715250 -0500 commit: rename conflict ours +7c2c5228c9e90170d4a35e6558e47163daf092e5 2f4024ce528d36d8670c289cce5a7963e625bb0c Edward Thomson 1365715274 -0500 checkout: moving from rename_conflict_ours to rename_conflict_theirs +2f4024ce528d36d8670c289cce5a7963e625bb0c 56a638b76b75e068590ac999c2f8621e7f3e264c Edward Thomson 1365715362 -0500 commit: rename conflict theirs +56a638b76b75e068590ac999c2f8621e7f3e264c 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson 1365715368 -0500 checkout: moving from rename_conflict_theirs to rename_conflict_ancestor +2392a2dacc9efb562b8635d6579fb458751c7c5b 56a638b76b75e068590ac999c2f8621e7f3e264c Edward Thomson 1365715371 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_theirs +56a638b76b75e068590ac999c2f8621e7f3e264c 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson 1365715404 -0500 checkout: moving from rename_conflict_theirs to rename_conflict_ancestor +2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson 1365715438 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours +2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson 1365715480 -0500 checkout: moving from rename_conflict_ours to rename_conflict_ancestor +2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson 1365715486 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours +2392a2dacc9efb562b8635d6579fb458751c7c5b f3293571dcd708b6a3faf03818cd2844d000e198 Edward Thomson 1365715538 -0500 commit: rename conflict ours +f3293571dcd708b6a3faf03818cd2844d000e198 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson 1365715546 -0500 checkout: moving from rename_conflict_ours to rename_conflict_ancestor +2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson 1365715550 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_thiers +2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson 1365715554 -0500 checkout: moving from rename_conflict_thiers to rename_conflict_ancestor +2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson 1365715557 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_theirs +2392a2dacc9efb562b8635d6579fb458751c7c5b a802e06f1782a9645b9851bc7202cee74a8a4972 Edward Thomson 1365715572 -0500 commit: rename conflict theirs +a802e06f1782a9645b9851bc7202cee74a8a4972 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1365715620 -0500 checkout: moving from rename_conflict_theirs to master diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/branch new file mode 100644 index 000000000..8b0acb702 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1351563886 -0500 branch: Created from HEAD +c607fc30883e335def28cd686b51f6cfa02b06ec 7cb63eed597130ba4abb87b3e544b85021905520 Edward Thomson 1351563965 -0500 commit: branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor new file mode 100644 index 000000000..df7695a66 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor @@ -0,0 +1,5 @@ +0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1355181639 -0600 branch: Created from HEAD +bd593285fc7fe4ca18ccdbabf027f5d689101452 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson 1355181673 -0600 commit: df_ancestor +2da538570bc1e5b2c3e855bf702f35248ad0735f a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson 1355181715 -0600 commit: df_side1 +a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson 1355181775 -0600 commit: df_side2 +9a301fbe6fada7dcb74fcd7c20269b5c743459a7 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson 1355182067 -0600 reset: moving to 2da538570bc1e5b2c3e855bf702f35248ad0735f diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side1 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side1 new file mode 100644 index 000000000..a504ad610 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side1 @@ -0,0 +1,14 @@ +0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1354574697 -0600 branch: Created from HEAD +bd593285fc7fe4ca18ccdbabf027f5d689101452 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1354574962 -0600 commit: df_ancestor +d4207f77243500bec335ab477f9227fcdb1e271a c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson 1354575027 -0600 commit: df_side1 +c94b27e41064c521120627e07e2035cca1d24ffa a90bc3fb6f15181972a2959a921429efbd81a473 Edward Thomson 1355017650 -0600 commit: df_added +a90bc3fb6f15181972a2959a921429efbd81a473 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson 1355017676 -0600 rebase -i (finish): refs/heads/df_side1 onto c94b27e +005b6fcc8fec71d2550bef8462d169b3c26aa14b f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson 1355017715 -0600 reset: moving to df_side2 +f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 8c749d9968d4b10dcfb06c9f97d0e5d92d337071 Edward Thomson 1355017744 -0600 commit: df_added +8c749d9968d4b10dcfb06c9f97d0e5d92d337071 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson 1355017756 -0600 rebase -i (finish): refs/heads/df_side1 onto f8958bd +0204a84f822acbf6386b36d33f1f6bc68bbbf858 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson 1355017793 -0600 reset: moving to 005b6fcc8fec71d2550bef8462d169b3c26aa14b +005b6fcc8fec71d2550bef8462d169b3c26aa14b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson 1355017826 -0600 reset: moving to 0204a84 +005b6fcc8fec71d2550bef8462d169b3c26aa14b e8107f24196736b870a318a0e28f048e29f6feff Edward Thomson 1355169065 -0600 commit: df_side1 +e8107f24196736b870a318a0e28f048e29f6feff 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson 1355169084 -0600 rebase -i (finish): refs/heads/df_side1 onto 005b6fc +80a8fbb3abb1ba423d554e9630b8fc2e5698f86b e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 Edward Thomson 1355169419 -0600 commit: side1 +e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson 1355169435 -0600 rebase -i (finish): refs/heads/df_side1 onto 80a8fbb diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side2 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side2 new file mode 100644 index 000000000..27d833eda --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side2 @@ -0,0 +1,9 @@ +0000000000000000000000000000000000000000 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson 1354575051 -0600 branch: Created from d4207f77243500bec335ab477f9227fcdb1e271a +d4207f77243500bec335ab477f9227fcdb1e271a f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson 1354575206 -0600 commit: df_side2 +0204a84f822acbf6386b36d33f1f6bc68bbbf858 944f5dd1a867cab4c2bbcb896493435cae1dcc1a Edward Thomson 1355169174 -0600 commit: both +944f5dd1a867cab4c2bbcb896493435cae1dcc1a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson 1355169185 -0600 rebase -i (finish): refs/heads/df_side2 onto 0204a84 +57079a46233ae2b6df62e9ade71c4948512abefb 58e853f66699fd02629fd50bde08082bc005933a Edward Thomson 1355169460 -0600 commit: side2 +58e853f66699fd02629fd50bde08082bc005933a fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson 1355169471 -0600 rebase -i (finish): refs/heads/df_side2 onto 57079a4 +fada9356aa3f74622327a3038ae9c6f92e1c5c1d 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson 1355169897 -0600 rebase finished: refs/heads/df_side2 onto a765fb87eb2f7a1920b73b2d5a057f8f8476a42b +0000000000000000000000000000000000000000 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson 1355182087 -0600 branch: Created from HEAD +2da538570bc1e5b2c3e855bf702f35248ad0735f fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson 1355182104 -0600 commit: df_side2 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch new file mode 100644 index 000000000..c4706175d --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch @@ -0,0 +1,5 @@ +0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351605785 -0500 branch: Created from HEAD +977c696519c5a3004c5f1d15d60c89dbeb8f235f 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson 1351605830 -0500 commit: fastforward +33d500f588fbbe65901d82b4e6b008e549064be0 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1351990202 -0500 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1351990205 -0500 merge master: Fast-forward +bd593285fc7fe4ca18ccdbabf027f5d689101452 fd89f8cffb663ac89095a0f9764902e93ceaca6a Edward Thomson 1351990229 -0500 commit: fastforward diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/master b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/master new file mode 100644 index 000000000..60475992a --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/master @@ -0,0 +1,5 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1351563869 -0500 commit (initial): initial +c607fc30883e335def28cd686b51f6cfa02b06ec 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351564033 -0500 commit: master +977c696519c5a3004c5f1d15d60c89dbeb8f235f 4e0d9401aee78eb345a8685a859d37c8c3c0bbed Edward Thomson 1351875091 -0500 merge octo1 octo2 octo3 octo4: Merge made by the 'octopus' strategy. +4e0d9401aee78eb345a8685a859d37c8c3c0bbed 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson 1351875108 -0500 reset: moving to 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae +54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875584 -0500 reset: moving to 977c696519c5a3004c5f1d15d60c89dbeb8f235f diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo1 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo1 new file mode 100644 index 000000000..0b6c9214a --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo1 @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351874933 -0500 branch: Created from HEAD +977c696519c5a3004c5f1d15d60c89dbeb8f235f 16f825815cfd20a07a75c71554e82d8eede0b061 Edward Thomson 1351874954 -0500 commit: octo1 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo2 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo2 new file mode 100644 index 000000000..5392a4f86 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo2 @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351874960 -0500 branch: Created from HEAD +977c696519c5a3004c5f1d15d60c89dbeb8f235f 158dc7bedb202f5b26502bf3574faa7f4238d56c Edward Thomson 1351874974 -0500 commit: octo2 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo3 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo3 new file mode 100644 index 000000000..7db5617c8 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo3 @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351874980 -0500 branch: Created from HEAD +977c696519c5a3004c5f1d15d60c89dbeb8f235f 50ce7d7d01217679e26c55939eef119e0c93e272 Edward Thomson 1351874998 -0500 commit: octo3 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo4 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo4 new file mode 100644 index 000000000..b0f9e42ef --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo4 @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875010 -0500 branch: Created from HEAD +977c696519c5a3004c5f1d15d60c89dbeb8f235f 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson 1351875023 -0500 commit: octo4 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo5 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo5 new file mode 100644 index 000000000..614563edf --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo5 @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875031 -0500 branch: Created from HEAD +977c696519c5a3004c5f1d15d60c89dbeb8f235f e4f618a2c3ed0669308735727df5ebf2447f022f Edward Thomson 1351875041 -0500 commit: octo5 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo6 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo6 new file mode 100644 index 000000000..4c812eacc --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo6 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson 1351875046 -0500 branch: Created from HEAD +977c696519c5a3004c5f1d15d60c89dbeb8f235f 4ca408a8c88655f7586a1b580be6fad138121e98 Edward Thomson 1351875057 -0500 commit: octo5 +4ca408a8c88655f7586a1b580be6fad138121e98 b6f610aef53bd343e6c96227de874c66f00ee8e8 Edward Thomson 1351875065 -0500 commit (amend): octo6 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames1 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames1 new file mode 100644 index 000000000..58a7e0565 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames1 @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1353177745 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 412b32fb66137366147f1801ecc962452757d48a Edward Thomson 1353177886 -0600 commit: renames diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames2 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames2 new file mode 100644 index 000000000..5645ecee7 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames2 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson 1353794647 -0600 branch: Created from HEAD +bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1353794677 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec ab40af3cb8a3ed2e2843e96d9aa7871336b94573 Edward Thomson 1353794852 -0600 commit: renames2 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10 new file mode 100644 index 000000000..b6bd247e7 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352100171 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson 1352100193 -0600 commit: trivial-10 +53825f41ac8d640612f9423a2f03a69f3d96809a 0ec5f433959cd46177f745903353efb5be08d151 Edward Thomson 1352100223 -0600 commit: trivial-10 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch new file mode 100644 index 000000000..14ce9e545 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson 1352100200 -0600 branch: Created from HEAD +53825f41ac8d640612f9423a2f03a69f3d96809a 11f4f3c08b737f5fd896cbefa1425ee63b21b2fa Edward Thomson 1352100211 -0600 commit: trivial-10-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11 new file mode 100644 index 000000000..3e6b77437 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352100930 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson 1352100958 -0600 commit: trivial-11 +35632e43612c06a3ea924bfbacd48333da874c29 3168dca1a561889b045a6441909f4c56145e666d Edward Thomson 1352100992 -0600 commit: trivial-11 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch new file mode 100644 index 000000000..30d5ec7a3 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson 1352100964 -0600 branch: Created from HEAD +35632e43612c06a3ea924bfbacd48333da874c29 6718a45909532d1fcf5600d0877f7fe7e78f0b86 Edward Thomson 1352100978 -0600 commit: trivial-11-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13 new file mode 100644 index 000000000..3a7302dea --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352100559 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson 1352100589 -0600 commit: trivial-13 +8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa a3fabece9eb8748da810e1e08266fef9b7136ad4 Edward Thomson 1352100625 -0600 commit: trivial-13 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch new file mode 100644 index 000000000..bb2604244 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson 1352100604 -0600 branch: Created from HEAD +8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c Edward Thomson 1352100610 -0600 commit: trivial-13-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14 new file mode 100644 index 000000000..4b70d2898 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352101083 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 596803b523203a4851c824c07366906f8353f4ad Edward Thomson 1352101113 -0600 commit: trivial-14 +596803b523203a4851c824c07366906f8353f4ad 7e2d058d5fedf8329db44db4fac610d6b1a89159 Edward Thomson 1352101141 -0600 commit: trivial-14 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch new file mode 100644 index 000000000..8e491ca68 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 596803b523203a4851c824c07366906f8353f4ad Edward Thomson 1352101117 -0600 branch: Created from HEAD +596803b523203a4851c824c07366906f8353f4ad 8187117062b750eed4f93fd7e899f17b52ce554d Edward Thomson 1352101132 -0600 commit: trivial-14-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt new file mode 100644 index 000000000..a2a28d401 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352091695 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson 1352092452 -0600 commit: 2alt diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch new file mode 100644 index 000000000..a0a48ae35 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352092411 -0600 branch: Created from HEAD +c607fc30883e335def28cd686b51f6cfa02b06ec c9174cef549ec94ecbc43ef03cdc775b4950becb Edward Thomson 1352092434 -0600 commit: 2alt-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt new file mode 100644 index 000000000..4374d3888 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt @@ -0,0 +1,3 @@ +566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352094547 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 5459c89aa0026d543ce8343bd89871bce543f9c2 Edward Thomson 1352094580 -0600 commit: 3alt +5459c89aa0026d543ce8343bd89871bce543f9c2 4c9fac0707f8d4195037ae5a681aa48626491541 Edward Thomson 1352094610 -0600 commit: 3alt-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch new file mode 100644 index 000000000..7a2e6f822 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352094594 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4 new file mode 100644 index 000000000..3ee6d2503 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4 @@ -0,0 +1,2 @@ +566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352094764 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson 1352094815 -0600 commit: trivial-4 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch new file mode 100644 index 000000000..51f8a9290 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352094830 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 183310e30fb1499af8c619108ffea4d300b5e778 Edward Thomson 1352094856 -0600 commit: trivial-4-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1 new file mode 100644 index 000000000..14497029a --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1 @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352096606 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson 1352096643 -0600 commit: 5alt-1 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch new file mode 100644 index 000000000..4cff83526 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352096657 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 Edward Thomson 1352096689 -0600 commit: 5alt-1-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2 new file mode 100644 index 000000000..3ca077b29 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352096711 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson 1352096764 -0600 commit: existing file +ebc09d0137cfb0c26697aed0109fb943ad906f3f 3b47b031b3e55ae11e14a05260b1c3ffd6838d55 Edward Thomson 1352096815 -0600 commit: 5alt-2 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch new file mode 100644 index 000000000..e7bb901f2 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson 1352096833 -0600 branch: Created from ebc09d0 +ebc09d0137cfb0c26697aed0109fb943ad906f3f f48097eb340dc5a7cae55aabcf1faf4548aa821f Edward Thomson 1352096855 -0600 commit: 5alt-2-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6 new file mode 100644 index 000000000..7c717a210 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352097371 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson 1352097389 -0600 commit: 6 +f7c332bd4d4d4b777366cae4d24d1687477576bf 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson 1352097404 -0600 commit: trivial-6 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch new file mode 100644 index 000000000..715f3ae1c --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson 1352097414 -0600 branch: Created from f7c332bd4d4d4b777366cae4d24d1687477576bf +f7c332bd4d4d4b777366cae4d24d1687477576bf a43150a738849c59376cf30bb2a68348a83c8f48 Edward Thomson 1352097431 -0600 commit: 6-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7 new file mode 100644 index 000000000..a014f1722 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352099765 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson 1352099790 -0600 commit: trivial-7 +092ce8682d7f3a2a3a769a6daca58950168ba5c4 d874671ef5b20184836cb983bb273e5280384d0b Edward Thomson 1352099947 -0600 commit: trivial-7 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch new file mode 100644 index 000000000..22331d78c --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch @@ -0,0 +1,5 @@ +0000000000000000000000000000000000000000 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson 1352099799 -0600 branch: Created from HEAD +092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson 1352099812 -0600 commit: trivial-7-branch +73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson 1352099874 -0600 reset: moving to 092ce8682d7f3a2a3a769a6daca58950168ba5c4 +092ce8682d7f3a2a3a769a6daca58950168ba5c4 009b9cab6fdac02915a88ecd078b7a792ed802d8 Edward Thomson 1352099921 -0600 commit: removed in 7 +009b9cab6fdac02915a88ecd078b7a792ed802d8 5195a1b480f66691b667f10a9e41e70115a78351 Edward Thomson 1352099927 -0600 commit (amend): trivial-7-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8 new file mode 100644 index 000000000..7670c3506 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352098816 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson 1352098884 -0600 commit: trivial-8 +75a811bf6bc57694adb3fe604786f3a4efd1cd1b 3575826c96a975031d2c14368529cc5c4353a8fd Edward Thomson 1352099000 -0600 commit: trivial-8 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch new file mode 100644 index 000000000..c4d68edcf --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson 1352098947 -0600 branch: Created from HEAD +75a811bf6bc57694adb3fe604786f3a4efd1cd1b 52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 Edward Thomson 1352098979 -0600 commit: trivial-8-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9 b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9 new file mode 100644 index 000000000..09a343bdb --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson 1352100268 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec +c607fc30883e335def28cd686b51f6cfa02b06ec f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson 1352100304 -0600 commit: trivial-9 +f0053b8060bb3f0be5cbcc3147a07ece26bf097e c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a Edward Thomson 1352100333 -0600 commit: trivial-9 diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch new file mode 100644 index 000000000..1b126fb7b --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson 1352100310 -0600 branch: Created from HEAD +f0053b8060bb3f0be5cbcc3147a07ece26bf097e 13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e Edward Thomson 1352100317 -0600 commit: trivial-9-branch diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/unrelated b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/unrelated new file mode 100644 index 000000000..a83ffc26a --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/unrelated @@ -0,0 +1 @@ +d6cf6c7741b3316826af1314042550c97ded1d50 55b4e4687e7a0d9ca367016ed930f385d4022e6f Edward Thomson 1358997664 -0600 commit: conflicting changes diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b b/tests-clar/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b new file mode 100644 index 0000000000000000000000000000000000000000..82a8da597f013237fc97a4940452778fd54fd7ac GIT binary patch literal 168 zcmV;Z09XHb0hNwB4#F@HM5%oWUjVf0-#QXPG~9q5`LkA1I7W#P*C(J{fNtiE^fal} zI(4axKJ=@j`Xe(xjfb>Gl~x~dQMW-^iD7U literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8 b/tests-clar/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8 new file mode 100644 index 0000000000000000000000000000000000000000..f663a3c5146f02f7aeabdee6732abab596d9cbee GIT binary patch literal 164 zcmV;V09*ff0i}-14Z<)GL^->PZ2)9D{;h-%7doJU&3dEc;20SL?Gq3kaGQCf84cG` zmJZg%U2h^FCK8KrXoE|PnSIn|r0m&6nlPI&Xm*0?MorcZ8ZAo-$>ul>WNDxnkU}OV zpU`U)(nwob(WB3`!6m<_Ww_3@-0KQQ+2}`|Y S8P%lJr({@Gi0Tb@V^GR)1x=s; literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08 b/tests-clar/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08 new file mode 100644 index 0000000000000000000000000000000000000000..72698dc3df0cac29445308ad42e04b1954cab618 GIT binary patch literal 147 zcmV;E0Brww0V^p=O;s>7wqP(Z00M=i{E`gal+2=H22-BA&5V419IUe6W+)pa2aCLX z8)0Z*U}j>XkdhXkn39s3!k{i}yPRp4O~QID#+Ny&q5mCRuT6rf$t+@M-!dsF;d+&I zt6brmb8OACuLW!}gjkc7nUktlQc=Q?zi;FeH-sWH BMcn`Z literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0 b/tests-clar/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0 new file mode 100644 index 0000000000000000000000000000000000000000..aa6336d3fac865556fb2c7e9f84e69a304fb13b0 GIT binary patch literal 166 zcmV;X09pTd0i}*f4gw(%06p`H{(uIWr6DoKi$CxKEKOz(42Hq@eaAO=tJI-Vxwp1X zK!c|_n4xAWl)SW+Vle0}nQE$+n2@!}1!YX3gdkpV@HQcFb*w3AW~VVlGRg+!yw|R3 zkCh9mLR3V2&!rD|lusN5o=b0g-{8uJ{n5RxdGx*4dC9%qKxt=58Lt(brIk_~_86V~ UM?xIOu`$Fg^FI3!Uu`Z<;^BBtW&i*H literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858 b/tests-clar/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858 new file mode 100644 index 0000000000000000000000000000000000000000..2f0a0e1bb139f469d893b97328c111886fdc93d1 GIT binary patch literal 168 zcmV;Z09XHb0hNwH3IZ_@1U=^!`G7K+WOg90$m)T^39(E%!;`d#}4|waYhN4rQ z=UcZJ{H8Zmn_!SQDUCr=h$4WAh@5wrqhcH}MVE^MbC;$|w>cniVO|9UFlq9hWD3kB zB63N2a*FK4$g^K-v#0!$mhJgk=SN-aURU}_NBs`Jwo7$BSm!Aa6T)D(FaVg9u4lXc Wts!vC6n+|$%jI#)islV~F;1nsUQS^E literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001 b/tests-clar/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001 new file mode 100644 index 0000000000000000000000000000000000000000..d623117c5451f527ebff27812643d7856fbe5a0a GIT binary patch literal 264 zcmV+j0r&oR0V^p=O;s>9H()R{FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRUTQg- zLz>xdT8Tz7FFi7~EW)1W;kW;tHGELTMX9;@Wf(5M|M-*kD*60#A?_1o4BB=*&bXI- z7gGb0%VVrFmdhXc6>Fw{xjimexF#!V`%9H)b$2FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRZhlH; zS|-#r`6Upy{0m|K6R_0k%@$dyym`yF2|aCG-wst@l$x7ghT-`8k3VUzlFvUE;yyvf zpl#RVjC4GGj0_FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nPt zDo)BT$o((3Q3>52inSC)Oh*VHo652`3RBQY->;)JB4#JuDTu&Jwa zTo%1>nY7m1ad-Bbc2m2#Ol!I^H6XcJ@ZM^MO_!H_UCeITv4)jLw>mBK5llmVURq9O za!F=hI@oN#j=Tg_%~LB^3tpFROZL9F?ZG_(69XVn0J@do>N06=&q+%@oLbTTi({*b z;dAxf`A~z>GNFEMZ z`ve(-wq1`i?q%PFYA6MI85{y&x1IQquV=k!nqx_N-NX;8UtM@&GcOhZ40*2>4RNg| literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03 b/tests-clar/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03 new file mode 100644 index 0000000000000000000000000000000000000000..04011a2ceb576207e2311a43982bbd4414831946 GIT binary patch literal 264 zcmV+j0r&oR0V^p=O;s>9vt%$dFfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRQEF~} z8N@LbV6{`{K8p)$DiPwpKhNst_iE7qAw^G2wMY)X|M-*kD*60#A?_1o4BB=*&bXI- z7gGb0!(*&7mdhXc6>Fw{xjimexF#!V`%z>-JwUU=aE literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47 b/tests-clar/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47 new file mode 100644 index 000000000..65fa6894f --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47 @@ -0,0 +1 @@ +x+)JMU06`040031QH,-M-JOMLI+(aH:,:C: o>ZC'g$楧f&%%g5qYeZokM2ԐX\ZDPC~^ZNfrIf^:XZHي1O(_,' jvn~JfZ&5&ؽ +gz43^2 I{| 2mg˾15ӿ,\})TC)0Xvz֛9MՅ'6b# \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe b/tests-clar/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe new file mode 100644 index 0000000000000000000000000000000000000000..d79dc30ba7155121f8e844b28d20c391df897375 GIT binary patch literal 63 zcmV-F0Korv0ZYosPf{>4F=r^r$ShV!%gjkt0Md!2CHc9jMd_)DNja%p!$&GPBQY;M VHANvaPa&x&F)ulT3jm+qT_Q;S7;yjq literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c b/tests-clar/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c new file mode 100644 index 0000000000000000000000000000000000000000..7b4b152f34b81cc4b40ed01324ac1946c396b058 GIT binary patch literal 170 zcmV;b09F5Z0i}*nYQr!PMDwjv=mHK}X;)UDg#P3PxxiYx!iLx?irQX3PRI@N_vQ@@ zlk2)J-Fote-c&6M6ETtik{=}pDR~4`h8#(}g2pmw8qE(k-MUQ$F@%!n6htJ_G>YJk zND(C=6qHj=%!YsaT${bfKR2=0xvuxR*)QGglfLy;ywfq)^=u)K2j?OxVO@x8-l)+W=vh8gF?v4}39G9kOy#6aWAK literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f b/tests-clar/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f new file mode 100644 index 0000000000000000000000000000000000000000..a34b6c23561a462aec7a79e840174db595fd093c GIT binary patch literal 165 zcmV;W09yZe0i}-1jRG+cL^(T(P5{()`zx&w7cw9LPrI$*V2r#5(q~w02HdLNlS;+C zwd(|9!)^`{5YiHIAt@%sYOGKsw#i|&KIoK47(*1R?s>><0(%UV31cK7az(0%TzvG4 z)75j$XcqLsDnI7h2b{|*j{=vqx8Ht)AKB<9o#apaw*&Wf1Wit-n9y3-qeE01-Of(= Taz-tEt}vwarxf)DDF;yiw$w}5 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e b/tests-clar/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e new file mode 100644 index 0000000000000000000000000000000000000000..23ab9217148566bcba07573afa4c376fd0f80c40 GIT binary patch literal 32 ocmbS$%Kcz!PVJJx=MLe89wz^nv|30NO4M_W%F@ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515 b/tests-clar/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515 new file mode 100644 index 0000000000000000000000000000000000000000..bf5b0fcc57ff618efc5784f508aee1634073bce2 GIT binary patch literal 41 zcmV+^0M`F_0ZYosPf{>4X9&s2ELKR%%t=)M(s`-n3YmEd`N<{uMtWQT%DoF6bTtwL literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc b/tests-clar/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc new file mode 100644 index 0000000000000000000000000000000000000000..9fb640dd5c947abdbcfa0a83f0d421d755fc5d1a GIT binary patch literal 36 ucmV+<0Nej~0ZYosPf{>4VkpVTEan2Dl*}Uiw9K4TL%ouU5-tFg0turl1P{3Y literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4 b/tests-clar/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4 new file mode 100644 index 0000000000000000000000000000000000000000..b709cf461bec50d06c7e92a4d9dc386fbe054808 GIT binary patch literal 163 zcmV;U09^lg0i}-74FVw$ME#};8aT7S-2#a*{@8&HEXxuv@x&l%`<})Q{F`}`ybRZ6 znY*^*xamzqm*ia_1IQ&q^fVHuh%Clg;bbTykC?!z8#TFh%?Q4*gAklJH)Sa{aK?aX zGG&zz*aRr7=+V!$>0^FT%ldS#%e`LpPFDI!2l)=aw&l9)wQ-Y$7<~ji00O9$u4lXa RsZqVn&zUZRS8o8yP?i&^P>KKm literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1 b/tests-clar/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1 new file mode 100644 index 0000000000000000000000000000000000000000..ae13207d7490a064ecf276149d49ccc8ddad31f4 GIT binary patch literal 266 zcmV+l0rmcP0V^p=O;s>9H)k+3FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRQEF~} z8N@M$2FUI?HtEeb1@;4L=5NZ=TG4V()A*j~TTDGj9=QMbllCh4{Bt4h6J!k9c0JCx zmwgvg1Cj?~tTUF&ANdt)rhmCTE?BrGD{A{vsD@HtsDJ|r?EDiS^7X7YO>-9H)b$2FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRZhlH; zS|-pnCHcC=iMbHR$KK_CmOqpCz|-^d-{j7A3vzfFTn|-Wl$x7ghT-`8k3VUzlFvUE z;yyvfpl#RVjC9H)b$2FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRZhlH; zS|-pnCHcC=iMbHRdwbk_YvpXox&PX>)wbRfO}ewbq(IddrRL_BVL1N&<4@YF4dj#_=z*Y27YHWA2KmGY-VEu0Hgp4jsO4v literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d b/tests-clar/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d new file mode 100644 index 0000000000000000000000000000000000000000..4b633e5043d1b0ba3cbb9ac4b0223b2a93342f38 GIT binary patch literal 235 zcmV9GG#C{FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRQEF~} z8HQu-KmMe>NBDf3F=Qyo$ShVU&&>GO3h2mO-)hA%u~oOEh^?p%u7*7 vP01{Q2$f`{W&#DdG&1v2Qd3iO6f#Q`fSS=2XmW9pVd$t95~BeC!$_Pzca$Uz literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151 b/tests-clar/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151 new file mode 100644 index 0000000000000000000000000000000000000000..1bee56c14b889c8933ce73c9797c99532081c84a GIT binary patch literal 165 zcmV;W09yZe0i}-74FVw$ME#};8qh4ez(Hb+KXzaP%dx~uyu%=B`<})Q{F`|%nao({ zdFmRi+w>-)AySf5V8EPhN{K@VMBWeCmm=2LTuh8&)Qy`gT{{ZsiZwij>@9eMC0ZoL z0>nNR%FzditoYF{wdqrSam)IAsq?)qdM_*eq@#SrukFS3X)y TpEIhr={fOrYe2mLSIb)7unqPA literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa b/tests-clar/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa new file mode 100644 index 000000000..6555194cb --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa @@ -0,0 +1 @@ +xQ D\fw)c^` ۴-Q/ơdb^ץjEDC$u> , z@8qjk<٩G>z2Lva2)Veŏ:%˜{A|Ǽ5K@mg9jY _;n,YyP \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e b/tests-clar/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e new file mode 100644 index 0000000000000000000000000000000000000000..4e4e175e8164e9fdc33ed173a3089ea2c7e75be1 GIT binary patch literal 168 zcmV;Z09XHb0i}-J4Z<)GgL%#rS)k}HY5pXHcwqw;xJ$2UCEB16V0{W=1K#Y
  • iU zWp3Iy+_fsAVQ@w)Ip+mqVuI9166ZsNgb@%kGWsy68&z4G9s!)?2p*X22rMph&KCPX z5G0H7JVFYh=+UN9^?7(v9rR@?%e}1nARGOtlYE1p+vl3VJOMSEan2Dl*}Uiw9K4TL%ouU5-yG8jKsY3)D(rxJcX3B Q_~OizR6|WJ0Kk(F;!lblxc~qF literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c b/tests-clar/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c new file mode 100644 index 000000000..064423d0c --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c @@ -0,0 +1,2 @@ +xK!D]sCboi2. bK*Eep73UӾ*NYYIԔ)jL:8<{NޓH6iDC"mqH!9Tm9>R^i.= +G'+~@@j+7أENsFt]7bN) \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061 b/tests-clar/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061 new file mode 100644 index 000000000..82d65253b --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061 @@ -0,0 +1 @@ +xK!D]sObo hJqo6AJـT1h3'Lՠ.{ec,a`ZJT1#e+هJUi">\+ ץG_X6IvN;^bYgGMM \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982 b/tests-clar/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982 new file mode 100644 index 0000000000000000000000000000000000000000..94e571e6547c9f6826e73200aeb24b334dc6b5a5 GIT binary patch literal 65 zcmV-H0KWft0ZYosPf{>8FlQ*q$ShV!%gjktD9_BvQAkQvC`!#s%uP+<%FI(p$}h=K XNGeLqOU_6w=HePv3l0YWsc3D@sF55# literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778 b/tests-clar/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778 new file mode 100644 index 0000000000000000000000000000000000000000..1c4010d0479c8f28d62969968746d6c346597c5c GIT binary patch literal 170 zcmV;b09F5Z0i}*nYQ!)MMEmVi_yQ?%6e|vtWq*2uy?`vqB+$gg>us;!Z0Qa9H}f70 zBX!@m?#CEjdNcE!(yWRLE1D_=D6&98l}y1V;2eQw)Zjk3+0nfa%ta7VLPLydWlW-! zD92Dp3d5LzOdLP@TAP2@Z*J;uYu)el@Nag~XS&+2_-n6H_lpl0M_8s5$qzsP?xg40 YZvUKdy=`9`e+<)*8y~W|ANj&k)P+J+s{jB1 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9 b/tests-clar/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9 new file mode 100644 index 0000000000000000000000000000000000000000..30f3110f122695066575e549207def7ff17a06ca GIT binary patch literal 68 zcmV-K0K5Nq0ZYosPf{>8GiE5s$ShVU&&>GO3h2mO-)hA%u~oOEh^?p%u7*7 aNzF+ufryo4q-Fv|xkmMn6$Suk#)28*xEz!K literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1 b/tests-clar/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1 new file mode 100644 index 0000000000000000000000000000000000000000..e34ccb855c0976b4a5f87a375b8af037a39364b7 GIT binary patch literal 53 zcmV-50LuS(0ZYosPf{>3W(Z2n%`Zz$QOL|wP&ZdsNGdH+$jwj5Ov{9c=_VB=<|SvS L>u~`9q0|r1O8^$N literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b b/tests-clar/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b new file mode 100644 index 0000000000000000000000000000000000000000..30802bcecd777a4452e76f2739946e26dfe9e50a GIT binary patch literal 33 pcmb9GG;I|FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nPt zDo)Nw%u9!uo>Y{Wmz)9CusX+O(F>PJYrP$JXRm2DwVTVdrW;cOl5+&_t!CJCdD+*+ z?3NvCSb21-(?TD?H00-{tM4hkQNjP177p((5LESpDk4 M6PtOl0L5v7+NnT@tpET3 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513 b/tests-clar/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513 new file mode 100644 index 0000000000000000000000000000000000000000..a843890c07c438afbf35caec02aa95cb7ed8e0bf GIT binary patch literal 271 zcmV+q0r38K0V^p=O;s>9w_q?dFfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRZhlH; zS|-Fbh9<}kQWLeZ|C};AM|)e)gf0V^p=O;s>AVK6i>Ff%bxNXyJgHPkDqC}9w>f^`v?#EoR&lyh^e#{@%Ki-H+b=~r`cK9q$OdT=Amb4 zI7AhRQQ6|k1TmDowh)aFv52xGJ)>tva{@OO-j*2g#va!7L?mJgZg5!K5&nhe+pk}` I0OLKb{)H1UA^-pY literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487 b/tests-clar/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487 new file mode 100644 index 0000000000000000000000000000000000000000..d0c8c9e1df90d8ef56a1aefeeed35636eed062cb GIT binary patch literal 43 zcmb literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b b/tests-clar/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b new file mode 100644 index 0000000000000000000000000000000000000000..86127a344bcd2dc01885a87ad9e6aa346a11d7c4 GIT binary patch literal 142 zcmV;90CE3#0i}(>4FVw$0DV)%HlSHK5J-&iV+S_CElchaVH0kR?R(bXZ!(V=lGauQ zi_<{|GgJyC%n^MJIXN4BaMjg_cD6h@Di-scN=hcbb&&y&^2wvXbCI@5hb!;)NB6eU wkWH$dNZJj~)5V9gOFqI8J)+s|K34uCVFvfy7)W}3t)!4U@uo7&7i`-^t=uR3FlQ*q$ShVU&&>GO3h2mO-)hA%u^`INX;xN=1R;< ZQAkb6EP)8-mjZ>jMzzpB!~oPvkr~kB9dG~u literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d b/tests-clar/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d new file mode 100644 index 0000000000000000000000000000000000000000..74a01373f44bdc87acb162136fd0dbe4527f9b49 GIT binary patch literal 30 mcmbxmhoiK()PmVkaVRdHKV~G30t^@$Pd<$;? literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74 b/tests-clar/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74 new file mode 100644 index 0000000000000000000000000000000000000000..60497caa56d0a276b89635791f80b13b00d2f717 GIT binary patch literal 268 zcmV+n0rUQN0V^p=O;s>9GGH(?FfcPQQAjK;$5Ep%1PBLsVHH1XTNaA0j~e` z4-``WZskbX%{y7=JXCRVMq*xiYKm@Vo^Dc6VqS6vSi|ZZmqjmJCav{$+?~Cq-PCR_ z)0%Ef4Y`TMC8e=fv*f{a1iuE!bovhP9-F9muI>{qbKCqCrsS#O%=Sdv~h S@x$s@7oOP6iv<9|ErgF|_<-vG literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3 b/tests-clar/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3 new file mode 100644 index 0000000000000000000000000000000000000000..2bae66998977385146fbe69aeb71f387b4ba800d GIT binary patch literal 381 zcmV-@0fPQ`0V^p=O;s>4Gh{F{FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nOy zF#rOEr2LW$-IUCtVg^&5yv>Y!e;ll`-)1NqB?pVVd>a8ZFgYVJFCF57q@u*UOYd*4uG+_L_E6ySYqjx-m5%Ia%=DYKBdhmwjE#ZrQPhl}EQaE%XsgLw;Ub zPG)jRW?nkjY`>1Y1XaybD_09%mv2k}vs=3?Y6_%Y+6?{=WG;V%n}>bdE3T zT_Umff6y_9l~5CkQgidmFuZ*K@h9z7^7-dN+$YEwwC#GFaWDHWR6{8+SiqqKcK?YF b`Fhryra6|R*G>Ge`qhOeHuGWur60HSw0OLC literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1 b/tests-clar/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1 new file mode 100644 index 0000000000000000000000000000000000000000..185214727f41552c6820f66bf63641d28505021e GIT binary patch literal 31 ncmbL(MnP81`KAxtJm>9ygvkL$KyV?si literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9 b/tests-clar/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9 new file mode 100644 index 0000000000000000000000000000000000000000..4fcaa07e228f0f20883cb11889c7b2bf3af0bc2c GIT binary patch literal 48 zcmV-00MGw;0ZYosPf{>8V(`sR$xO>kO;O0qQ&2A{$}G!F%+WP8(M>8!%uCKt=K=r< Gt_7HfJy~00M=i{E`gal+2=Hh9%O{+b-<0QTm&@!ZK-Ar!bX?Dpyx@zbD6GK&}#)8#aoDm3ypo%2b) zAU1Pb)t$$Nh6V;^CMF7LnK`L?B^4zMU2Es^EjF1dyQDfv%01fhOy2d#{QwO{Hryq0 BNbLXs literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136 b/tests-clar/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136 new file mode 100644 index 0000000000000000000000000000000000000000..d24231eda0f690b57f01446f58ae5499ab63533d GIT binary patch literal 53 zcmV-50LuS(0ZYosPf{>3Heo2q$ShVU&&>GO3h2mO-)hA%u^`INX;xN<{FhA Lbio1u#AcpFSWp&g literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 b/tests-clar/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 new file mode 100644 index 0000000000000000000000000000000000000000..d10ca636b6bf5f66bce633362d871d17d9a292c6 GIT binary patch literal 56 zcmV-80LTA$0ZYosPf{>3VkpVTELKR%%t=)M(#aW#dFiPs3YmEdNkxfy$r%cXc_|9H OiNz(UMO*-@y%7?c&=$P_ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f b/tests-clar/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f new file mode 100644 index 000000000..83253f81c --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f @@ -0,0 +1,2 @@ +xK +1D]N"n{t:L$ UEQ>~7:L D [5ɇ,y2eT@z*.([žunum3VhBpj%`Zz$QOL|wP`A`gDoV^t&QMoKDlJjS%}>cp%Y;d( Ka{&OEIuE5qZx!?a literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c b/tests-clar/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c new file mode 100644 index 0000000000000000000000000000000000000000..0100fd70e832555800236cc897868c9a56402c62 GIT binary patch literal 179 zcmV;k08IaQ0iBLZYQr!PhP&1&bb*Gk9y1bzkWFvUz0qTY4V4vSgi~AMo*| zR_i*Xvh*=Jv-C{GXzPtDL_|8cfUYV{^3jR49TAo&RnKpg8K hJNONZLp1iph{HToo>SY(&zi_|Št@ +apg%haJYծA8թ훠fN4;h[%cOuJWyΏ \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16 b/tests-clar/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16 new file mode 100644 index 000000000..1d9f226e2 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16 @@ -0,0 +1 @@ +x+)JMU067c040031QH,-M-JOMLI+(aH:,:C: o>ZC'g$楧f&%%g5qYeZokM2ԐX\ZDPC~^ZNfrIf^:XZHي1O(_,' jvQjn~13zדm9Wu]:$I{| 2mg˾15ӿ,\})TC)0Pavz֛9MՅ'6b \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d b/tests-clar/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d new file mode 100644 index 000000000..2de1c5a79 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d @@ -0,0 +1,2 @@ +xQ +0D)rJMxMHz}xfރaRYipkUD $1fQ2q-=Y3R76ġg9e7 bw GJe*˽ |ůSY"5&Нƨng9Z3_;kdO \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6 b/tests-clar/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6 new file mode 100644 index 0000000000000000000000000000000000000000..5ec5acb596853e163ca7c0de2dddb36f8ed17446 GIT binary patch literal 41 zcmV+^0M`F_0ZYosPf{>4X9&s2ELKR%%t=)M(s`-n3YmEd`N<{u#(G=;%D@XAcs~+| literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b b/tests-clar/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b new file mode 100644 index 0000000000000000000000000000000000000000..d36138d796c849f28fc5131ef23a56a58b747c01 GIT binary patch literal 47 zcmbSr<#>h{|9Gnb DkgXG9 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475 b/tests-clar/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475 new file mode 100644 index 0000000000000000000000000000000000000000..11546cea449c383f0c292d17e7e773de4c722ed3 GIT binary patch literal 33 pcmby>隿ïm6*Rn>O \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05 b/tests-clar/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05 new file mode 100644 index 000000000..c653cec50 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05 @@ -0,0 +1 @@ +xKj1D) >`7A. $<`Morlm4G&dVd[j2JCъgu_Gu%2:3XزQ'";?wpkm׾&Pf! %QJ%:Cez=6q;iO \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58 b/tests-clar/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58 new file mode 100644 index 0000000000000000000000000000000000000000..2eee602335c69d4c9f13f8a8b4e3e20096b21190 GIT binary patch literal 92 zcmV-i0HgnS0V^p=O;xZoWH2-^Ff%bxNK8pdP0`KF(@n}R$LJ2x0gUIYNNCnV+hk|<>W literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241 b/tests-clar/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241 new file mode 100644 index 0000000000000000000000000000000000000000..ea024ccd9e3dce798de1762fcababd72d55f9e43 GIT binary patch literal 31 ncmbL(MnP81`KAxsem>9ygvhxD~yWtBo literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4 b/tests-clar/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4 new file mode 100644 index 0000000000000000000000000000000000000000..1dd13c44a00f1c388d5b23fa12cdb68a82fde9c7 GIT binary patch literal 41 xcmb5!L_z literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29 b/tests-clar/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29 new file mode 100644 index 000000000..be7684f19 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29 @@ -0,0 +1 @@ +xN !LdMb60^,40;iUFf+)1vB939fG(DIݸʵA$sk]l|L{Ig$m.N5y.\a/]|Ʋ@[g4< Hl?gTsˠzCP \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd b/tests-clar/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd new file mode 100644 index 0000000000000000000000000000000000000000..24e33bc41cfc3d41d574d366c4311ffd44d005a9 GIT binary patch literal 163 zcmV;U09^lg0i}*z3c@fDgniB_a)HWj^A94vcmpr6Nj9`#8;Mb`ZxwIg+syaDFsUxf z+~uhCt~X{0*^I2n7|ZBGBna9Q8|yp-^njQ!qIFr^sPWpRGvpCtu`wBEgQgr+VGG)M zTNG(78B@k6=+URzxtJKOos R8PVJPoas7v@dn@TPdO==P_O_1 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495 b/tests-clar/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495 new file mode 100644 index 0000000000000000000000000000000000000000..7f8044372ad4d15a6ed522b15fa7c3fdcb6f01fd GIT binary patch literal 68 zcmV-K0K5Nq0ZYosPf{>3G-W8s$ShVU&&>GO3h2mO-)hA%u~oOEh^?p%u7*7 aNzF+ufryo4q-Fv|xkmNSEers*8I)M#?Hu0# literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3 b/tests-clar/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3 new file mode 100644 index 0000000000000000000000000000000000000000..90fd9651f8c2a1f4e6803607a09b2f47b8ce092b GIT binary patch literal 35 rcmb4X9&s2ELKR%%t=)M(s`-n3YmEd`N<{urg~fe%Ek*IcT*B@ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced b/tests-clar/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced new file mode 100644 index 000000000..e95ff3a88 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced @@ -0,0 +1,2 @@ +x-MK +1 uSYRą6C6뛪oknYt Ep iDCddLB+8%qk +e6fHB1J4F1l \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55 b/tests-clar/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55 new file mode 100644 index 0000000000000000000000000000000000000000..82086466f5eb3142bf86233dbb9d3600ec655264 GIT binary patch literal 161 zcmV;S0ABxi0i}-J4FVw$gngz88aT7S@w16BzSw~cz_R2_yu%>I_C;d{zRi4-%na3a zy>_W*wd;*p0(ut`XRTcT$_au+7fgxhyeFcNZPb>OxW~p@mz)SNgEF2<0@BW*k30Zi zNjRIB5nM4v#Ajb>ljr=3Ez8SN*GJvtfd~Dl6MuuB+f!YSQW>jZtSc)gZ~$V^aklfH PHNwVQpR{-bS`tn#OrcCs literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f b/tests-clar/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f new file mode 100644 index 0000000000000000000000000000000000000000..723a9ae4c919bbd0cad50bb475b5a80764e28dd0 GIT binary patch literal 269 zcmV+o0rLKM0V^p=O;s>9GGH(?FfcPQQAjK;$5Ep%1PBLsVHH1XTNaA0j~e` z4-``WZskbX%{y7=JXCRVMq*xiYKm@Vo^Dc6VqS6vSi|ZZmqjmJCav{$+?~Cq-PCR_ z)0%Ef4Y`TMC802Pi7oB#j- literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b b/tests-clar/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b new file mode 100644 index 0000000000000000000000000000000000000000..3b5998ca61dbf820b46e70619c4143407fa11688 GIT binary patch literal 172 zcmV;d08{^X0i}*j4#FT5gk5tAE}-G3JZNH!8*kvg0p25RBE?V|uP+*J;BMxdWM*Q? zd1+i3b7+-_kILdO4mx7MIC=n!!4asKQ20SQE0i|TNcQxV*2V)RAZ>#t06V=yrQ0)>>!B8IEWq`5sOE%|V2Mf)#~tty7k)pzF`8W@"nt:x}xwUxjum'뫈.9=y 6$@T8&Lhf4Aܻf0B(.K>9S< +z_f}]Z]eO:wzރP.ިaNU6O \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505 b/tests-clar/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505 new file mode 100644 index 0000000000000000000000000000000000000000..ac86823b67ad33129ed2a6874cb5ed85a4318ec0 GIT binary patch literal 37 tcmb9G-oh0FfcPQQAjK;$5Ep%1PBLsVHH1XTNaA0j~e` z4-``WZskbX%{y7=JXCRVMq*xiYKm@Vo^Dc6VqS6vSi|ZZmqjmJCav{$+?~Cq-PCR_ z)0%Ef4Y`TMC8-4F=Qyo$ShV!%gjkt0Md!2CHc9jMd_)DNja%pLrlujKvkhQ QGdD9Qu?U~j0Zl+!Z0_9{>i_@% literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289 b/tests-clar/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289 new file mode 100644 index 0000000000000000000000000000000000000000..c39b53aa8f4435495440249b2c4b1186bce3460f GIT binary patch literal 382 zcmV-^0fGK_0V^p=O;s>4Gh{F{FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nOy zF#rOEr2LW$-IUCtVunN$zIWG6_?NAy)O|lI(qeb4i^*=Nfyo((dFc=rBo!s*C1-#Q zU7h2y=!MIqwcd`qv)8nn+RbHJ(~YSC$;pEERx@n6yzJ{@cFT@6tUS8aX`zo`8uIhf zax#-kGV{{GX8U#IC8%niTDe;Ax_n!*_r+}w?g>DZrliG#!-GLx*mgP7E}Mk)T8uAq zR73wexL%tCQsGnKH|N-zXI~50WC-zdS|&7D^7qZ(5z}`4qH}yv z?-Gf<|AUS>tc03Sl$x7ghT-M=k3VUzlFvUE;yyvfpl#RVjC8-%)vqo*v6&YO02yex1P1}fp#T5? literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53 b/tests-clar/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53 new file mode 100644 index 0000000000000000000000000000000000000000..3e5f66e5560744991baf7532f0bb954b9600e11f GIT binary patch literal 522 zcmV+l0`>iP0V^p=O;xZkGh#3_FfcPQQ7}l<&CAzK&PdElPt_}_C}GGrcEX!eQtkLn zu4T(-Yaia7b?0m!Ol^{GQEGWnW=TnEo^EEIZhmP|F<8%sbJ~5LZ5+O{4f?mMb3PF{ zWViJhOiwbZo|25zOsFpXFd;_vYYjrPVy92ss(4zic+p!!s4l}qpsu{c+|(49J-VqW znI#Y|k`ae5=)Lb$IDB|J>J^4Jn-w? z*#6lrp$ZGlpn8muZB5EA$pC8*?{wG_@X2Cn4sYr9sWDrtEhHtIpc;%Z11BXlClxbv zBu*P<%e-IkW7(8^J_V^hsr;6xoiH=7`4!0&xM$52XM36zx}Da`5)IwD^7WMqMlDmJ zW|&~QBQYffBaW2HCvDYQ#jV`^is4ONz-*=@d$Asv3D{f#Hv{g58CP}L4{X@|X-lfd zw^czxQxBaEy$vq40teuYU*+4H4Vn(VGTYwN6`||n+@TTx)ntaM zsW>wwRo4)#X^Xnh`Iob#0=TsT|IP5@?kZ>6C5T58#HMZYUzk5RcmB%BB|LYQAGC05 MYroG904*0qfHpz+zW@LL literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 b/tests-clar/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 new file mode 100644 index 0000000000000000000000000000000000000000..d9e250e6664438ecd41046a769dab963dd08e2df GIT binary patch literal 165 zcmV;W09yZe0i}*n4#FT1ME&LzT%cxwvO;2vKiOoDGt{LHw*+B?SobySt4V*CG zU6`fRD literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742 b/tests-clar/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742 new file mode 100644 index 0000000000000000000000000000000000000000..e2c49f5c45ebcceed1bcfdd595a2c5be008a778f GIT binary patch literal 37 tcmbtR22&{sO^Tl}?;T=y^QaQ46y9M}m0fEje0 V?fTCdgL3QB(&WgSc>?+*PaNcdRCE9U literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98 b/tests-clar/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98 new file mode 100644 index 0000000000000000000000000000000000000000..15cb7f29af812ea83abf3da21b8d664ebc3053db GIT binary patch literal 159 zcmV;Q0AT-k0i}*h4gw(%L|t(zG%FGyH~H0N~Koo zZAfikH#+kKf=ERLaAY)i2cTVMF40gym90gaiZkLKI&VXgbIFoLMG3-FQ6X z$Fy~x-**>0-t3#1t1WXAR?a>}45Da~OOob2#H2}USHVuxN7-!I9mbxB6LRZHP(X?d zKQaePq{&PTDnz{MIcbw>S_!CgrN}_FdzR2kksin=<=8HIhGwFofP@ky8^y-sxNO!D zvq8(=NUryApqQ)g(V8E&F!obW^$e=45jc$he6gu~?#Fd=&-zt1b+5;DKCO#;u)+SI zC;M&uRrhV3A02weBn%nd9WcN&*b&}t{~R;D*!DJ}-^$XCc5;Wc?JFK`&wwt#A6!}V MOoqes4Yh%VE1n{S9RL6T literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 b/tests-clar/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 new file mode 100644 index 0000000000000000000000000000000000000000..53168a038b77edb9bb0073f155cf7e2315c07f59 GIT binary patch literal 56 zcmV-80LTA$0ZYosPf{>3VkpVTELKR%%t=)M(#aW#dFiPs3YmEdxrxOksYMEjc_|7> OMTvRI8C(FZ2N4pV?iL9E literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216 b/tests-clar/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216 new file mode 100644 index 0000000000000000000000000000000000000000..f4ec0efecb41f295973d32a8d8714c6003cdd54e GIT binary patch literal 34 qcmbMm*mHjKlI>-x^RwRVL07t|lYWDr+e@8~)?x6Fh+4 J7+ib=ngRRb6OsS` literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272 b/tests-clar/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272 new file mode 100644 index 0000000000000000000000000000000000000000..e2f9f67fdf9f04909cb4b067bb05982887bc686a GIT binary patch literal 159 zcmV;Q0AT-k0i}-74FVw$ME#};8qh2U3v6PHKXzaPuq-)0ILwK$eNSTt&ezPFWHQ`a zyA3@#*o{?0Gv+Lakf{PW=W^L(g{v(T7_ysE3emH=r&X4rDFpW9(GX*@0FJSmVz7AN zNF_^-b+TAhdXBlT`chtLE&4k5_UMZ~%0@r#EZ^dn4&2*G8;d3eM-=rQ5I}8oJ3Hjh N8P$1otKOQ@Y$wPd_em07lC}d8I^8``N~S YV#0cThS//SYe'+~mrh\cQwFMQϙ]b5M R \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a b/tests-clar/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a new file mode 100644 index 0000000000000000000000000000000000000000..08cb0b66fe7341717f2a9cffd560cb5881568657 GIT binary patch literal 164 zcmV;V09*ff0i}-14FWL`Lpi64G_Yv$n@uEyxX=L&t~VkMP6Y}XW|@)>A$V)8QiyZHoJkvmyJ?(_kܖ6-$#-Zzvȟ3 +:NqMB \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd b/tests-clar/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd new file mode 100644 index 0000000000000000000000000000000000000000..dccd220068018e53496f4cd75b5184f91c646594 GIT binary patch literal 58 zcmV-A0LA}!0ZYosPf{>3VJOMSEan2Dl*}Uiw9K4TBfXM}5-yG8jKsY3)D(rxJcX3B Q_~OizR3l9;0Kl>k$^Rb[{=coj'|褯UjeKLnn5СPY|2`zzQ{ \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb b/tests-clar/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb new file mode 100644 index 0000000000000000000000000000000000000000..c7eabc46b2d774774dcc7b7caa89f632b5703db5 GIT binary patch literal 168 zcmV;Z09XHb0hNwP4gw(%MO||WE}(&eVn~c};|<&!h9cdIZVZj_`l9Izxcfu?%TIM# z=FUN@LvKV}8q(XK1u`o;3(ttQ`OpB2rQcL)6w5 z4=^S0rmOTprt;3D^hVy-y-?yGlI( literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29 b/tests-clar/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29 new file mode 100644 index 0000000000000000000000000000000000000000..f6b2a2bfeb4d48d806b494808d911568d1f0596e GIT binary patch literal 71 zcmV-N0J#5n0ZYosPf{>4He@i>w^Yc<%u6j+NGwWK$jnnn&d<%w&*Lh|$ShV!%gh0a d0i{Yy@^e#*(o++Ya#FeIqlLN-0066CQ>C;SA)^2Q literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a b/tests-clar/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a new file mode 100644 index 0000000000000000000000000000000000000000..cf6db633cb14c78d897ce66f18b6c3b8eaf0be8e GIT binary patch literal 160 zcmV;R0AK%j0i}-14Z<)GL^-<(Hvn3%Kgmi6aiIemSlb&V2gk@UqJ09Q18y_#Nux=% zl%;EQaOh1$qo)jGVrO|G1P~G?1{%dV_G~>!%oMG9aFexbKfplj0$R(0G2|(r5N8=o za^yyDkfTgd@w3mh>05qr%lbaovei|eWv8EXm9O}x{njrHCHasl)a0)X1-ezwb> OGiqD1pn3x#AWl##I7qhu literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad b/tests-clar/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad new file mode 100644 index 0000000000000000000000000000000000000000..cbc8cbef31cfa7d4903799d2b4ff665e98991f28 GIT binary patch literal 163 zcmV;U09^lg0i}*z3c@fDgniB_a)HWj{tF_$cmpqxZZ@=F8;Mb`ZxnAJ^D^@d3`1pI zmnN05yH=ScSj;*35ilB~a?I9EN;&K@BY2b1k|*QEEmq!|BnLi|h7dGsZ4+k|D7oOK zKIAAzP!l*N5g%%AflE7H~`UUKil}v R8PV$UT;g>=Bi;=0PayLcMuh+X literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15 b/tests-clar/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15 new file mode 100644 index 0000000000000000000000000000000000000000..7b41413dad72f58fe84603614b4841980a210781 GIT binary patch literal 37 tcmb4V<^eUELH%bqSV~{veXoX%shqM#Nv|FA}#>O3=5lsKNCg( literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d b/tests-clar/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d new file mode 100644 index 0000000000000000000000000000000000000000..7500b99146bf4f8687cc39ff009bb767740ab83e GIT binary patch literal 168 zcmV;Z09XHb0hNwT3c@fDgE2 z#$ZkqH5=4HgjbtNl{b2aO7cFHWh<*Zb5FnO%D-ZFr&N}U)D|2dD1-8-yi%g4{cPjE WF`)hEtllw#@?)EcwfF#MV^6TQs!*%| literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f b/tests-clar/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f new file mode 100644 index 0000000000000000000000000000000000000000..9d8691eb231f4bb873bb27c7ba7f170cac33bda3 GIT binary patch literal 43 zcmbfc@eT%#r#wpmh@cW4 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d b/tests-clar/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d new file mode 100644 index 0000000000000000000000000000000000000000..aca2666cfbbc4bc54b331124e3fd1219af9ba422 GIT binary patch literal 268 zcmV+n0rUQN0V^p=O;s>9GGH(?FfcPQQAjK;$5Ep%1PBLsVHH1XTNaA0j~e` z4-``WZskbX%{y7=JXCRVMq*xiYKm@Vo^Dc6VqS6vSi|ZZmqjmJCav{$+?~Cq-PCR_ z)0%Ef4Y`TMC84Fk>(@FfcPQQAo~6%u7#A(ap@$O)5&vOU}?MsVHGso#V3T zh0CP1-j2Jo*R-43&1G8Cjj16wvA86)h@gi2ytJImJK(OvPIRvu)a;_v-25^O$J~GXNqd!i{<#qM2{Hz4yB=rU%f5@L0m(5j))~v?kNk=? z)4$vv7c5+p6}5dSTti-BF3<*31F%~nod50FWYTb-JMOm9BALkJMZ5bn(Nvn7f>kcB z3R^P2-|CUm19kC_eY=nG#2j@-Q)vk?(^Yeh%+d$dyX0?jIJ4$&jr^G{9|cue3JhOx haD$CJ@gZN&debz=lJvTXA6CD*@Wf_bEC3IeppN(ik30YX literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567 b/tests-clar/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567 new file mode 100644 index 0000000000000000000000000000000000000000..fa63afba1ca8ab9a3ce31f0d78e2a27de4e1a923 GIT binary patch literal 258 zcmV+d0sa1X0V^p=O;s>9GG#C{FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su{!Ffqg$O8`Us{WKQApOGr1%)FCDD(iu!|1 z4=nwQCzLNvmJ{9U231;=nwwvS;h6i6KWVR$&p#L9K0(HyZP(+Bd)ap}H6S@A z#yVrU{E=U=X8M=g8-%)vqo*v6&YO I051iBvbQ&TDgXcg literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb b/tests-clar/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb new file mode 100644 index 0000000000000000000000000000000000000000..e830cafe5eff805763300f2b48d7c9200bfda9e7 GIT binary patch literal 92 zcmV-i0HgnS0V^p=O;xZoWH2-^Ff%bxNK8pdP0`KF(@n}R$y(6_CBq$XC literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6 b/tests-clar/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6 new file mode 100644 index 0000000000000000000000000000000000000000..bedc5f27ecdb5d005c8e8c787350359c204911de GIT binary patch literal 289 zcmV++0p9+20V^p=O;s>9He@g~FfcPQQAo)w(ls<-$YHy4F*%|}=V12}kMIZE1;s41 zUL&b6V{l&e*LmOa$~Ps3wTBbGL_Pj$w3HP|fjPqh!$n=s`HYu2p7ZYNPZs~Uy7Jx= z69XUsTVcTv(DFyCW82zu3&FX8D n-zfWSNI0ZYosPf{>4F=i;q$ShV!%gjkt0Md!2CHc9jMd_)DNja%p!&M3ZEje5) D!8{Wo literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b b/tests-clar/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b new file mode 100644 index 0000000000000000000000000000000000000000..0edf6599447ee43958038f7768201d1320ca0e42 GIT binary patch literal 269 zcmV+o0rLKM0V^p=O;s>9GGH(?FfcPQQAjK;$5Ep%1PBLsVHH1XTNaA0j~e` z4-``WZskbX%{y7=JXCRVMq*xiYKm@Vo^Dc6VqS6vSi|ZZmqjmJCav{$+?~Cq-PCR_ z)0%Ef4Y`TMC89V=y!@Ff%bxNXyJg)hnqeVJKV8r;#C9cQE4awd1{Ay!zK| LEq($3HxCdv1CbQA literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38 b/tests-clar/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38 new file mode 100644 index 000000000..bc2d7384d --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38 @@ -0,0 +1 @@ +xݏ;1 D}AV\8HIVp|?LyOuN7C] ͥlt:iA(xip,O;o7 UYZ Bý]dUmyk[cͥ)!X{Z \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86 b/tests-clar/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86 new file mode 100644 index 000000000..ffda698f0 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86 @@ -0,0 +1 @@ +xM1 DNi`ǹDB~ǧ]ݠY74M89H)1d}FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8uy7hhOYd*4uG+_L_E6ySYqjx-m5%IR~GH{JgZB%;b{H zymYYHSJWSDdT0?KWh;8dJfVDXvYhB%H>lY~sk!-O7>>FB_>=Z3`TTPs?h|AT+IBt8 zxR-qwQv;G?VyrWk%OCj_Yo>p>JuX9vt%$dFfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRUTV2+ zVqS`FabhmS;iuCkFYhRfZgSxYdGPA2E7nZ^a(i5`a7|X!_N7n_rNGbt2M*ZTCqCrsS#O%= WSdv~h@x$s@7oOP6iv<87+mri?YJ;Qz literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c b/tests-clar/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c new file mode 100644 index 0000000000000000000000000000000000000000..c39318683c48cf2f6efd2d7534311546e5f52536 GIT binary patch literal 53 zcmV-50LuS(0V^p=O;s>9V=y!@Ff%bxNXyJg)hnqeVOSbw%yReJsa>k41B|TnmVLkd LXYDBfM0pYg4$u|k literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb b/tests-clar/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb new file mode 100644 index 0000000000000000000000000000000000000000..2f54be818f84403317ba606d205d00e2a67f14b8 GIT binary patch literal 30 mcmb20V^p=O;s>7FfnE@G%zqTF;Or`)XmG+P0mQnOHb7+sVHH{ICjFDQ&R2t zO|E6jXKNqcoptAIA53kMZc1rEPG)jqNotC2W}a?-X;Cp)(}#1~eV%O`zOxPbx2tnL z5jkYH^%+7_QEGWnW=TnE9*UmFWxwyTz5bwhQeEZNe6=r5LdAY|Fg?k-D0)gVQZu18 zy+0`=fA7|Z?u89o?e_2aE`6orEhki$Aeu{4Lbbo89EZD4YOt5FZi)+ zN0RG(CSOVm!N877!!B&MVw`yA{FrSeHzwN`N}cfVqIQx`CsDal@}2WCPNvb&+7 z1vdj8SohlX9w&BpdQN(I@nQF(*N?&$3jKtdU`m`DE*H*yYwDt_WQp=MI$s zs7`ZKoyD0csk%nsVBa?Xh53_n=dYYx!gFW&K?}FG_WSHuHGx%cQ5QP@a+Xv8w^rc4 Q8GhVd(@FfcPQQAjK;$5Ep%1PBLsVHH%P`VYWA! z+-pqcwO^2Nj(9SA6I5|>eqLHmW^zepUOHH*x2o;pEUp*(&n2&0edFlX7R^baQ=m%o zQpA(GGaI=Zf^vM7aA5L>elTZQx literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9 b/tests-clar/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9 new file mode 100644 index 0000000000000000000000000000000000000000..a2c8d93ad5351c7bd8d8cb65b82c56b464480bdf GIT binary patch literal 83 zcmV-Z0IdIb0V^p=O;xZkWH2-^Ff%bx$V)BPP0P$l)hnqeVK}&Lsr2slLqD{|M1&4s p;*R{2xnTmDIwP>cwF_M)c{ZAr%V*wol}J0aXT!_)vjCBvBKOVWCfNW0 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f b/tests-clar/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f new file mode 100644 index 0000000000000000000000000000000000000000..02e183144619b23cdaa815ce9571b136b323f1bd GIT binary patch literal 265 zcmV+k0rviQ0V^p=O;s>9H)1d}FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRQEF~} z8PqZPB@m~u9~If)Hse7?r?%mV%RxILgm=!#!=fDF_WO@NX|IycKNsRYLB^nM*W-+P z*>^EDAh|upI%B!~kzcW9`j^||f`x0cqP8!EYA6K;2snJeEw literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531 b/tests-clar/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531 new file mode 100644 index 0000000000000000000000000000000000000000..dd7d58f1fd9cd06cf97f859c9583c94dbf1bd5be GIT binary patch literal 152 zcmV;J0B8Sr0qv4O4#GePMP1J+IDlhtnrN%C4D=K%}in1?r(Csl&(Hz z;et-JW9{C9H)Aj~FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRQEF~} z8N@M`$j+I=on!Lee3sLVh!6JfEt2`&vnMiPYC&@U{l}lQSIOs}3vr(yW6-wiamKyu zyOJ?$_k[Q,"Zz˟39 LO \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060 b/tests-clar/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060 new file mode 100644 index 0000000000000000000000000000000000000000..f578a4a680a3ea718e8bdda793058aecb3df291a GIT binary patch literal 49 zcmV-10M7q-0ZYosPf{>8WhlwWELH%bw9K4T1rr60-29Zxw9M2Lh0HvKqRjM+5=|}u HFjWoVCBYV3 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9 b/tests-clar/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9 new file mode 100644 index 0000000000000000000000000000000000000000..4d41ad8cd0441142957aa75ba9b07914728eb842 GIT binary patch literal 37 tcmb9H)b$2FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRZhlH; zS|-#r`6UpyTzw-L^iTEWzXOb&uNgMqF1)RBNCK+9C^a{~48!sFAAizbC7*vT#C?K{ zLEEm!8TYd9VroEge2jI*a`_{_V$Jj~x5otw*JMR)UkcSw3Jei&0D+x;;zPck^`>c# VCFyk&KdgRr;fc+>SODQ{mRtGjeQ^K) literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5 b/tests-clar/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5 new file mode 100644 index 0000000000000000000000000000000000000000..52fde92a14135ddf042413bcc61825ef840ba4ac GIT binary patch literal 172 zcmV;d08{^X0i}*fP6ROw06FIs{s72{?JN>PT=)SWNN3TsMA|5w(R_cJH*hP}p;Sf3 z*cUhsFKf02zLN`-3I)g2R_Rg1r9_xI#puy@#=t(B-#pu~AjW2+B#YTfsg}0dnWJ#7 zNTG9Nw;^pVnS5V2o$ys3c~)i6%hxC`b6M|YlZVvl$DPu*_@#X)>rtX+g@8yX2QUDl(|)$4 TKW9X%%X9J90ZF_8=h01b0%1%w literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d b/tests-clar/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d new file mode 100644 index 0000000000000000000000000000000000000000..2f833c2924a9765de95b5d05b189c632dc944c5c GIT binary patch literal 199 zcmV;&067160i{nnPDLRQwPqD`uhI+*!zVGu#?sCWz%Y0%xELPB?TbcNU~_VQImzMC z``gk8{$@=P-Fy*nNo;fsEmbasgClYnBNagcE19fO?|I6(bg7ikMrs_IIL;9{oWZ^ZJi!m}2@%l!Y3%IL#1&`u7 z&-bR$*EaM*Bb%y6K1!$Gbe7++wFB4rq>c5?lLsWdhX9~D?Pr_( Wt??!#h-TBMGTzpE5u-i`{!TB?T2e3o literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d b/tests-clar/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d new file mode 100644 index 0000000000000000000000000000000000000000..19cac9faf4a65b1ebae6515b1ea3c58745f85e87 GIT binary patch literal 170 zcmV;b09F5Z0i}*xYQr!TMEk8(Xaf%Sp{vMHLVmh~Za|M~Y^bfED(UvCmh2#ZGiTs1 zx$gVcEeXB#rfLahEVIiY71jiSrDP$3qM{)sMpz;Wvbn{kN4GNj0Gx!FfTIh7Igrb6 zX76Y4%MkG5VlnaF*V^ny`H8LA&$aH~^|0?c=wG_(|L|<5T=$Db7D7N|vJ-d!Gw3+m Y^+k>8ZF_9-YeF}r7LUA|U!_%0xC}*EumAu6 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e b/tests-clar/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e new file mode 100644 index 0000000000000000000000000000000000000000..5a96a4e4e3c624146e03fd75965117b132d562e8 GIT binary patch literal 30 mcmb!yw5#gVhE67`v(BN9t4Ghi?@FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nOy zF#rOEr2LW$-IUCtVupu8vu%Dy%y{to8ry@@4Vy~i0xl*(4NT5R%u9#3AgL%ZFF6Bj z=;|DoMK4??t@U=?oxP^r)NU@*nr=)DNKO{Kx0+$odm6iz&m9DjO`4*c@m0ePu zB;_7$c_#0A%m3;oW5cdf(25q|@XWYxa3)N5x3>9!7ft`Qi cL%yE%rfH5P>2(u7tbTRjiOsxN0LB=(`fXvqU;qFB literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497 b/tests-clar/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497 new file mode 100644 index 000000000..67271ac50 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497 @@ -0,0 +1,3 @@ +xK +1D]v7t3L$ UEV4X9&s2ELKR%%t=)M(s`-n3YmEd`N<{uhI(88%DM|2b}JG< literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab b/tests-clar/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab new file mode 100644 index 0000000000000000000000000000000000000000..623a747f01c770b9e71d758624383a1d6967dfd7 GIT binary patch literal 47 zcmV+~0MP$<0ZYosPf{>8W+=(XEan2DM6R^VoK!B2%shpXj8uihyyVp4lKdh~E&vNJ F4D}Gn7F7TM literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231 b/tests-clar/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231 new file mode 100644 index 0000000000000000000000000000000000000000..91944ffb57fde51caf171042f60e03d565e03f5d GIT binary patch literal 43 zcmV+`0M!3@0ZYosPf{?lWGKnVEan2DM6R^VoK!B2%shpZwD{u8lvE>4E&$k?3p&Qe B6oUW& literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29 b/tests-clar/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29 new file mode 100644 index 0000000000000000000000000000000000000000..ae1c5e242c1878b29dce3a1aa7b5372f92c958b6 GIT binary patch literal 177 zcmV;i08amS0iBLPYC|CyguC_>ULZyNpFV|9vdIm)59&XXKz$aUA=j@7IYDPN^9>A> zTdlVtjkO=612QitAdSGtQ$CrrFmVYnW|O(VgkYWa;x~3|LnaglS`z_|Op5EGZP3np za)rDpV550mS;YIewl2^7h@Iv4wbi|C@`y>F?u1wIPM^8eFR95XWyonQ56UYgCY@&o f{xRd7CI6lg^Lebe8XDG?YEan2DM6R^VoK!B2ZC'g$楧f&%%g5qYeZokM2ԐX\ZDPC~^ZNfrIf^:XZHي1O(_,' jv^j9!Ɖ9%`<sBާHrS3d Ң2 wI{| 2mg˾15ӿ,\})TC)00avʉz֛9MՅ'6bG \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f b/tests-clar/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f new file mode 100644 index 0000000000000000000000000000000000000000..4ec0138816823c0fad877882b0bf10fff18ddfd5 GIT binary patch literal 201 zcmV;)05<=40i};UP6aUxgsF3ibgu$Aaef>LAsR|LE?_4POOXxATg3HQ5S)N+=9|$- zQ|B0Oheb#?$5OQ&yK^Z>B8gdCDN#}+CQs-=Q|k!iI;(l-rTbx}#e$wkxX#_) z>q)=Wqy7ZH?yk=9U>zm^zzEUa0s)wlp63qz=ZtyQ^{Q4D?{%}6^EVH@`iuDjY}aAH DgREd- literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a b/tests-clar/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a new file mode 100644 index 000000000..f4249c23d --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a @@ -0,0 +1 @@ +x퐱 0 S{"2d,0^?&SH[8눪E`фrZ*drl, cbF/'gв \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071 b/tests-clar/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071 new file mode 100644 index 000000000..e42393cf7 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071 @@ -0,0 +1,2 @@ +xAB!C]s +.acxf`|_ bh5m^mzL`}$26#"8`s.`ԝܺ.!bH\< i",K8ٗ_X>MeЏ:7]AC40뭙Q]Q\.,VO \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa b/tests-clar/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa new file mode 100644 index 0000000000000000000000000000000000000000..d2de777ccdec25efd39427d93f9d31fbb6850225 GIT binary patch literal 164 zcmV;V09*ff0i}*n3c@fDME%Ywa)HVw>824x{P6}}V7t4a1#2Wmy}nhvfqyga!7yBw zWp38@xM@|@k`N;GD6U682nWeLxL(O|MDgl0=26pNZdCQ!EJqkJ2S_Ps2q>C;Vu>k^ z=yK#t;75qch8}Gy)t=-D60 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57 b/tests-clar/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57 new file mode 100644 index 0000000000000000000000000000000000000000..35453ebfd5d3284e933cfe2358d17e7f9fe4cff6 GIT binary patch literal 271 zcmV+q0r38K0V^p=O;s>9H)Jq0FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRZhlH; zS|-FbhQ?skGXLitTT&HV+;H#yUGbC8b4yfbX+u>PrRL_BVL1K%<4@YF9Ghr|^FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nPt zDo)Nw%u9!uo>Y{Wmz)9CusX+O(F>PJYrP$JXRm2DwVTVdrW;cOl5_UX*>aYvoM-3S z)A8pN6`kHYypl16X~@q@%gIbG$;?X!o9)+;m!PV7YUOId>+)^M-WRt$xF-NrnwMG* z@@85ZSfQEsKA#sn)tBXx+3!5Bb$k4L(Mncx6@%~Lw3ea>7oH3@vq#L$0?;}ZZT_z%8A4N0YI#v+Nl9uRik`=1zwffW{-AhLUFFt%wJ%OW#eQ}$J;}N#dP*`>1-*CQ1Do%c zw%oXAb-*BaB9Aa5P9wnHyKp7aPwdmfiR_Pm)h)BDTJ_%`{x?iV640q2JHY`0u?5Lm zcnH0ns{BmP)h^)f?1u?iA1BY;nP*vr#|W^s+VS!da*wz6Ef4&9H@1JaOQ^y^GpHUT zWUnUWmt=r7h<7?{3HW5OG>5lz`_!1N)fSSHO;8QSn1Pd$nv;qdIufT1vt`~d__1tC zKA(bApHzNJ)J~`wCYWX}F5$Ve{Gf$fTl;->teU`9Y*80F Z|8ka80Jm1)zZrhqUFA%>1OY*1MnrK80~P=P literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25 b/tests-clar/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25 new file mode 100644 index 0000000000000000000000000000000000000000..4b2d93b07de774c1c0c7aa7cf266e6034d4ac9b2 GIT binary patch literal 53 zcmbXvEU#p|pKHt;UtbER6gj5# d+XD%?D8yil28|jcL2~arFGPE?-~;t=8hU`!AFKcX literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b b/tests-clar/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b new file mode 100644 index 000000000..de9ba2894 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b @@ -0,0 +1,3 @@ +xM ]s +.Obo eHh1E {?y_XenR}hY* HFS +S !$1œ*MwUv4It:8KFEA6*oM5T=+ݝƲ\ѠCV`nLjۜXiO\ \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149 b/tests-clar/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149 new file mode 100644 index 0000000000000000000000000000000000000000..e998de849c865efaac9d2aa5e89b23d36dc55a4f GIT binary patch literal 40 ycmV+@0N4L`0ZYosPf{>4VF*gi%`Zz$QOL|wP%kOUEXz#H(X~+5;{pJy9|`qUiV=bU literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4 b/tests-clar/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4 new file mode 100644 index 0000000000000000000000000000000000000000..359e43a8834a5bd911de3f1c2c258301ab22eff6 GIT binary patch literal 53 zcmV-50LuS(0V^p=O;s>9V=y!@Ff%bxNXyJg)hnqeVL0Ctb7Do5{MRMB0w3(D`I;7% LIms6QRPquG^QRP3 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f b/tests-clar/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f new file mode 100644 index 0000000000000000000000000000000000000000..e561b473f8a3cbd55205277cfe1017a51fbb124e GIT binary patch literal 160 zcmV;R0AK%j0i}*X4#F@D1Ucsw`2fhVn>0uWap4Diz;zszLmJcu@qGf~2i$gNrO`?) z<=!j~Zr4^-n`q=TA|rSV(Ps$=PLT5v91y1g6T*0$%!5~5nw0=!7J?W_iG0$GQBsIu z@-7F-3^+khG5l;*Rs{cp0v|Xy6AWKwS&}hwvN2>VE`h#2M=I6?Pr_* OsWFAOE-`NlT~2zA6-V&^ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714 b/tests-clar/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714 new file mode 100644 index 000000000..6f5e97978 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714 @@ -0,0 +1 @@ +x- 0  G%ȅ"M!@yj񼽊vj:AAM~dА{.3);l]vi6D% 9f|.z \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db b/tests-clar/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db new file mode 100644 index 000000000..c8d636e8b --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db @@ -0,0 +1,3 @@ +xA +0=y}NˮtaJ[ +/^r$<, "1*[\ Yj (;m9 oNxcz"1(7yۗ. \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0 b/tests-clar/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0 new file mode 100644 index 0000000000000000000000000000000000000000..01ad66eaac30ec303895cc9307a6615643841fba GIT binary patch literal 164 zcmV;V09*ff0i}-J2?8+?gndpGX+YT|o826U_+ke(ke@y7z%vq~w(lu+AoDWw4Gfd( zvdmpu#7%D^8j82#qW2^w3@O5ZA&MssFqiU1gvn6l;FvVV4AlmmAqK0eCQ8OvCmwO-dzaCdRP&IE4m^aM2omO~ zEt&v879AQ@K*VRC+A1&Q71tuKQ(Lxnmq#A-9WH2-^Ff%bx$V)9}crt%twM(zC!)DpCr{0S@)cUWSR006| Gr4E*%ITSzu literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102 b/tests-clar/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102 new file mode 100644 index 0000000000000000000000000000000000000000..e6f8500790efd5b74d9339fbc567300bb5719056 GIT binary patch literal 38 ucmbqhTS#F!Zm<7~gOe4TPyl^GbPwdmfiR_Pm)h)BDTJ_%`{x?iVl5SpVImlLo_mHfG z2a<}E0PES=GuVwvq)RNKI|ZHg?9#wv1lU^bczFrA$6NcB2Y$U9+dtbSRAHeRRF4s| ztx5SM8DI_Koeo<9K3Ocy;Vs=hHD+tIg`{K?RD&@#cc!G~q(Z|7?oczu*`8*FZm0FK zL_@c(e0}ADQOi`Q879Egg77OifD=2C>;QpgL)Th!1s?za literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4 b/tests-clar/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4 new file mode 100644 index 0000000000000000000000000000000000000000..c63fc2c969e292d9cd3aad9e53818bb4a8d7acf8 GIT binary patch literal 36 scmb7FfnE@G%zqTF;Or`)XmG+P0mQnOHb7+sVHH{ICjFDQ&R2t zO|E6jXKNqcoptAIA53kMZc1rEPG)jqNotC2W}a?-X;Cp)(}#1~eV%O`zOxPbx2tnL z5jkYH^%+7_QEGWnW=TnE9*UmFWxwyTz5bwhQeEZNe6=r5LdAY|Fg?k-D0)gVQZu18 zy+0`=fA7|Z?u89o?e_2aE`6orEhki$Aeu{4Lbbo89EZD4YOt5FZi)+ zN0RG(CSOVm!N877!!B&MVw`yA{FrSeHzwN`N}cfVqIQx`CsDal@}2WCPNvb&+7 z1vdj8SohlX9w&BpdQN(I@nQF(*N?&$3jKtdU`m`DE*H*Rxi8;LSX6a6G1H*> z$3iI!s0n7epg4o497Elbd|e|%cDnJad|R_Y)4^9}+nc%~bbXvVR05zn%~5q0XQrg; z8i9j-+x!>iPtKjca&igJo#h8D+}hgjvt!i+R=q`C=={rBQUTmrf&XUsad(w7?Ggk4 HRR@?)?dUH% literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4 b/tests-clar/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4 new file mode 100644 index 0000000000000000000000000000000000000000..a6c05d1821889a71bfd029059b1ff84cf2992a7f GIT binary patch literal 37 tcmb+2;a~44FIfP3wHnj literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4 b/tests-clar/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4 new file mode 100644 index 0000000000000000000000000000000000000000..24d7dbc2eb253f6470cb837fbe8b70361e5b6e94 GIT binary patch literal 164 zcmV;V09*ff0i}*n4#FT1ME&LzT%cxwT?&aY{&)i~04z^>ZTZmRHC0XtU2$<{N~$4PwW*`@@A9)cM+`qNu+sJ{dpl+8B}V{{JS3 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48 b/tests-clar/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48 new file mode 100644 index 0000000000000000000000000000000000000000..06ae09eb6d090f5a394cd7c39882de7d68e82565 GIT binary patch literal 162 zcmV;T0A2rh0i}*h3IZ_@L|x|;xxh#|pY8-i+;{^okWUZeA`^)bukVa_1F22bD<~?r zTJJ+@YYw9`OI4g^6P-f_A5v7o`p6E+s{mdBWJrKi#3OcIhny)Jqf@cF5)m219N89a z3!o=U8VJOMSELH%bL(MnP87o`X?VaJJb+r$_3V(p YF=%}9$^0;?)C(;vkI}k%17&AXZjo(N>Hq)$ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543 b/tests-clar/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543 new file mode 100644 index 0000000000000000000000000000000000000000..76dd5f91b42a319c9ab512351b355f50e2cbeaa7 GIT binary patch literal 65 zcmV-H0KWft0ZYosPf{>4wqz*D$ShV!%gjkt0Md!2CHc9jMd_)DNja%p#7dc)swxy` X=4R$377?w7Je%<7BGM56^gmd;=`kMA literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0 b/tests-clar/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0 new file mode 100644 index 000000000..67126c90b --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0 @@ -0,0 +1 @@ +xN !L4a/v{`1,Ec?/R.ޘ%3$L15fe53'427^G1yBGVLAG *|R) sm^%yk*O<C,{eCg;9#RoKboN \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972 b/tests-clar/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972 new file mode 100644 index 0000000000000000000000000000000000000000..d39034b82f3cd141006946fc99129f860142c13a GIT binary patch literal 172 zcmV;d08{^X0i}*zs=_c3Mt#mIW&=ty?c@h87hl|g8%QP-S}={I5w~w#gKr-PzQf^K z%GLoFf9g#HQZ#6EuxyBJPR2UIiG7SFBQKU5hUg`cdWI(ZIL;meroikaM=}_lnJF2v zKF5WeCC!#8s_^P-ZE#EP&=TC&T8_HIpA7n4*RpY|N6r1hwfuvghe8usg!4qxqy`ZC'g$楧f&%%g5qYeZokM2ԐX\ZDPC~^ZNfrIf^:XZHي1O(_,' jv^jnb^nJfZZjQj^ X#3|>^U:'A2R2 I{| 2mg˾15ӿ,\})TC)0H!vʉz֛9MՅ'6bGx \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473 b/tests-clar/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473 new file mode 100644 index 000000000..91113ee8e --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473 @@ -0,0 +1,2 @@ +xK +1D]};7d=oo^UQT\;hk6@g 5rѓ]uOMndgz&c圈'} NJ7p?(G\8CآGTg9x$faxN" \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573 b/tests-clar/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573 new file mode 100644 index 0000000000000000000000000000000000000000..7da1da656f6ab89f34f15cc1162a92b04458e657 GIT binary patch literal 161 zcmV;S0ABxi0i}-74FVw$ME#};8qjbo%VA=SKXzaPu{8eW`=4h z_pYsjU2n`9B}vL;Tyjj&c|j*GAVeUHQzo`SaPp`gvGLk9c{2-v2>}=YGm8zBeeek< z@g&jMWIU6K&%V^AulW^Q*0-gWt*-jSgMQS7zroKPsFt&y0AW6kb*uNr8>0ptXFLB{ PqsH@Cc(dva#nMn^m!eK? literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b b/tests-clar/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b new file mode 100644 index 0000000000000000000000000000000000000000..d840c1a573d3e52ace510582218e0665a8806093 GIT binary patch literal 33 pcmbL(MnP87o`X?V;(m3biX?o>}lPD{Lxft&)08*I{F#rGn literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05 b/tests-clar/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05 new file mode 100644 index 0000000000000000000000000000000000000000..4c32d63f82cfe08f3000fc0e168eecba1dc7b0f0 GIT binary patch literal 29 lcmb4WhlwWEan2D#Jv2HjMO59ywq~8w9K4TE&#U}3f_356G;F7 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87 b/tests-clar/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87 new file mode 100644 index 000000000..3091b8f3d --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87 @@ -0,0 +1 @@ +x+)JMU067d040031QH,-M-JOMLI+(aH:,:C: o>ZC'g$楧f&%%g5qYeZokM2ԐX\ZDPC~^ZNfrIf^:XZHي1O(_,' jvn~JfZ&5`nלU7 V.6t6L/R2 I{| 2mg˾15ӿ,\})TC)0<vʉz֛9MՅ'6bN* \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656 b/tests-clar/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656 new file mode 100644 index 000000000..20fa838f2 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656 @@ -0,0 +1,2 @@ +xQA1+xċϡ-kI*5f/z af!^/WJcܤ5Lƛ;+B6HZP|`h>\($sX@75}57K ++= ;g @!4!,\$\ \b/Hs#aQ \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11 b/tests-clar/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11 new file mode 100644 index 000000000..2820b46cc --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11 @@ -0,0 +1,5 @@ +xA + {B{M1 ߯>P3F֎7E02 X0̒,)$;:ܷ(: \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8 b/tests-clar/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8 new file mode 100644 index 0000000000000000000000000000000000000000..fb102f15dc5a37b8cdedca9064e37463ec832a2e GIT binary patch literal 162 zcmV;T0A2rh0iBLZ4gw(%L|t^KaPNA+>svYC+{40y?<|1Q3(XvxEQB QSll$CrO~YT09BApVed#rm;e9( literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127 b/tests-clar/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127 new file mode 100644 index 0000000000000000000000000000000000000000..22f2d137d4d55d218d30aa0f04ca3aece24a4c96 GIT binary patch literal 320 zcmV-G0l)ru0V^p=O;s?quw*baFfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nPt zDo)Nw%u9!uo>Y{Wmz)9CusX+O(F>PJYrP$JXRm2DwVTVdrW;cOl5+&_t!CJCdD+*+ z?3NvCSb21-(?TD?H00-{|BDN`YPmhXB}ZCqCrsS#O%=Sdv~h S@x$s@7oOP6iv9He@g{00M=S%pzSw1BS`vf6i_a2;uHwRXCH^@H$yXVXrDe zff>WJp5;@PMAp}5?cTKGro$HYE`Iwf2zhe`uc-mA&rCn3v`OD}eRb394F(sOzar!< z7y?@UXmxDcyQ_Dn_2sEn{rV%FFfn3DqYLL z!njBdS%onJv)-Pl9kEk`cT3dTT;VAFue{9e04HDWL{FfcPQQAjK;$5Ep%1PBLsVHH1XTNaA0j~e` z4-``WZskbX%{y7=JXCRVMq*xiYKm@Vo^Dc6VqS6vSi|ZZmqjmJCav{$+?~Cq-PCR_ z)0%Ef4Y`TMC8#9Cq(LG5y$& zWteJBz-m9utKT{yRA8zyPon4P=lXgRS(Z|BDN`V0k4sWo@CqCrsS#O%=Sdv~h@x$s@7oOP6ivg+xyA;@=`73 z-nE3VMxwM%u7#AQOL|w$W1IRNiE_^%qh-SKoU+%(@iQ$%uCMT J0szul5x^+`6>4GG#C{FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nPt zDo)Nw%u9!uo>Y{Wmz)9CusX+O(F>PJYrP$JXRm2DwVTVdrW;cOl5+&_t!CJCdD+*+ z?3NvCSb21-(?TD?H00-{sGnKH|N-zXI~50WN2al z1PW=HP%r22o4+Hb?fOOM_@dq=5_|s#9dlR-HK8aqH@^(S%l99D(q1K>e=fv*f{a1i uuE!bovhPAQlmdeV974GGQ<@FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nPt zDo)Nw%u9!uo>Y{Wmz)9CusX+O(F>PJYrP$JXRm2DwVTVdrW;cOl5+&_t!CJCdD+*+ z?3NvCSb21-(?TD?H00-{e=fv*f{a1i uuE!bovhPAQlmbHq97tg2pZJikXT52fV@Z15#1E@qU3g+MFBSk{8W+=(XEan2DM1{Q6a)q?aoK!B2%shpZwD{u8lvE>4E&vVI F47C%Q7E}NL literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a b/tests-clar/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a new file mode 100644 index 0000000000000000000000000000000000000000..d22b3b23cac60ba521d25c4293898064c0ae1593 GIT binary patch literal 162 zcmV;T0A2rh0i{k`4#FT1^qW&~ftoGLqa?=o;|;t3EKAzN7K5nQ7mYXYZ(f-hD(kv5 zskGj;$}Ckjeo}g3Cd#B78wy${pS;BhvrU#qs^ttb9#lE3FlQ*q$ShVU&&>GO3h2mO-)hA%u~oOEh^?p%u7*7 ZP01{Q2$f`{W&#DdMzzpB!~n^9kr}|f9F710 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618 b/tests-clar/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618 new file mode 100644 index 0000000000000000000000000000000000000000..c7572d5bc5eb9063222d584fc3d15e6d3e8c78ea GIT binary patch literal 538 zcmV+#0_FX90V^p=O;xZkF=a3`FfcPQQ7}l<&CAzK&PdElPt_}_C}GGrcEX!eQtkLn zu4T(-Yaia7b?0m!Ol^{GN@+n(W^!UlYKm@Vo^F0=Q88H4hjZF}o^2eyvkm&Ut8+dP zIb^r>8A4N0YI#v+Nl9uRik`=1zwffW{-AhLUFFt%wJ%OW#eQ}$J;}N#dP*`>1-*CQ1Do%c zw%oXAb-*BaB9Aa5P9wnHyKp7aPwdmfiR_Pm)h)BDTJ_%`{x?iV640q2JHY`0u?5Lm zcnH0ns{BmP)h^)f?1u?iA1BY;nP*vr#|W^s+VS!da*wz6Ef4&9H@1JaOQ^y^GpHUT zWUnUWmt=r7h<7?{3HW5OG>5lz`_!1N)fSSHO;8QSn1Pd$nv;qdIufT1vt`~d__1tC zKA(bApHzNJ)J~`wCYWXNG>uS)7@Ys%r#x@V5Cc%%7Y)f92#7o;%AATDY~f-)G0F39NdH cy3qNTv!nvJwF3Xm@Z;_(XWAtQ0Q_A_2K7t`WdHyG literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd b/tests-clar/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd new file mode 100644 index 000000000..a1d5321e8 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd @@ -0,0 +1 @@ +xKj1D)t4l|>-f 9UV61:̸ !>Z.P0x hhQ+t`1NZe,X[ =vyI_vJ^2$?I7o4{K>V!~|U= \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c b/tests-clar/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c new file mode 100644 index 0000000000000000000000000000000000000000..2f2ada732bb550a444b455b69cd88602c3d67f07 GIT binary patch literal 269 zcmV+o0rLKM0V^p=O;s>9GGH(?FfcPQQAjK;$5Ep%1PBLsVHH1XTNaA0j~e` z4-``WZskbX%{y7=JXCRVMq*xiYKm@Vo^Dc6VqS6vSi|ZZmqjmJCav{$+?~Cq-PCR_ z)0%Ef4Y`TMC8,af<EZȳ5%<'.v,;]=2tws-,w8@ \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed b/tests-clar/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed new file mode 100644 index 0000000000000000000000000000000000000000..ae430bd4afba7895c20f6edf0ded1ed7cfc1fac1 GIT binary patch literal 40 wcmb_-c8b@je%D{V@~65^K(h082;^T>t<8 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d b/tests-clar/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d new file mode 100644 index 0000000000000000000000000000000000000000..5dae4c3acfaa6a68557540b75781f0684061f48d GIT binary patch literal 45 zcmb1a-}BM%t|iLpQ@5E3@i_MjRCl> B5-|V( literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb b/tests-clar/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb new file mode 100644 index 000000000..da8dba244 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb @@ -0,0 +1,2 @@ +xQ +0D)rfnSxfCHx}xfc,˵Y kUb8pu`%|@r3GtB;W]!z'%QiӐdT ?\=d/sYe';^r#l`6m Z7U^e6oVO \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa b/tests-clar/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa new file mode 100644 index 0000000000000000000000000000000000000000..fd1ec9fab86ffc33f034346301fe738c72bafa5d GIT binary patch literal 162 zcmV;T0A2rh0i}*h3IZ_@L|x|;xq#B?NvCH(#Emy_FZpzsS&WfP#OoWy8@Q`_1&@kl zU6-cO4!c%GbfVyLoE*gooj0Lp5lap%hRMP>28xu>sE4m|Z#r27&dgmv072p~kPntQ zPl%jj3PzBPSN_@NQuQUhd`Ti}ylK=n! literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b b/tests-clar/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b new file mode 100644 index 0000000000000000000000000000000000000000..32ba2aa53a6f19f21a89b0dc13c6e221ea00f81d GIT binary patch literal 40 wcmb7-s7w2Zl(JJfmRniF`=WS>qk5dtT)>G!ul%4q|lT92TiIa-m2w zno3+V*cnJz<>y%Xs?X(>*P<^=Z;yV{2ifQ+o#ZQiZO^?OwQ*=tjEP83fB5oQ1Y literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f b/tests-clar/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f new file mode 100644 index 000000000..9a0cb7a0c --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f @@ -0,0 +1,2 @@ +x] +0})&_H -Fb[6}0L2wPzc*sXb Rt#G$[lvH$kf.ʧLF+ QHD|68Wl.S]uoNOu9Va0^ZF9^# Od \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3 b/tests-clar/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3 new file mode 100644 index 0000000000000000000000000000000000000000..860f9952f115c5613a9ad3706050f35c37da9b38 GIT binary patch literal 48 zcmb3Ghryn$ShVU&&>GO3h2mO-)hA%u~oOEh^?3l^k+` J002O@s4gWp6x09! literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e b/tests-clar/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e new file mode 100644 index 0000000000000000000000000000000000000000..558a8513fd0e24e1957965d5b64174e87018989e GIT binary patch literal 268 zcmV+n0rUQN0V^p=O;s>9GGH(?FfcPQQAjK;$5Ep%1PBLsVHH1XTNaA0j~e` z4-``WZskbX%{y7=JXCRVMq*xiYKm@Vo^Dc6VqS6vSi|ZZmqjmJCav{$+?~Cq-PCR_ z)0%Ef4Y`TMC8AVK6i>Ff%bxNXyJgHPkDqC}H@zm8W$@&6^UR-xF m%!g?*k|HCpBG0)$#6{!0@7m-&oU~jg=u2Z`lK}wRQy>;UUn6<| literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b b/tests-clar/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b new file mode 100644 index 0000000000000000000000000000000000000000..b2f39bff413f61faf64377a96e8132790ec9911e GIT binary patch literal 233 zcmV5H)Aj~FfcPQQAjK;$5Ep%1PBLsVHH1XTNaA0j~e` z4-``WZskbX%{y7=JXCRVMq*xiYKm@Vo^Dc6VqS6vSi|ZZmqjmJCav{$+?~Cq-PCR_ z)0%Ef4Y`TMC8L(MnP87o`X?VSG5BY&Jp}-?)eH9k literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f b/tests-clar/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f new file mode 100644 index 0000000000000000000000000000000000000000..a7921de43c86dc803ee6c2958286aec2d7c0c8d0 GIT binary patch literal 81 zcmV-X0IvUd0V^p=O;s>AVK6i>Ff%bxNXyJgHPkDqC}H3f-z_?I+PWo|rhaLyU0&Be nZS(OvNQ#WWitf$|=P_Y;_MrO6^;FqBg$Y{Wmz)9CusX+O(F>PJYrP$JXRm2DwVTVdrW;cOl5+&_t!CJCdD+*+ z?3NvCSb21-(?TD?H00-{yi7R zYKl^G^UE;2bN}%t?N##m=R({k$QZQkdYo}D`z};NDbUN{5CFUF#D{!6>rK-fOVaBm RepvnL!V{Z$u>dpcn{GXBs5Jlp literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218 b/tests-clar/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218 new file mode 100644 index 0000000000000000000000000000000000000000..0d2534bc9be203e229c6ed85d5330c2480e8ac0b GIT binary patch literal 24 gcmbqhTS#F!Zm<7~gOe4TPyl^GbPwdmfiR_Pm)h)BDTJ_%`{x?iVl5SpVImlLo_mHfG z2a<}E0PES=GuVwvq)RNKI|ZHg?9#wv1lU^bczFrA$6NcB2Y$U9+dtbSRAHeRRF4s| ztx5SM8DI_Koeo<9K3Ocy;Vs=hHD+tIg`{K?RD&@#cc!G~q(Z|7?oczu*`8*FZm0FK zL_@c(e0}ADQOi`Q879Egg77OifD=jA d=D#q1a_;<9WH2-^Ff%bx$V)9}_!{7E_Uy~+%BrwG(Q9&Y>W!w&{saIG G+YnXBv=vzZ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50 b/tests-clar/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50 new file mode 100644 index 000000000..8f9ae1fc6 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50 @@ -0,0 +1,2 @@ +xQ +1 D)r%i@oje[7̤ZʽMSLBNlm B~>-uY8ꠟt֯]Qa͠3f]>bl(A] \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b b/tests-clar/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b new file mode 100644 index 0000000000000000000000000000000000000000..1d8037895d1381f172b57a4d583d94675912a95c GIT binary patch literal 162 zcmV;T0A2rh0i}*h3IZ_@L|x|;xxh&0-wh&eynz?!Nm^#WF%qL*-%-4QyQ=q~s9cw2 z?%I04=}kmK305435XB&k4CIj|#DqWw!I*4D6H`$)ZgTA!$Y#NawxqLT%Z^hdj)gM^ zqyQ%3$RT^hk3Q9=kLAU!=+jh}d%fzNtn`x(@)f_f&2`yp8VJOMSELH%bw9K4T1tSHG-29Zxw9M2Lh0HvKoYb@uO)dZ- GSq*CoG8G&E literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf b/tests-clar/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf new file mode 100644 index 0000000000000000000000000000000000000000..6292118e04480b8dca7e237cc3539b7fc92704d9 GIT binary patch literal 41 zcmV+^0M`F_0ZYosPf{>4X9&s2ELKR%%t=)M(s`-n3YmEd`N<{uW_nxz%E=2MbzKrP literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5 b/tests-clar/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5 new file mode 100644 index 0000000000000000000000000000000000000000..b82e7fcafe919ebc819e724da40b51811ee2e77f GIT binary patch literal 624 zcmV-$0+0Q80V^p=O;s>7FfnH^G%zqTF;Or`)XmG+P0mQnOHb7+sVHH{ICjFDQ&R2t zO|E6jXKNqcoptAIA53kMZc%D^QD#X=YMyRpo^F0=Q88H0rpq(ei7NTYMzH1C^nT}g zT>E@k7)(#HZc1rEPG)jqNoop6Q%Oc@CRCe#m=GiTwFaSCvC}7RRXi#BH$ZIDIPHctgGE4+IFfTC|ViU-Dx~VCdm=^Q4>-V)w87MTf zPVYPY^I2}2znBHoVoW2zKD=-x(ogKu!-?#Vf7LCst6KHnApSQ@N0M${YB|VOg!hoF zg$I&~lmP45*)!OUN~B9HqB{kh_UzKYV+7b*?Ra?!xyM`kmIr>l8{0qIB~)Rd8B~uE zvaLz^B^h81;++m#0zO$R&EYNGJ~d`*wS}Z)6I6pSHg~3^=A=Ty2kuZa#o3-_g>I+y zvP46-u6%vvf>Fyari$u=~@NRF7|~f`q0XIvsi&YJw?oZkVyliS^HC`PiE_6LU@f*<0K4 zrHdd;Ku;!c7a%3H%Z0O|UOc&}RJPU7=4IKpcTu0VzyjZtIA0{I6slU}zI-!bQPt(d zOoQql3#DM`%uE*)XYk}|s9Tb+YlNIRZu~0W)@;yp@Riy2rmhHGALkC00I2)TQS}yQ zrljf`f}?+ny3qNTv!nvJwF3Xm@Z;_(XWAu*RTEhCw)rp2pPV~?<>V5cJIfDRxV5$4 KX9oay(2y@gz%(=f literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9 b/tests-clar/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9 new file mode 100644 index 0000000000000000000000000000000000000000..8fd60cbe8f3786729c2cb298f759694f2b533c1d GIT binary patch literal 36 ucmV+<0Nej~0ZYosPf{>4VkpVTEan2Dl*}Uiw9K4TBfXM}5-tFg2nnPqw-2BI literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9 b/tests-clar/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9 new file mode 100644 index 000000000..04dda4a75 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9 @@ -0,0 +1 @@ +x퐱 S3ŏlKAB4Wb T5:8Sc ԻP`KIˆO3Z&ؐ \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd b/tests-clar/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd new file mode 100644 index 0000000000000000000000000000000000000000..e135694400ea648ddc37f5f9aaab082ab2c75097 GIT binary patch literal 40 ycmV+@0N4L`0ZYosPf{>4V<^eUELH%bqSV~{veXoX%shppqQt!93@!l1nG1b@;uBy1 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7 b/tests-clar/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7 new file mode 100644 index 0000000000000000000000000000000000000000..955431dd7f86a53df75f5e724b277f513956f5a7 GIT binary patch literal 53 zcmV-50LuS(0V^p=O;s>9V=y!@Ff%bxNXyJg)hnqeVff>doSQ28@=@YXHQqg+&*Yt& L{)z(tOS2K(E*BT$ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161 b/tests-clar/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161 new file mode 100644 index 0000000000000000000000000000000000000000..751f1dd3339cf62eb47d7ecdec90c61467ef6a25 GIT binary patch literal 92 zcmV-i0HgnS0V^p=O;xZoWH2-^Ff%bxNK8pdP0`KF(@n}R$Ma%9vt%$dFfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRQEF~} z8N@N>V6~Fl*LVbFZYx^I$i^|{p7qK@i+nF)szq}6{l}lQSIOs}3vr(yW6-wiamKyu zyO literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595 b/tests-clar/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595 new file mode 100644 index 000000000..7b84ce966 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595 @@ -0,0 +1 @@ +x+)JMU067f040031QH,-M-JOMLI+(aH:,:C: o>ZC'g$楧f&%%g5qYeZokM2ԐX\ZDPC~^ZNfrIf^:XZHي1O(_,' jvn~JfZ&5%\N,5[e2 I{| 2mg˾15ӿ,\})TC)0Dvz֛9MՅ'6b \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4 b/tests-clar/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4 new file mode 100644 index 0000000000000000000000000000000000000000..a28ded3fba14e82d26b34749d3a0eaf2ebee1bc8 GIT binary patch literal 210 zcmV;@04@J`0V^p=O;s>5H)Aj~FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRQEF~} z8HQu-KmMe>N>Q@(@ M*vyLs00YTyo6fjkXaE2J literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e b/tests-clar/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e new file mode 100644 index 000000000..8da234114 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e @@ -0,0 +1,3 @@ +xA@E]s +`@ uH)M=Scz:ʊ(N+6ޛDFe𭭘Yg$+G&F +pG 4mQ\85#FC~QERu);c6'j \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922 b/tests-clar/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922 new file mode 100644 index 0000000000000000000000000000000000000000..870c3e732127964210a7f5f278df26e3cb8f5010 GIT binary patch literal 36 scmbɗEc9%bh37z~5)`i{mMxT|_EsZ?sU zE<+lp4x=+m4#j!mloFZ@7A700P-bEk0XR<@v$u%i5j(F#dKG;kvXMwrbjD@?#{sP( zUy@h@=eD;&&t>8mSo8%# E0I#nTssI20 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5 b/tests-clar/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5 new file mode 100644 index 0000000000000000000000000000000000000000..da4a5edd1879fd8a04095cbe6861992870e543fe GIT binary patch literal 36 scmb4Ghi?@FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nOy zF#rOEr2LW$-IUCtVumHs(%UZVvr+n;>SS`*+Nn?U#2Iy{fyo((dFc=rBo!s*C1-#Q zU7h2y=!MIqwcd`qv)8nn+RbHJ(~YSC$;pEERx@n6yzJ{@cFT@6tUS8aX`zo`8uIhf zax#-kGV{{GX8U#IC8%niTDe;Ax_n!*_r+}w?g>B~oRSs~4i1LPRf~83li~5)$yp&3 zxHIhb>KF0Tph_}}7!=dxG?^+i@;9CHNxmR9b6eG&$A(b5(lVi;(zSLj-(r)gvP-Iy zq}-z|&*WW?+z(Y#l$x7ghT-G;k3VUzlFvUE;yyvfpl#RVjC8-%)vqo*v6&YO051!-Ob*V-0{{R3 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f b/tests-clar/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f new file mode 100644 index 0000000000000000000000000000000000000000..83b489d3af2d92dec412f70ec7f4dcc9f56e8186 GIT binary patch literal 166 zcmV;X09pTd0i}-J4Z<)GgL%#rw*csxeVFMPpUM{o}ZBQEo;}Z}Y@MimyEpwTs zv4Xa7Qx_3H3NG!5xJw*u?j4cqaxmI4QtynmIwi?L-D#1z0(%UZ4aR817}t=FoP6|s zt9j0Zx<@ap=uwBVz&Sl>NpKm;w3ZnT($bGQ$rt?Gj>~j}&Nz*GAH0PvdPLRIcDBl& UGfH0LTF3bcdAx~w15FfCVrz#@_5c6? literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a b/tests-clar/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a new file mode 100644 index 0000000000000000000000000000000000000000..8490346e1a44c34a5cc8749097072053d727e94f GIT binary patch literal 166 zcmV;X09pTd0i}*XuEH=3ME^Rc@C86l(zuC)5F2j51zg9Wf6)eoAg)hA+<@K8d(vp6 zF3W!hH;jM1sRCiLMVz1~_R%q=Atv=D*r*zqHSwT15i+GIDtM?&maZxfw znu0APh0#q&Duy3@t_{xl$t}ZWuFJiyaL|pu(@8(#+xAkIBUm47ipdTLI}C_squbf8 UKhBt`_8Da6^{Uyt06KtB`Og|r)c^nh literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a b/tests-clar/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a new file mode 100644 index 0000000000000000000000000000000000000000..7853e235cb6cc2e4aacfc92f61d976c1b13afbfe GIT binary patch literal 87 zcmV-d0I2_X0ZYosPf{>3wqz*D$ShVU&&>GO3h2mO-)hA%u^`INX;xN=1R;< tQAkb6EP)8-mjZ>jG(dtm`8o=jB?>^zsM4BTTx1wIs)fWz001s1n?-`eB#{6B literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be b/tests-clar/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be new file mode 100644 index 0000000000000000000000000000000000000000..87d808007f0f50037fc92a0ba5c11d7430530327 GIT binary patch literal 291 zcmV+;0o?w00V^p=O;s>9He@g~FfcPQQAo)w(ls<-@D%3Kcpp~vbJ;QhAB%GPx4+`2 zJDC^&0a$?x0 z`)=tV*$IU5=FVw$Xenhg?wkpnVU`-|9!pk?~17_=W$Hj zWxfikAT2W|Ro9R~&i{kvx@Wz-neXRqd%x`T$2Z0LGm%voG2Bd^K1aEhW7{rEHofQj zQ;uKuQvU&QCD;gK1|!A0f!cMtN4D-|X370_LRxDRJ2#R76NXve4Sxyd%I#csKwxT; pyUxF7UsqNlDKKR?*O72#p7pEz>#pryjN1=C$z5^!FaVUJit5r)lRN+b literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf b/tests-clar/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf new file mode 100644 index 0000000000000000000000000000000000000000..974b72dfd16aba70c9dd263a606d7c89f6f8d542 GIT binary patch literal 64 zcmV-G0Kflu0ZYosPf{>4F=r^r$ShV!%gjkt0Mf}BiFxU%DGHf+3b~2JC85H)Aj~FfcPQQAjK;$5Ep%1PBLsVHG!ZFT%(F6jM7 z_U^a6+5eCKdV1*DKd9p5jKsY3)D+#!Jl&+C#JuDTum-;kKJzMH^rtyokpH?VX3;YC zluu%q8gdhhOHzx#8dm4HEPCNGX|1>8?(8+~rgn3g)^x)(=^5eiw>y=j_bNqXJH5365Ycw#dz7683sbmAR6dtv|p literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6 b/tests-clar/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6 new file mode 100644 index 000000000..55f79e066 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6 @@ -0,0 +1,2 @@ +x 0О:JBݟOV +y55jq!4{:p; \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075 b/tests-clar/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075 new file mode 100644 index 0000000000000000000000000000000000000000..bc9350bc0ae4db2b21c363fba25efd2112002d92 GIT binary patch literal 32 ocmb#%dHz)aQ|tRo0kXhC#vbaZ}yyT}ujqZm3Y=++B%$EEZu~wgp|->3lVI>(Gr4_Tbi8u90;O~R0V}V0fNWold~r)jnML^ Oj`-L4^5O@<{^3(s5>KW8 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e b/tests-clar/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e new file mode 100644 index 0000000000000000000000000000000000000000..c63d37fb0c17e54194d10b7b63f254ad28294dca GIT binary patch literal 163 zcmV;U09^lg0i}*n4#FT1ME&LzT%cxwLTO@*Ki{8eCJ#%_ zYm<5$cdatZNd+QJQ=AQ18YHORC%UXs3b2-$@j9!19$pKC;kqQW>iifPqmC&^<(_UAFO` RH=@<`xrXa7i#PWmP;1?^OtSz0 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6 b/tests-clar/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6 new file mode 100644 index 0000000000000000000000000000000000000000..e78c19f1a7fc0d2c60a6f7ec2db9068059635032 GIT binary patch literal 125 zcmV-@0D}K`0qu}W4gx_4L|t$=Ne&I7OQ(?<^m}NE@ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753 b/tests-clar/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753 new file mode 100644 index 0000000000000000000000000000000000000000..34d9aed2078aaf12eecf75dd49070222816386c5 GIT binary patch literal 29 lcmb4X9&s2ELKR%%t=)M(s`-n3YmEd`N<{uCVE@|%EJpEb4d~G literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198 b/tests-clar/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198 new file mode 100644 index 000000000..f748743b8 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198 @@ -0,0 +1 @@ +xKj0)dZ!\@nI,3{XZ?F/\E12zc"#X1][_G&c+9XWKžiUtgS*O۹)u|oXp*" pӤtũ`9zKO\ \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae b/tests-clar/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae new file mode 100644 index 0000000000000000000000000000000000000000..5f0b4e424b070951dcb5fe1350d396c9ad772126 GIT binary patch literal 310 zcmV-60m=S&0V^p=O;s>4Fkvt>FfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chbt=$r*`x=@1P`MTvRI8DI^ob6gg^aGA8$+i`dHns!sWxlC)i zF*P9BAb4*z!=}s2zAk3B>{!Ffqg$O8`Us{WKQApOGr1%)FCA>QUq@bos^+Pcs|Byi zwZ`ve(-wq1`i?q%PFYsgE?1!^!g02>|Q z{BO@DlZN};akrHg$wVG6+TEv#rqbLLta5o(*pm7ER*#$>sEdE>+kK2D=BP88N=t~D zzYf^z1X?EcwqG?9IT+kz{qeK#G^ol_U~qv$4{YR#5BYl5o2EIIq}NUSu=>@7CpPn9 I0c?$+eO|GbjQ{`u literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae b/tests-clar/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae new file mode 100644 index 0000000000000000000000000000000000000000..21ce1a0fc8e574dba18fe5844e053a5d8ba1f42a GIT binary patch literal 320 zcmV-G0l)ru0V^p=O;s?quw*baFfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nPt zDo)Nw%u9!uo>Y{Wmz)9CusX+O(F>PJYrP$JXRm2DwVTVdrW;cOl5+&_t!CJCdD+*+ z?3NvCSb21-(?TD?H00-{WH4yjTF=BcAs?_@;vZ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f b/tests-clar/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f new file mode 100644 index 0000000000000000000000000000000000000000..5a4a9a54ff8c314cd2ad57c89720e64ccdcf51d4 GIT binary patch literal 165 zcmV;W09yZe0i}-J2?8+?gndpGY2aipx%`2MFLq!9o6Uw3%td0v_Kji(zRi3CGefnM zYnN*AyWW^31ZREZq?GkQDS|h_dYv&?=ZMHBgUZk>?$~(ik|RM#Fl$FS0x4^QLmmJ^ zj$zT50@$pxz-M1;ljropmgHrv&D0lEPxnvobCK) TjWBrYlbT|~jaKmnl08t_mX=cO literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2 b/tests-clar/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2 new file mode 100644 index 0000000000000000000000000000000000000000..2aa0c3b9ac075224b59d6b1e498ac5798182e253 GIT binary patch literal 42 ycmbXs^_G$iz?`#c>w^1pW?r literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931 b/tests-clar/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931 new file mode 100644 index 0000000000000000000000000000000000000000..17ad5063d7cc04ca26dea94be689a6d5446dc2d3 GIT binary patch literal 244 zcmV9Gh{F{FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~TNenDnx z3Ru;N5BYl5o2EIIq}NUSu=>@7CpPn9p{j~fbMwm}PDXM;jCICx`6It#&Gav~#{~=5 uWJPUX3fGXAn2X`Z`;R|quaeI{7vera#-MH29wPY|fFfcPQQAkWlNlnqs%+t+HEG|hc(krPbVNkZ=lsq}( ztss|o{^xvCt2f<+Chaigr6u{fsYU6jiAg!BV8!q37w$N~^}qgsLh9eG94WhbC+nPt zDo)Nw%u9!uo>Y{Wmz)9CusX+O(F>PJYrP$JXRm2DwVTVdrW;cOl5+&_t!CJCdD+*+ z?3NvCSb21-(?TD?H00-{3R=>LN#AaSBR8>)GUSckWlkY$Nq`gW$|6GXs1Q~<2U5_*F PW#2{BfYV$6R3ehVI5CG+ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf b/tests-clar/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf new file mode 100644 index 0000000000000000000000000000000000000000..b36bceabf726a44d0d0d4a49070f3f812b52f583 GIT binary patch literal 156 zcmV;N0Av4n0i}-34T2#ML_Jf*HlSHpSs*dSiyhbiKTAFjh(V0)`)TaJ+su2(WO8rq zI>8Wc<`4m=wcE2x+B4e7jFQA!&!ut@Lt0C*jG?M~9CDkG8C}hU-jfj7BGvd@*mJVF za;_0gLYA!Jb1r?rxxC^ia9MhL^cx&xrJrf1x}xwUQv kv@`O;$Kșybh2癈sLA ?R\˷ץ(~Yïb-'Yǎp=Njq˟m[zO+ \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396 b/tests-clar/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396 new file mode 100644 index 0000000000000000000000000000000000000000..30e07e5b71907affcaa1fe169114c2091179b9f9 GIT binary patch literal 76 zcmV-S0JHyi0ZYosPf{>4F=Z&p$ShV!%gjkt0Mf}BiFxU%DGHf+3b~2JC8`5|TmSkbE%WNtM?olhxoR=Q;oZ0!L$zf_1L1 zwVPrJinUWD=+)*@o_Yw-ci3{MpqXiz%< literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03 b/tests-clar/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03 new file mode 100644 index 0000000000000000000000000000000000000000..4f1e7268818a62a5d3fdf8ec89d1ec7a62624e79 GIT binary patch literal 264 zcmV+j0r&oR0V^p=O;s>9vt%$dFfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRQEF~} z8N@N>V72F-g&m%>mNk6yst3OvMM@8`J(`q{sTRrM_aA@KUL~J@F2sF;j6vJ3#~Jss z?_z2|a(Ik&#&Y>1zhcewFSo}93)f^tZC?u2PznqUaNvNQa^ge2p7o|_jwR`J6F;ne Ob>WH4yjTEmHI#L$M0@uD literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08 b/tests-clar/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08 new file mode 100644 index 0000000000000000000000000000000000000000..be8a810cd868bbbd5bcf1e52b507985151ed663f GIT binary patch literal 29 lcmbJ%v0V^p=O;xb8G+{6_FfcPQQ7}l<&CAzK&PdElPt_}_C}GGrcEX!eQtkLn zu4T(-Yaia7b?0m!Ol^{GQEGWnW=TnEo^EEIZhmP|F<8%sbJ~5LZ5+O{4f?mMb3PF{ zWViJhOiwbZo|25zOsFpXFd;_vYYjrPVy92ss(4zic+p!!s4l}qpsu{c+|(49J-VqW znI#Y|k`ae5=)Lb$IDB|J>J^4Jn-w? z*#6lrp$ZGlpn8muZB5EA$pC8*?{wG_@X2Cn4sYr9sWDrtEhHtIpc;%Z11BXlClxbv zBu*P<%e-IkW7(8^J_V^hsr;6xoiH=7`4!0&xM$52XM36zx}Da`5)IwD^7WMqMlDmJ zW|&~QBQYffBaW2HCvDYQ#jV`^is4ONz-*=@d$Asv3D{f#Hv{g58CP}L4{X@|X-lfd zw^czxQxBaEy$vk^=vaNJ3psPGU;0P7weA literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66 b/tests-clar/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66 new file mode 100644 index 0000000000000000000000000000000000000000..21e6b2c558d523a599df3883b7c13b0080096328 GIT binary patch literal 279 zcmV+y0qFjC0V^p=O;s>9H(@X|FfcPQQAjK;$5Ep%1PBLsVHGc5;`oqI?hAM z{rdDef8NUpvTr_GLlq}yB<7{3rs!to=_VB=<|Su>F;6I8oGd50*9~fRUTV2+ zVqS`FN@iMGYEf!l30T8_&kGfq%~IDBci(!&l+gC0V)kj6SBp|}^UE+?fB*3(?N##m z=R({k$QZQkdYo}D`!3W5Bn?QekFm~JE`Q`#teO7h_PAi-nyjepOQ9M{fk6TeA+S?U de8|_c-ZagzB)x9pht;nxJh7P<3jkUVmYXeUg!BLa literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a b/tests-clar/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a new file mode 100644 index 000000000..2f9d83b26 --- /dev/null +++ b/tests-clar/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a @@ -0,0 +1,2 @@ +xK!D]s +.{`cx/ɸ`0 oURy|Y`dPA!4C2d=x#e`BgrubLffG@՗-}KԲUy=];)r0 R$(%o=׶OPw \ No newline at end of file diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87 b/tests-clar/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87 new file mode 100644 index 0000000000000000000000000000000000000000..4ce7d2297be2cd33aee824f46f5a127f4c56ca98 GIT binary patch literal 48 zcmV-00MGw;0ZYosPf{>8V(`sR$xO>kO;O0qQ&2A{$}G!F%+WP8)J-Z%%uCKt=K=r< GkPO-w^A&6W literal 0 HcmV?d00001 diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00 b/tests-clar/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00 new file mode 100644 index 0000000000000000000000000000000000000000..eada39b77e4dc3894bb405dbb2c7d93d0d6ab6a7 GIT binary patch literal 24 gcmb Date: Tue, 30 Apr 2013 14:56:41 -0500 Subject: [PATCH 115/384] renames! --- include/git2/index.h | 53 ++++ include/git2/merge.h | 13 + src/diff.h | 12 + src/diff_tform.c | 18 +- src/index.c | 218 ++++++++++++++ src/index.h | 1 + src/merge.c | 485 +++++++++++++++++++++++++++++- tests-clar/index/names.c | 83 +++++ tests-clar/merge/merge_helpers.c | 87 +++++- tests-clar/merge/trees/renames.c | 253 ++++++++++++++++ tests-clar/merge/trees/treediff.c | 275 +++++++++++++++++ 11 files changed, 1487 insertions(+), 11 deletions(-) create mode 100644 tests-clar/index/names.c create mode 100644 tests-clar/merge/trees/renames.c diff --git a/include/git2/index.h b/include/git2/index.h index fcfe4be3d..01e3d2c29 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -84,6 +84,12 @@ typedef struct git_index_entry { char *path; } git_index_entry; +typedef struct git_index_name_entry { + char *ancestor; + char *ours; + char *theirs; +} git_index_name_entry; + /** Representation of a resolve undo entry in the index. */ typedef struct git_index_reuc_entry { unsigned int mode[3]; @@ -478,6 +484,53 @@ GIT_EXTERN(int) git_index_has_conflicts(const git_index *index); /**@}*/ +/** @name Conflict Name entry functions + * + * These functions work on rename conflict entries. + */ +/**@{*/ + +/** + * Get the count of filename conflict entries currently in the index. + * + * @param index an existing index object + * @return integer of count of current filename conflict entries + */ +GIT_EXTERN(unsigned int) git_index_name_entrycount(git_index *index); + +/** + * Get a filename conflict entry from the index. + * + * The returned entry is read-only and should not be modified + * or freed by the caller. + * + * @param index an existing index object + * @param n the position of the entry + * @return a pointer to the filename conflict entry; NULL if out of bounds + */ +GIT_EXTERN(const git_index_name_entry *) git_index_name_get_byindex( + git_index *index, size_t n); + +/** + * Record the filenames involved in a rename conflict. + * + * @param index an existing index object + * @param ancestor the path of the file as it existed in the ancestor + * @param ours the path of the file as it existed in our tree + * @param theirs the path of the file as it existed in their tree + */ +GIT_EXTERN(int) git_index_name_add(git_index *index, + const char *ancestor, const char *ours, const char *theirs); + +/** + * Remove all filename conflict entries. + * + * @param index an existing index object + * @return 0 or an error code + */ +GIT_EXTERN(void) git_index_name_clear(git_index *index); +/**@}*/ + /** @name Resolve Undo (REUC) index entry manipulation. * * These functions work on the Resolve Undo index extension and contains diff --git a/include/git2/merge.h b/include/git2/merge.h index 43a61f0e4..8ca90b95f 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -27,6 +27,8 @@ GIT_BEGIN_DECL * passed in via the `flags` value in the `git_diff_tree_many_options`. */ typedef enum { + /** Detect renames */ + GIT_MERGE_TREE_FIND_RENAMES = (1 << 0), } git_merge_tree_flags; /** @@ -44,6 +46,17 @@ typedef struct { unsigned int version; git_merge_tree_flags flags; + /** Similarity to consider a file renamed (default 50) */ + unsigned int rename_threshold; + + /** Maximum similarity sources to examine (overrides the + * `merge.renameLimit` config) (default 200) + */ + unsigned int target_limit; + + /** Pluggable similarity metric; pass NULL to use internal metric */ + git_diff_similarity_metric *metric; + /** Flags for automerging content. */ git_merge_automerge_flags automerge_flags; } git_merge_tree_opts; diff --git a/src/diff.h b/src/diff.h index 8e3cbcd46..48e20d1e3 100644 --- a/src/diff.h +++ b/src/diff.h @@ -74,5 +74,17 @@ extern int git_diff__from_iterators( git_iterator *new_iter, const git_diff_options *opts); +int git_diff_find_similar__hashsig_for_file( + void **out, const git_diff_file *f, const char *path, void *p); + +int git_diff_find_similar__hashsig_for_buf( + void **out, const git_diff_file *f, const char *buf, size_t len, void *p); + +void git_diff_find_similar__hashsig_free(void *sig, void *payload); + +int git_diff_find_similar__calc_similarity( + int *score, void *siga, void *sigb, void *payload); + + #endif diff --git a/src/diff_tform.c b/src/diff_tform.c index 5c1a86cb9..201a0e896 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -170,7 +170,7 @@ int git_diff_merge( return error; } -static int find_similar__hashsig_for_file( +int git_diff_find_similar__hashsig_for_file( void **out, const git_diff_file *f, const char *path, void *p) { git_hashsig_option_t opt = (git_hashsig_option_t)p; @@ -187,12 +187,12 @@ static int find_similar__hashsig_for_file( return error; } -static int find_similar__hashsig_for_buf( +int git_diff_find_similar__hashsig_for_buf( void **out, const git_diff_file *f, const char *buf, size_t len, void *p) { git_hashsig_option_t opt = (git_hashsig_option_t)p; int error = 0; - + GIT_UNUSED(f); error = git_hashsig_create((git_hashsig **)out, buf, len, opt); @@ -204,13 +204,13 @@ static int find_similar__hashsig_for_buf( return error; } -static void find_similar__hashsig_free(void *sig, void *payload) +void git_diff_find_similar__hashsig_free(void *sig, void *payload) { GIT_UNUSED(payload); git_hashsig_free(sig); } -static int find_similar__calc_similarity( +int git_diff_find_similar__calc_similarity( int *score, void *siga, void *sigb, void *payload) { GIT_UNUSED(payload); @@ -291,10 +291,10 @@ static int normalize_find_opts( opts->metric = git__malloc(sizeof(git_diff_similarity_metric)); GITERR_CHECK_ALLOC(opts->metric); - opts->metric->file_signature = find_similar__hashsig_for_file; - opts->metric->buffer_signature = find_similar__hashsig_for_buf; - opts->metric->free_signature = find_similar__hashsig_free; - opts->metric->similarity = find_similar__calc_similarity; + opts->metric->file_signature = git_diff_find_similar__hashsig_for_file; + opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf; + opts->metric->free_signature = git_diff_find_similar__hashsig_free; + opts->metric->similarity = git_diff_find_similar__calc_similarity; if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE) opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE; diff --git a/src/index.c b/src/index.c index fe3a2104b..656fb5bc5 100644 --- a/src/index.c +++ b/src/index.c @@ -35,6 +35,7 @@ static const unsigned int INDEX_VERSION_NUMBER_EXT = 3; static const unsigned int INDEX_HEADER_SIG = 0x44495243; static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'}; static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'}; +static const char INDEX_EXT_CONFLICT_NAME_SIG[] = {'N', 'A', 'M', 'E'}; #define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx))) @@ -187,6 +188,46 @@ static int index_icmp(const void *a, const void *b) return diff; } +static int conflict_name_cmp(const void *a, const void *b) +{ + const git_index_name_entry *name_a = a; + const git_index_name_entry *name_b = b; + + if (name_a->ancestor && !name_b->ancestor) + return 1; + + if (!name_a->ancestor && name_b->ancestor) + return -1; + + if (name_a->ancestor) + return strcmp(name_a->ancestor, name_b->ancestor); + + if (!name_a->ours || !name_b->ours) + return 0; + + return strcmp(name_a->ours, name_b->ours); +} + +static int conflict_name_icmp(const void *a, const void *b) +{ + const git_index_name_entry *name_a = a; + const git_index_name_entry *name_b = b; + + if (name_a->ancestor && !name_b->ancestor) + return 1; + + if (!name_a->ancestor && name_b->ancestor) + return -1; + + if (name_a->ancestor) + return strcasecmp(name_a->ancestor, name_b->ancestor); + + if (!name_a->ours || !name_b->ours) + return 0; + + return strcasecmp(name_a->ours, name_b->ours); +} + static int reuc_srch(const void *key, const void *array_member) { const git_index_reuc_entry *reuc = array_member; @@ -278,6 +319,7 @@ int git_index_open(git_index **index_out, const char *index_path) } if (git_vector_init(&index->entries, 32, index_cmp) < 0 || + git_vector_init(&index->names, 32, conflict_name_cmp) < 0 || git_vector_init(&index->reuc, 32, reuc_cmp) < 0) return -1; @@ -331,6 +373,8 @@ void git_index_clear(git_index *index) git_index_reuc_clear(index); + git_index_name_clear(index); + git_futils_filestamp_set(&index->stamp, NULL); git_tree_cache_free(index->tree); @@ -1042,6 +1086,72 @@ int git_index_has_conflicts(const git_index *index) return 0; } +unsigned int git_index_name_entrycount(git_index *index) +{ + assert(index); + return (unsigned int)index->names.length; +} + +const git_index_name_entry *git_index_name_get_byindex( + git_index *index, size_t n) +{ + assert(index); + + git_vector_sort(&index->names); + return git_vector_get(&index->names, n); +} + +int git_index_name_add(git_index *index, + const char *ancestor, const char *ours, const char *theirs) +{ + git_index_name_entry *conflict_name; + + assert ((ancestor && ours) || (ancestor && theirs) || (ours && theirs)); + + conflict_name = git__calloc(1, sizeof(git_index_name_entry)); + GITERR_CHECK_ALLOC(conflict_name); + + if (ancestor) { + conflict_name->ancestor = git__strdup(ancestor); + GITERR_CHECK_ALLOC(conflict_name->ancestor); + } + + if (ours) { + conflict_name->ours = git__strdup(ours); + GITERR_CHECK_ALLOC(conflict_name->ours); + } + + if (theirs) { + conflict_name->theirs = git__strdup(theirs); + GITERR_CHECK_ALLOC(conflict_name->theirs); + } + + return git_vector_insert(&index->names, conflict_name); +} + +void git_index_name_clear(git_index *index) +{ + size_t i; + git_index_name_entry *conflict_name; + + assert(index); + + git_vector_foreach(&index->names, i, conflict_name) { + if (conflict_name->ancestor) + git__free(conflict_name->ancestor); + + if (conflict_name->ours) + git__free(conflict_name->ours); + + if (conflict_name->theirs) + git__free(conflict_name->theirs); + + git__free(conflict_name); + } + + git_vector_clear(&index->names); +} + unsigned int git_index_reuc_entrycount(git_index *index) { assert(index); @@ -1228,6 +1338,52 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) return 0; } + +static int read_conflict_names(git_index *index, const char *buffer, size_t size) +{ + size_t len; + + /* This gets called multiple times, the vector might already be initialized */ + if (index->names._alloc_size == 0 && + git_vector_init(&index->names, 16, conflict_name_cmp) < 0) + return -1; + +#define read_conflict_name(ptr) \ + len = strlen(buffer) + 1; \ + if (size < len) \ + return index_error_invalid("reading conflict name entries"); \ + \ + if (len == 1) \ + ptr = NULL; \ + else { \ + ptr = git__malloc(len); \ + GITERR_CHECK_ALLOC(ptr); \ + memcpy(ptr, buffer, len); \ + } \ + \ + buffer += len; \ + size -= len; + + while (size) { + git_index_name_entry *conflict_name = git__calloc(1, sizeof(git_index_name_entry)); + GITERR_CHECK_ALLOC(conflict_name); + + read_conflict_name(conflict_name->ancestor); + read_conflict_name(conflict_name->ours); + read_conflict_name(conflict_name->theirs); + + if (git_vector_insert(&index->names, conflict_name) < 0) + return -1; + } + +#undef read_conflict_name + + /* entries are guaranteed to be sorted on-disk */ + index->names.sorted = 1; + + return 0; +} + static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size) { size_t path_length, entry_size; @@ -1332,6 +1488,9 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer } else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) { if (read_reuc(index, buffer + 8, dest.extension_size) < 0) return 0; + } else if (memcmp(dest.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4) == 0) { + if (read_conflict_names(index, buffer + 8, dest.extension_size) < 0) + return 0; } /* else, unsupported extension. We cannot parse this, but we can skip * it by returning `total_size */ @@ -1545,6 +1704,61 @@ static int write_extension(git_filebuf *file, struct index_extension *header, gi return error; } +static int create_name_extension_data(git_buf *name_buf, git_index_name_entry *conflict_name) +{ + int error = 0; + + if (conflict_name->ancestor == NULL) + error = git_buf_put(name_buf, "\0", 1); + else + error = git_buf_put(name_buf, conflict_name->ancestor, strlen(conflict_name->ancestor) + 1); + + if (error != 0) + goto on_error; + + if (conflict_name->ours == NULL) + error = git_buf_put(name_buf, "\0", 1); + else + error = git_buf_put(name_buf, conflict_name->ours, strlen(conflict_name->ours) + 1); + + if (error != 0) + goto on_error; + + if (conflict_name->theirs == NULL) + error = git_buf_put(name_buf, "\0", 1); + else + error = git_buf_put(name_buf, conflict_name->theirs, strlen(conflict_name->theirs) + 1); + +on_error: + return error; +} + +static int write_name_extension(git_index *index, git_filebuf *file) +{ + git_buf name_buf = GIT_BUF_INIT; + git_vector *out = &index->names; + git_index_name_entry *conflict_name; + struct index_extension extension; + size_t i; + int error = 0; + + git_vector_foreach(out, i, conflict_name) { + if ((error = create_name_extension_data(&name_buf, conflict_name)) < 0) + goto done; + } + + memset(&extension, 0x0, sizeof(struct index_extension)); + memcpy(&extension.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4); + extension.extension_size = (uint32_t)name_buf.size; + + error = write_extension(file, &extension, &name_buf); + + git_buf_free(&name_buf); + +done: + return error; +} + static int create_reuc_extension_data(git_buf *reuc_buf, git_index_reuc_entry *reuc) { int i; @@ -1615,6 +1829,10 @@ static int write_index(git_index *index, git_filebuf *file) /* TODO: write tree cache extension */ + /* write the rename conflict extension */ + if (index->names.length > 0 && write_name_extension(index, file) < 0) + return -1; + /* write the reuc extension */ if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0) return -1; diff --git a/src/index.h b/src/index.h index 9498907b6..2ad401741 100644 --- a/src/index.h +++ b/src/index.h @@ -33,6 +33,7 @@ struct git_index { git_tree_cache *tree; + git_vector names; git_vector reuc; git_vector_cmp entries_cmp_path; diff --git a/src/merge.c b/src/merge.c index 8df156abe..681f302f4 100644 --- a/src/merge.c +++ b/src/merge.c @@ -23,6 +23,7 @@ #include "merge_file.h" #include "blob.h" #include "hashsig.h" +#include "oid.h" #include "git2/types.h" #include "git2/repository.h" @@ -640,6 +641,440 @@ done: return error; } +/* Rename detection and coalescing */ + +struct merge_diff_similarity { + unsigned char similarity; + size_t other_idx; +}; + +static int index_entry_similarity_exact( + git_repository *repo, + git_index_entry *a, + size_t a_idx, + git_index_entry *b, + size_t b_idx, + void **cache, + const git_merge_tree_opts *opts) +{ + GIT_UNUSED(repo); + GIT_UNUSED(a_idx); + GIT_UNUSED(b_idx); + GIT_UNUSED(cache); + GIT_UNUSED(opts); + + if (git_oid__cmp(&a->oid, &b->oid) == 0) + return 100; + + return 0; +} + +static int index_entry_similarity_calc( + void **out, + git_repository *repo, + git_index_entry *entry, + const git_merge_tree_opts *opts) +{ + git_blob *blob; + git_diff_file diff_file = {{{0}}}; + int error; + + *out = NULL; + + if ((error = git_blob_lookup(&blob, repo, &entry->oid)) < 0) + return error; + + git_oid_cpy(&diff_file.oid, &entry->oid); + diff_file.path = entry->path; + diff_file.size = entry->file_size; + diff_file.mode = entry->mode; + diff_file.flags = 0; + + error = opts->metric->buffer_signature(out, &diff_file, + git_blob_rawcontent(blob), git_blob_rawsize(blob), + opts->metric->payload); + + git_blob_free(blob); + + return error; +} + +static int index_entry_similarity_inexact( + git_repository *repo, + git_index_entry *a, + size_t a_idx, + git_index_entry *b, + size_t b_idx, + void **cache, + const git_merge_tree_opts *opts) +{ + int score = 0; + int error = 0; + + if (GIT_MODE_TYPE(a->mode) != GIT_MODE_TYPE(b->mode)) + return 0; + + /* update signature cache if needed */ + if (!cache[a_idx] && (error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0) + return error; + if (!cache[b_idx] && (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0) + return error; + + /* some metrics may not wish to process this file (too big / too small) */ + if (!cache[a_idx] || !cache[b_idx]) + return 0; + + /* compare signatures */ + if (opts->metric->similarity( + &score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0) + return -1; + + /* clip score */ + if (score < 0) + score = 0; + else if (score > 100) + score = 100; + + return score; +} + +static int merge_diff_mark_similarity( + git_repository *repo, + git_merge_diff_list *diff_list, + struct merge_diff_similarity *similarity_ours, + struct merge_diff_similarity *similarity_theirs, + int (*similarity_fn)(git_repository *, git_index_entry *, size_t, git_index_entry *, size_t, void **, const git_merge_tree_opts *), + void **cache, + const git_merge_tree_opts *opts) +{ + size_t i, j; + git_merge_diff *conflict_src, *conflict_tgt; + int similarity; + + git_vector_foreach(&diff_list->conflicts, i, conflict_src) { + /* Items can be the source of a rename iff they have an item in the + * ancestor slot and lack an item in the ours or theirs slot. */ + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry) || + (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry) && + GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry))) + continue; + + git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) { + size_t our_idx = diff_list->conflicts.length + j; + size_t their_idx = (diff_list->conflicts.length * 2) + j; + + if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry)) + continue; + + if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) && + !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) { + similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts); + + if (similarity == GIT_EBUFS) + continue; + else if (similarity < 0) + return similarity; + + if (similarity > similarity_ours[i].similarity && + similarity > similarity_ours[j].similarity) { + /* Clear previous best similarity */ + if (similarity_ours[i].similarity > 0) + similarity_ours[similarity_ours[i].other_idx].similarity = 0; + + if (similarity_ours[j].similarity > 0) + similarity_ours[similarity_ours[j].other_idx].similarity = 0; + + similarity_ours[i].similarity = similarity; + similarity_ours[i].other_idx = j; + + similarity_ours[j].similarity = similarity; + similarity_ours[j].other_idx = i; + } + } + + if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) && + !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) { + similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts); + + if (similarity > similarity_theirs[i].similarity && + similarity > similarity_theirs[j].similarity) { + /* Clear previous best similarity */ + if (similarity_theirs[i].similarity > 0) + similarity_theirs[similarity_theirs[i].other_idx].similarity = 0; + + if (similarity_theirs[j].similarity > 0) + similarity_theirs[similarity_theirs[j].other_idx].similarity = 0; + + similarity_theirs[i].similarity = similarity; + similarity_theirs[i].other_idx = j; + + similarity_theirs[j].similarity = similarity; + similarity_theirs[j].other_idx = i; + } + } + } + } + + return 0; +} + +/* + * Rename conflicts: + * + * Ancestor Ours Theirs + * + * 0a A A A No rename + * b A A* A No rename (ours was rewritten) + * c A A A* No rename (theirs rewritten) + * 1a A A B[A] Rename or rename/edit + * b A B[A] A (automergeable) + * 2 A B[A] B[A] Both renamed (automergeable) + * 3a A B[A] Rename/delete + * b A B[A] (same) + * 4a A B[A] B Rename/add [B~ours B~theirs] + * b A B B[A] (same) + * 5 A B[A] C[A] Both renamed ("1 -> 2") + * 6 A C[A] Both renamed ("2 -> 1") + * B C[B] [C~ours C~theirs] (automergeable) + */ +static void merge_diff_mark_rename_conflict( + git_merge_diff_list *diff_list, + struct merge_diff_similarity *similarity_ours, + bool ours_renamed, + size_t ours_source_idx, + struct merge_diff_similarity *similarity_theirs, + bool theirs_renamed, + size_t theirs_source_idx, + git_merge_diff *target, + const git_merge_tree_opts *opts) +{ + git_merge_diff *ours_source = NULL, *theirs_source = NULL; + + if (ours_renamed) + ours_source = diff_list->conflicts.contents[ours_source_idx]; + + if (theirs_renamed) + theirs_source = diff_list->conflicts.contents[theirs_source_idx]; + + /* Detect 2->1 conflicts */ + if (ours_renamed && theirs_renamed) { + /* Both renamed to the same target name. */ + if (ours_source_idx == theirs_source_idx) + ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED; + else { + ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1; + theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1; + } + } else if (ours_renamed) { + /* If our source was also renamed in theirs, this is a 1->2 */ + if (similarity_theirs[ours_source_idx].similarity >= opts->rename_threshold) + ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2; + + else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry)) { + ours_source->type = GIT_MERGE_DIFF_RENAMED_ADDED; + target->type = GIT_MERGE_DIFF_RENAMED_ADDED; + } + + else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(ours_source->their_entry)) + ours_source->type = GIT_MERGE_DIFF_RENAMED_DELETED; + + else if (ours_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED) + ours_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED; + } else if (theirs_renamed) { + /* If their source was also renamed in ours, this is a 1->2 */ + if (similarity_ours[theirs_source_idx].similarity >= opts->rename_threshold) + theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2; + + else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry)) { + theirs_source->type = GIT_MERGE_DIFF_RENAMED_ADDED; + target->type = GIT_MERGE_DIFF_RENAMED_ADDED; + } + + else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(theirs_source->our_entry)) + theirs_source->type = GIT_MERGE_DIFF_RENAMED_DELETED; + + else if (theirs_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED) + theirs_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED; + } +} + +GIT_INLINE(void) merge_diff_coalesce_rename( + git_index_entry *source_entry, + git_delta_t *source_status, + git_index_entry *target_entry, + git_delta_t *target_status) +{ + /* Coalesce the rename target into the rename source. */ + memcpy(source_entry, target_entry, sizeof(git_index_entry)); + *source_status = GIT_DELTA_RENAMED; + + memset(target_entry, 0x0, sizeof(git_index_entry)); + *target_status = GIT_DELTA_UNMODIFIED; +} + +static void merge_diff_list_coalesce_renames( + git_merge_diff_list *diff_list, + struct merge_diff_similarity *similarity_ours, + struct merge_diff_similarity *similarity_theirs, + const git_merge_tree_opts *opts) +{ + size_t i; + bool ours_renamed = 0, theirs_renamed = 0; + size_t ours_source_idx = 0, theirs_source_idx = 0; + git_merge_diff *ours_source, *theirs_source, *target; + + for (i = 0; i < diff_list->conflicts.length; i++) { + target = diff_list->conflicts.contents[i]; + + ours_renamed = 0; + theirs_renamed = 0; + + if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry) && + similarity_ours[i].similarity >= opts->rename_threshold) { + ours_source_idx = similarity_ours[i].other_idx; + + ours_source = diff_list->conflicts.contents[ours_source_idx]; + + merge_diff_coalesce_rename( + &ours_source->our_entry, + &ours_source->our_status, + &target->our_entry, + &target->our_status); + + similarity_ours[ours_source_idx].similarity = 0; + similarity_ours[i].similarity = 0; + + ours_renamed = 1; + } + + /* insufficient to determine direction */ + if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry) && + similarity_theirs[i].similarity >= opts->rename_threshold) { + theirs_source_idx = similarity_theirs[i].other_idx; + + theirs_source = diff_list->conflicts.contents[theirs_source_idx]; + + merge_diff_coalesce_rename( + &theirs_source->their_entry, + &theirs_source->their_status, + &target->their_entry, + &target->their_status); + + similarity_theirs[theirs_source_idx].similarity = 0; + similarity_theirs[i].similarity = 0; + + theirs_renamed = 1; + } + + merge_diff_mark_rename_conflict(diff_list, + similarity_ours, ours_renamed, ours_source_idx, + similarity_theirs, theirs_renamed, theirs_source_idx, + target, opts); + } +} + +static int merge_diff_empty(const git_vector *conflicts, size_t idx) +{ + git_merge_diff *conflict = conflicts->contents[idx]; + + return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) && + !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) && + !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)); +} + +static void merge_diff_list_count_candidates( + git_merge_diff_list *diff_list, + size_t *src_count, + size_t *tgt_count) +{ + git_merge_diff *entry; + size_t i; + + *src_count = 0; + *tgt_count = 0; + + git_vector_foreach(&diff_list->conflicts, i, entry) { + if (GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry) && + (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->our_entry) || + !GIT_MERGE_INDEX_ENTRY_EXISTS(entry->their_entry))) + src_count++; + else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry)) + tgt_count++; + } +} + +int git_merge_diff_list__find_renames( + git_repository *repo, + git_merge_diff_list *diff_list, + const git_merge_tree_opts *opts) +{ + struct merge_diff_similarity *similarity_ours, *similarity_theirs; + void **cache = NULL; + size_t cache_size = 0; + size_t src_count, tgt_count, i; + int error = 0; + + assert(diff_list && opts); + + if ((opts->flags & GIT_MERGE_TREE_FIND_RENAMES) == 0) + return 0; + + similarity_ours = git__calloc(diff_list->conflicts.length, + sizeof(struct merge_diff_similarity)); + GITERR_CHECK_ALLOC(similarity_ours); + + similarity_theirs = git__calloc(diff_list->conflicts.length, + sizeof(struct merge_diff_similarity)); + GITERR_CHECK_ALLOC(similarity_theirs); + + /* Calculate similarity between items that were deleted from the ancestor + * and added in the other branch. + */ + if ((error = merge_diff_mark_similarity(repo, diff_list, similarity_ours, + similarity_theirs, index_entry_similarity_exact, NULL, opts)) < 0) + goto done; + + if (diff_list->conflicts.length <= opts->target_limit) { + cache_size = diff_list->conflicts.length * 3; + cache = git__calloc(cache_size, sizeof(void *)); + GITERR_CHECK_ALLOC(cache); + + merge_diff_list_count_candidates(diff_list, &src_count, &tgt_count); + + if (src_count > opts->target_limit || tgt_count > opts->target_limit) { + /* TODO: report! */ + } else { + if ((error = merge_diff_mark_similarity( + repo, diff_list, similarity_ours, similarity_theirs, + index_entry_similarity_inexact, cache, opts)) < 0) + goto done; + } + } + + /* For entries that are appropriately similar, merge the new name's entry + * into the old name. + */ + merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts); + + /* And remove any entries that were merged and are now empty. */ + git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty); + +done: + if (cache != NULL) { + for (i = 0; i < cache_size; ++i) { + if (cache[i] != NULL) + opts->metric->free_signature(cache[i], opts->metric->payload); + } + + git__free(cache); + } + + git__free(similarity_ours); + git__free(similarity_theirs); + + return error; +} + /* Directory/file conflict handling */ GIT_INLINE(const char *) merge_diff_path( @@ -951,6 +1386,43 @@ static int merge_tree_normalize_opts( else { git_merge_tree_opts init = GIT_MERGE_TREE_OPTS_INIT; memcpy(opts, &init, sizeof(init)); + + opts->flags = GIT_MERGE_TREE_FIND_RENAMES; + opts->rename_threshold = GIT_MERGE_TREE_RENAME_THRESHOLD; + } + + if (!opts->target_limit) { + int32_t limit = 0; + + opts->target_limit = GIT_MERGE_TREE_TARGET_LIMIT; + + if (git_config_get_int32(&limit, cfg, "merge.renameLimit") < 0) { + giterr_clear(); + + if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0) + giterr_clear(); + } + + if (limit > 0) + opts->target_limit = limit; + } + + /* assign the internal metric with whitespace flag as payload */ + if (!opts->metric) { + opts->metric = git__malloc(sizeof(git_diff_similarity_metric)); + GITERR_CHECK_ALLOC(opts->metric); + + opts->metric->file_signature = git_diff_find_similar__hashsig_for_file; + opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf; + opts->metric->free_signature = git_diff_find_similar__hashsig_free; + opts->metric->similarity = git_diff_find_similar__calc_similarity; + + if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE) + opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE; + else if (opts->flags & GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE) + opts->metric->payload = (void *)GIT_HASHSIG_NORMAL; + else + opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE; } return 0; @@ -1035,6 +1507,12 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) their_path = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? conflict->their_entry.path : NULL; + + if ((our_path && strcmp(ancestor_path, our_path) != 0) || + (their_path && strcmp(ancestor_path, their_path) != 0)) { + if ((error = git_index_name_add(index, ancestor_path, our_path, their_path)) < 0) + goto on_error; + } } /* Add each entry in the resolved conflict to the REUC independently, since @@ -1099,7 +1577,8 @@ int git_merge_trees( diff_list = git_merge_diff_list__alloc(repo); GITERR_CHECK_ALLOC(diff_list); - if ((error = git_merge_diff_list__find_differences(diff_list, ancestor_tree, our_tree, their_tree)) < 0) + if ((error = git_merge_diff_list__find_differences(diff_list, ancestor_tree, our_tree, their_tree)) < 0 || + (error = git_merge_diff_list__find_renames(repo, diff_list, &opts)) < 0) goto done; memcpy(&changes, &diff_list->conflicts, sizeof(git_vector)); @@ -1115,6 +1594,9 @@ int git_merge_trees( git_vector_insert(&diff_list->conflicts, conflict); } + if (!given_opts || !given_opts->metric) + git__free(opts.metric); + error = index_from_diff_list(out, diff_list); done: @@ -1134,3 +1616,4 @@ void git_merge_diff_list__free(git_merge_diff_list *diff_list) git_pool_clear(&diff_list->pool); git__free(diff_list); } + diff --git a/tests-clar/index/names.c b/tests-clar/index/names.c new file mode 100644 index 000000000..ffc984249 --- /dev/null +++ b/tests-clar/index/names.c @@ -0,0 +1,83 @@ +#include "clar_libgit2.h" +#include "index.h" +#include "git2/repository.h" +#include "../reset/reset_helpers.h" + +static git_repository *repo; +static git_index *repo_index; + +#define TEST_REPO_PATH "mergedrepo" +#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" + +// Fixture setup and teardown +void test_index_names__initialize(void) +{ + repo = cl_git_sandbox_init("mergedrepo"); + git_repository_index(&repo_index, repo); +} + +void test_index_names__cleanup(void) +{ + git_index_free(repo_index); + repo_index = NULL; + + cl_git_sandbox_cleanup(); +} + +void test_index_names__add(void) +{ + const git_index_name_entry *conflict_name; + + cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs")); + cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL)); + cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3")); + + cl_assert(git_index_name_entrycount(repo_index) == 3); + + conflict_name = git_index_name_get_byindex(repo_index, 0); + cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0); + cl_assert(strcmp(conflict_name->ours, "ours") == 0); + cl_assert(strcmp(conflict_name->theirs, "theirs") == 0); + + conflict_name = git_index_name_get_byindex(repo_index, 1); + cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0); + cl_assert(strcmp(conflict_name->ours, "ours2") == 0); + cl_assert(conflict_name->theirs == NULL); + + conflict_name = git_index_name_get_byindex(repo_index, 2); + cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0); + cl_assert(conflict_name->ours == NULL); + cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0); +} + +void test_index_names__roundtrip(void) +{ + const git_index_name_entry *conflict_name; + + cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs")); + cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL)); + cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3")); + + cl_git_pass(git_index_write(repo_index)); + git_index_clear(repo_index); + cl_assert(git_index_name_entrycount(repo_index) == 0); + + cl_git_pass(git_index_read(repo_index)); + cl_assert(git_index_name_entrycount(repo_index) == 3); + + conflict_name = git_index_name_get_byindex(repo_index, 0); + cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0); + cl_assert(strcmp(conflict_name->ours, "ours") == 0); + cl_assert(strcmp(conflict_name->theirs, "theirs") == 0); + + conflict_name = git_index_name_get_byindex(repo_index, 1); + cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0); + cl_assert(strcmp(conflict_name->ours, "ours2") == 0); + cl_assert(conflict_name->theirs == NULL); + + conflict_name = git_index_name_get_byindex(repo_index, 2); + cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0); + cl_assert(conflict_name->ours == NULL); + cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0); + +} diff --git a/tests-clar/merge/merge_helpers.c b/tests-clar/merge/merge_helpers.c index b10e9ec32..7cb1e53da 100644 --- a/tests-clar/merge/merge_helpers.c +++ b/tests-clar/merge/merge_helpers.c @@ -51,10 +51,62 @@ int merge_trees_from_branches( return 0; } +static void dump_index_entries(git_vector *index_entries) +{ + size_t i; + const git_index_entry *index_entry; + + printf ("\nINDEX [%d]:\n", (int)index_entries->length); + for (i = 0; i < index_entries->length; i++) { + index_entry = index_entries->contents[i]; + + printf("%o ", index_entry->mode); + printf("%s ", git_oid_allocfmt(&index_entry->oid)); + printf("%d ", git_index_entry_stage(index_entry)); + printf("%s ", index_entry->path); + printf("\n"); + } + printf("\n"); +} + +static void dump_names(git_index *index) +{ + size_t i; + const git_index_name_entry *conflict_name; + + for (i = 0; i < git_index_name_entrycount(index); i++) { + conflict_name = git_index_name_get_byindex(index, i); + + printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs); + } + printf("\n"); +} + +static void dump_reuc(git_index *index) +{ + size_t i; + const git_index_reuc_entry *reuc; + + printf ("\nREUC:\n"); + for (i = 0; i < git_index_reuc_entrycount(index); i++) { + reuc = git_index_reuc_get_byindex(index, i); + + printf("%s ", reuc->path); + printf("%o ", reuc->mode[0]); + printf("%s\n", git_oid_allocfmt(&reuc->oid[0])); + printf(" %o ", reuc->mode[1]); + printf(" %s\n", git_oid_allocfmt(&reuc->oid[1])); + printf(" %o ", reuc->mode[2]); + printf(" %s ", git_oid_allocfmt(&reuc->oid[2])); + printf("\n"); + } + printf("\n"); +} + static int index_entry_eq_merge_index_entry(const struct merge_index_entry *expected, const git_index_entry *actual) { git_oid expected_oid; - bool test_oid; + bool test_oid; if (strlen(expected->oid_str) != 0) { cl_git_pass(git_oid_fromstr(&expected_oid, expected->oid_str)); @@ -84,6 +136,16 @@ static int name_entry_eq(const char *expected, const char *actual) return (strcmp(expected, actual) == 0) ? 1 : 0; } +static int name_entry_eq_merge_name_entry(const struct merge_name_entry *expected, const git_index_name_entry *actual) +{ + if (name_entry_eq(expected->ancestor_path, actual->ancestor) == 0 || + name_entry_eq(expected->our_path, actual->ours) == 0 || + name_entry_eq(expected->their_path, actual->theirs) == 0) + return 0; + + return 1; +} + static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual) { if (!index_entry_eq_merge_index_entry((const struct merge_index_entry *)&expected->ancestor, &actual->ancestor_entry) || @@ -139,6 +201,29 @@ int merge_test_index(git_index *index, const struct merge_index_entry expected[] return 1; } +int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len) +{ + size_t i; + const git_index_name_entry *name_entry; + + /* + dump_names(index); + */ + + if (git_index_name_entrycount(index) != expected_len) + return 0; + + for (i = 0; i < expected_len; i++) { + if ((name_entry = git_index_name_get_byindex(index, i)) == NULL) + return 0; + + if (! name_entry_eq_merge_name_entry(&expected[i], name_entry)) + return 0; + } + + return 1; +} + int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len) { size_t i; diff --git a/tests-clar/merge/trees/renames.c b/tests-clar/merge/trees/renames.c new file mode 100644 index 000000000..dc0564bc4 --- /dev/null +++ b/tests-clar/merge/trees/renames.c @@ -0,0 +1,253 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "buffer.h" +#include "merge.h" +#include "../merge_helpers.h" +#include "fileops.h" + +static git_repository *repo; + +#define TEST_REPO_PATH "merge-resolve" + +#define BRANCH_RENAME_OURS "rename_conflict_ours" +#define BRANCH_RENAME_THEIRS "rename_conflict_theirs" + +// Fixture setup and teardown +void test_merge_trees_renames__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_merge_trees_renames__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_merge_trees_renames__index(void) +{ + git_index *index; + git_merge_tree_opts *opts = NULL; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" }, + { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" }, + { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" }, + { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" }, + { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" }, + { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" }, + { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" }, + { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" }, + { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" }, + { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" }, + { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" }, + { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" }, + { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" }, + { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" }, + { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 2, "3a-newname-in-ours-deleted-in-theirs.txt" }, + { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 1, "3a-renamed-in-ours-deleted-in-theirs.txt" }, + { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 3, "3b-newname-in-theirs-deleted-in-ours.txt" }, + { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 1, "3b-renamed-in-theirs-deleted-in-ours.txt" }, + { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" }, + { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" }, + { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 1, "4a-renamed-in-ours-added-in-theirs.txt" }, + { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" }, + { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" }, + { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 1, "4b-renamed-in-theirs-added-in-ours.txt" }, + { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" }, + { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" }, + { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 1, "5a-renamed-in-ours-added-in-theirs.txt" }, + { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 3, "5a-renamed-in-ours-added-in-theirs.txt" }, + { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" }, + { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" }, + { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 1, "5b-renamed-in-theirs-added-in-ours.txt" }, + { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 2, "5b-renamed-in-theirs-added-in-ours.txt" }, + { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 2, "6-both-renamed-1-to-2-ours.txt" }, + { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 3, "6-both-renamed-1-to-2-theirs.txt" }, + { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 1, "6-both-renamed-1-to-2.txt" }, + { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "7-both-renamed-side-1.txt" }, + { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "7-both-renamed-side-1.txt" }, + { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "7-both-renamed-side-2.txt" }, + { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "7-both-renamed-side-2.txt" }, + { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" }, + { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }, + }; + + struct merge_name_entry merge_name_entries[] = { + { + "3a-renamed-in-ours-deleted-in-theirs.txt", + "3a-newname-in-ours-deleted-in-theirs.txt", + "" + }, + + { + "3b-renamed-in-theirs-deleted-in-ours.txt", + "", + "3b-newname-in-theirs-deleted-in-ours.txt", + }, + + { + "4a-renamed-in-ours-added-in-theirs.txt", + "4a-newname-in-ours-added-in-theirs.txt", + "", + }, + + { + "4b-renamed-in-theirs-added-in-ours.txt", + "", + "4b-newname-in-theirs-added-in-ours.txt", + }, + + { + "5a-renamed-in-ours-added-in-theirs.txt", + "5a-newname-in-ours-added-in-theirs.txt", + "5a-renamed-in-ours-added-in-theirs.txt", + }, + + { + "5b-renamed-in-theirs-added-in-ours.txt", + "5b-renamed-in-theirs-added-in-ours.txt", + "5b-newname-in-theirs-added-in-ours.txt", + }, + + { + "6-both-renamed-1-to-2.txt", + "6-both-renamed-1-to-2-ours.txt", + "6-both-renamed-1-to-2-theirs.txt", + }, + + { + "7-both-renamed-side-1.txt", + "7-both-renamed.txt", + "7-both-renamed-side-1.txt", + }, + + { + "7-both-renamed-side-2.txt", + "7-both-renamed-side-2.txt", + "7-both-renamed.txt", + }, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + { "1a-newname-in-ours-edited-in-theirs.txt", + 0, 0100644, 0, + "", + "c3d02eeef75183df7584d8d13ac03053910c1301", + "" }, + + { "1a-newname-in-ours.txt", + 0, 0100644, 0, + "", + "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", + "" }, + + { "1a-renamed-in-ours-edited-in-theirs.txt", + 0100644, 0, 0100644, + "c3d02eeef75183df7584d8d13ac03053910c1301", + "", + "0d872f8e871a30208305978ecbf9e66d864f1638" }, + + { "1a-renamed-in-ours.txt", + 0100644, 0, 0100644, + "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", + "", + "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb" }, + + { "1b-newname-in-theirs-edited-in-ours.txt", + 0, 0, 0100644, + "", + "", + "241a1005cd9b980732741b74385b891142bcba28" }, + + { "1b-newname-in-theirs.txt", + 0, 0, 0100644, + "", + "", + "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136" }, + + { "1b-renamed-in-theirs-edited-in-ours.txt", + 0100644, 0100644, 0, + "241a1005cd9b980732741b74385b891142bcba28", + "ed9523e62e453e50dd9be1606af19399b96e397a", + "" }, + + { "1b-renamed-in-theirs.txt", + 0100644, 0100644, 0, + "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", + "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", + "" }, + + { "2-newname-in-both.txt", + 0, 0100644, 0100644, + "", + "178940b450f238a56c0d75b7955cb57b38191982", + "178940b450f238a56c0d75b7955cb57b38191982" }, + + { "2-renamed-in-both.txt", + 0100644, 0, 0, + "178940b450f238a56c0d75b7955cb57b38191982", + "", + "" }, + }; + + cl_git_pass(merge_trees_from_branches(&index, repo, + BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS, + opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 41)); + cl_assert(merge_test_names(index, merge_name_entries, 9)); + cl_assert(merge_test_reuc(index, merge_reuc_entries, 10)); + + git_index_free(index); +} + +void test_merge_trees_renames__no_rename_index(void) +{ + git_index *index; + git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" }, + { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" }, + { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" }, + { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" }, + { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" }, + { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" }, + { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" }, + { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" }, + { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" }, + { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-newname-in-ours-edited-in-theirs.txt" }, + { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" }, + { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 1, "1a-renamed-in-ours-edited-in-theirs.txt" }, + { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 3, "1a-renamed-in-ours-edited-in-theirs.txt" }, + { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt" }, + { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" }, + { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 1, "1b-renamed-in-theirs-edited-in-ours.txt" }, + { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 2, "1b-renamed-in-theirs-edited-in-ours.txt" }, + { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" }, + { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" }, + { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" }, + { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" }, + { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" }, + { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" }, + { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" }, + { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" }, + { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" }, + { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" }, + { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" }, + { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" }, + { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" }, + { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" }, + { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }, + }; + + cl_git_pass(merge_trees_from_branches(&index, repo, + BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS, + &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 32)); + + git_index_free(index); +} + diff --git a/tests-clar/merge/trees/treediff.c b/tests-clar/merge/trees/treediff.c index b2554f8be..afd8ac3ca 100644 --- a/tests-clar/merge/trees/treediff.c +++ b/tests-clar/merge/trees/treediff.c @@ -12,11 +12,17 @@ static git_repository *repo; #define TREE_OID_ANCESTOR "0d52e3a556e189ba0948ae56780918011c1b167d" #define TREE_OID_MASTER "1f81433e3161efbf250576c58fede7f6b836f3d3" #define TREE_OID_BRANCH "eea9286df54245fea72c5b557291470eb825f38f" +#define TREE_OID_RENAMES1 "f5f9dd5886a6ee20272be0aafc790cba43b31931" +#define TREE_OID_RENAMES2 "5fbfbdc04b4eca46f54f4853a3c5a1dce28f5165" #define TREE_OID_DF_ANCESTOR "b8a3a806d3950e8c0a03a34f234a92eff0e2c68d" #define TREE_OID_DF_SIDE1 "ee1d6f164893c1866a323f072eeed36b855656be" #define TREE_OID_DF_SIDE2 "6178885b38fe96e825ac0f492c0a941f288b37f6" +#define TREE_OID_RENAME_CONFLICT_ANCESTOR "476dbb3e207313d1d8aaa120c6ad204bf1295e53" +#define TREE_OID_RENAME_CONFLICT_OURS "c4efe31e9decccc8b2b4d3df9aac2cdfe2995618" +#define TREE_OID_RENAME_CONFLICT_THEIRS "9e7f4359c469f309b6057febf4c6e80742cbed5b" + void test_merge_trees_treediff__initialize(void) { repo = cl_git_sandbox_init(TEST_REPO_PATH); @@ -46,6 +52,20 @@ static void test_find_differences( git_tree *ancestor_tree, *ours_tree, *theirs_tree; struct treediff_cb_data treediff_cb_data = {0}; + git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.target_limit = 1000; + opts.rename_threshold = 50; + + opts.metric = git__malloc(sizeof(git_diff_similarity_metric)); + cl_assert(opts.metric != NULL); + + opts.metric->file_signature = git_diff_find_similar__hashsig_for_file; + opts.metric->buffer_signature = git_diff_find_similar__hashsig_for_buf; + opts.metric->free_signature = git_diff_find_similar__hashsig_free; + opts.metric->similarity = git_diff_find_similar__calc_similarity; + opts.metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE; + cl_git_pass(git_oid_fromstr(&ancestor_oid, ancestor_oidstr)); cl_git_pass(git_oid_fromstr(&ours_oid, ours_oidstr)); cl_git_pass(git_oid_fromstr(&theirs_oid, theirs_oidstr)); @@ -55,6 +75,7 @@ static void test_find_differences( cl_git_pass(git_tree_lookup(&theirs_tree, repo, &theirs_oid)); cl_git_pass(git_merge_diff_list__find_differences(merge_diff_list, ancestor_tree, ours_tree, theirs_tree)); + cl_git_pass(git_merge_diff_list__find_renames(repo, merge_diff_list, &opts)); /* dump_merge_index(merge_index); @@ -72,6 +93,8 @@ static void test_find_differences( git_tree_free(theirs_tree); git_merge_diff_list__free(merge_diff_list); + + git__free(opts.metric); } void test_merge_trees_treediff__simple(void) @@ -277,3 +300,255 @@ void test_merge_trees_treediff__df_conflicts(void) test_find_differences(TREE_OID_DF_ANCESTOR, TREE_OID_DF_SIDE1, TREE_OID_DF_SIDE2, treediff_conflict_data, 20); } +void test_merge_trees_treediff__strict_renames(void) +{ + struct merge_index_conflict_data treediff_conflict_data[] = { + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt", GIT_DELTA_ADDED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt", GIT_DELTA_MODIFIED }, + { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt", GIT_DELTA_MODIFIED }, + { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt", GIT_DELTA_MODIFIED }, + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "renamed-in-branch.txt", GIT_DELTA_RENAMED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "renamed.txt", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "copied.txt", GIT_DELTA_RENAMED }, + GIT_MERGE_DIFF_NONE, + }, + }; + + test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES1, treediff_conflict_data, 8); +} + +void test_merge_trees_treediff__rename_conflicts(void) +{ + struct merge_index_conflict_data treediff_conflict_data[] = { + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt", GIT_DELTA_ADDED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-rewritten-in-ours.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 0, "0b-rewritten-in-ours.txt", GIT_DELTA_MODIFIED }, + { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 0, "0b-rewritten-in-ours.txt", GIT_DELTA_MODIFIED }, + GIT_MERGE_DIFF_BOTH_MODIFIED, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-rewritten-in-theirs.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt", GIT_DELTA_MODIFIED }, + { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 0, "0c-rewritten-in-theirs.txt", GIT_DELTA_MODIFIED }, + GIT_MERGE_DIFF_BOTH_MODIFIED, + }, + + { + { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-renamed-in-ours-edited-in-theirs.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-newname-in-ours-edited-in-theirs.txt", GIT_DELTA_RENAMED }, + { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-renamed-in-ours-edited-in-theirs.txt", GIT_DELTA_MODIFIED }, + GIT_MERGE_DIFF_RENAMED_MODIFIED, + }, + + { + { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt", GIT_DELTA_RENAMED }, + { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-renamed-in-theirs-edited-in-ours.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-renamed-in-theirs-edited-in-ours.txt", GIT_DELTA_MODIFIED }, + { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt", GIT_DELTA_RENAMED }, + GIT_MERGE_DIFF_RENAMED_MODIFIED, + }, + + { + { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt", GIT_DELTA_RENAMED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-renamed-in-both.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt", GIT_DELTA_RENAMED }, + { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt", GIT_DELTA_RENAMED }, + GIT_MERGE_DIFF_BOTH_RENAMED, + }, + + { + { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-renamed-in-ours-deleted-in-theirs.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt", GIT_DELTA_RENAMED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + GIT_MERGE_DIFF_RENAMED_DELETED, + }, + + { + { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-renamed-in-theirs-deleted-in-ours.txt", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt", GIT_DELTA_RENAMED }, + GIT_MERGE_DIFF_RENAMED_DELETED, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_RENAMED_ADDED, + }, + + { + { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-renamed-in-ours-added-in-theirs.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt", GIT_DELTA_RENAMED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + GIT_MERGE_DIFF_RENAMED_ADDED, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt", GIT_DELTA_ADDED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_RENAMED_ADDED, + }, + + { + { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-renamed-in-theirs-added-in-ours.txt", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt", GIT_DELTA_RENAMED }, + GIT_MERGE_DIFF_RENAMED_ADDED, + }, + + { + { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-ours.txt", GIT_DELTA_RENAMED }, + { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-theirs.txt", GIT_DELTA_RENAMED }, + GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2, + }, + + { + { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed.txt", GIT_DELTA_RENAMED }, + { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1, + }, + + { + { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed.txt", GIT_DELTA_RENAMED }, + GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1, + }, + }; + + test_find_differences(TREE_OID_RENAME_CONFLICT_ANCESTOR, + TREE_OID_RENAME_CONFLICT_OURS, TREE_OID_RENAME_CONFLICT_THEIRS, treediff_conflict_data, 18); +} + +void test_merge_trees_treediff__best_renames(void) +{ + struct merge_index_conflict_data treediff_conflict_data[] = { + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt", GIT_DELTA_ADDED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt", GIT_DELTA_MODIFIED }, + { 0100644, "45299c1ca5e07bba1fd90843056fb559f96b1f5a", 0, "renamed-90.txt", GIT_DELTA_RENAMED }, + GIT_MERGE_DIFF_RENAMED_MODIFIED, + }, + + { + { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt", GIT_DELTA_MODIFIED }, + { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt", GIT_DELTA_MODIFIED }, + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt",GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_DELETED }, + { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED }, + GIT_MERGE_DIFF_MODIFIED_DELETED, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "5843febcb23480df0b5edb22a21c59c772bb8e29", 0, "renamed-50.txt", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_NONE, + }, + + { + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { 0100644, "a77a56a49f8f3ae242e02717f18ebbc60c5cc543", 0, "renamed-75.txt", GIT_DELTA_ADDED }, + GIT_MERGE_DIFF_NONE, + }, + }; + + test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES2, treediff_conflict_data, 7); +} From 75d1c8c664eba0ddef802b1bf6d1327707014c6e Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 30 Apr 2013 17:33:11 -0500 Subject: [PATCH 116/384] move NAME and REUC extensions to sys/ --- include/git2/index.h | 156 --------------------------- include/git2/sys/index.h | 180 +++++++++++++++++++++++++++++++ src/index.c | 1 + src/merge.c | 1 + tests-clar/index/names.c | 1 + tests-clar/index/reuc.c | 1 + tests-clar/merge/merge_helpers.c | 1 + tests-clar/merge/trees/trivial.c | 1 + 8 files changed, 186 insertions(+), 156 deletions(-) create mode 100644 include/git2/sys/index.h diff --git a/include/git2/index.h b/include/git2/index.h index 01e3d2c29..d23c3a8ea 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -84,19 +84,6 @@ typedef struct git_index_entry { char *path; } git_index_entry; -typedef struct git_index_name_entry { - char *ancestor; - char *ours; - char *theirs; -} git_index_name_entry; - -/** Representation of a resolve undo entry in the index. */ -typedef struct git_index_reuc_entry { - unsigned int mode[3]; - git_oid oid[3]; - char *path; -} git_index_reuc_entry; - /** Capabilities of system that affect index actions. */ enum { GIT_INDEXCAP_IGNORE_CASE = 1, @@ -484,149 +471,6 @@ GIT_EXTERN(int) git_index_has_conflicts(const git_index *index); /**@}*/ -/** @name Conflict Name entry functions - * - * These functions work on rename conflict entries. - */ -/**@{*/ - -/** - * Get the count of filename conflict entries currently in the index. - * - * @param index an existing index object - * @return integer of count of current filename conflict entries - */ -GIT_EXTERN(unsigned int) git_index_name_entrycount(git_index *index); - -/** - * Get a filename conflict entry from the index. - * - * The returned entry is read-only and should not be modified - * or freed by the caller. - * - * @param index an existing index object - * @param n the position of the entry - * @return a pointer to the filename conflict entry; NULL if out of bounds - */ -GIT_EXTERN(const git_index_name_entry *) git_index_name_get_byindex( - git_index *index, size_t n); - -/** - * Record the filenames involved in a rename conflict. - * - * @param index an existing index object - * @param ancestor the path of the file as it existed in the ancestor - * @param ours the path of the file as it existed in our tree - * @param theirs the path of the file as it existed in their tree - */ -GIT_EXTERN(int) git_index_name_add(git_index *index, - const char *ancestor, const char *ours, const char *theirs); - -/** - * Remove all filename conflict entries. - * - * @param index an existing index object - * @return 0 or an error code - */ -GIT_EXTERN(void) git_index_name_clear(git_index *index); -/**@}*/ - -/** @name Resolve Undo (REUC) index entry manipulation. - * - * These functions work on the Resolve Undo index extension and contains - * data about the original files that led to a merge conflict. - */ -/**@{*/ - -/** - * Get the count of resolve undo entries currently in the index. - * - * @param index an existing index object - * @return integer of count of current resolve undo entries - */ -GIT_EXTERN(unsigned int) git_index_reuc_entrycount(git_index *index); - -/** - * Finds the resolve undo entry that points to the given path in the Git - * index. - * - * @param at_pos the address to which the position of the reuc entry is written (optional) - * @param index an existing index object - * @param path path to search - * @return 0 if found, < 0 otherwise (GIT_ENOTFOUND) - */ -GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path); - -/** - * Get a resolve undo entry from the index. - * - * The returned entry is read-only and should not be modified - * or freed by the caller. - * - * @param index an existing index object - * @param path path to search - * @return the resolve undo entry; NULL if not found - */ -GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path); - -/** - * Get a resolve undo entry from the index. - * - * The returned entry is read-only and should not be modified - * or freed by the caller. - * - * @param index an existing index object - * @param n the position of the entry - * @return a pointer to the resolve undo entry; NULL if out of bounds - */ -GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n); - -/** - * Adds a resolve undo entry for a file based on the given parameters. - * - * The resolve undo entry contains the OIDs of files that were involved - * in a merge conflict after the conflict has been resolved. This allows - * conflicts to be re-resolved later. - * - * If there exists a resolve undo entry for the given path in the index, - * it will be removed. - * - * This method will fail in bare index instances. - * - * @param index an existing index object - * @param path filename to add - * @param ancestor_mode mode of the ancestor file - * @param ancestor_id oid of the ancestor file - * @param our_mode mode of our file - * @param our_id oid of our file - * @param their_mode mode of their file - * @param their_id oid of their file - * @return 0 or an error code - */ -GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path, - int ancestor_mode, const git_oid *ancestor_id, - int our_mode, const git_oid *our_id, - int their_mode, const git_oid *their_id); - -/** - * Remove an resolve undo entry from the index - * - * @param index an existing index object - * @param n position of the resolve undo entry to remove - * @return 0 or an error code - */ -GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n); - -/** - * Remove all resolve undo entries from the index - * - * @param index an existing index object - * @return 0 or an error code - */ -GIT_EXTERN(void) git_index_reuc_clear(git_index *index); - -/**@}*/ - /** @} */ GIT_END_DECL #endif diff --git a/include/git2/sys/index.h b/include/git2/sys/index.h new file mode 100644 index 000000000..f74637f84 --- /dev/null +++ b/include/git2/sys/index.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_sys_git_index_h__ +#define INCLUDE_sys_git_index_h__ + +/** + * @file git2/sys/index.h + * @brief Low-level Git index manipulation routines + * @defgroup git_backend Git custom backend APIs + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** Representation of a rename conflict entry in the index. */ +typedef struct git_index_name_entry { + char *ancestor; + char *ours; + char *theirs; +} git_index_name_entry; + +/** Representation of a resolve undo entry in the index. */ +typedef struct git_index_reuc_entry { + unsigned int mode[3]; + git_oid oid[3]; + char *path; +} git_index_reuc_entry; + +/** @name Conflict Name entry functions + * + * These functions work on rename conflict entries. + */ +/**@{*/ + +/** + * Get the count of filename conflict entries currently in the index. + * + * @param index an existing index object + * @return integer of count of current filename conflict entries + */ +GIT_EXTERN(unsigned int) git_index_name_entrycount(git_index *index); + +/** + * Get a filename conflict entry from the index. + * + * The returned entry is read-only and should not be modified + * or freed by the caller. + * + * @param index an existing index object + * @param n the position of the entry + * @return a pointer to the filename conflict entry; NULL if out of bounds + */ +GIT_EXTERN(const git_index_name_entry *) git_index_name_get_byindex( + git_index *index, size_t n); + +/** + * Record the filenames involved in a rename conflict. + * + * @param index an existing index object + * @param ancestor the path of the file as it existed in the ancestor + * @param ours the path of the file as it existed in our tree + * @param theirs the path of the file as it existed in their tree + */ +GIT_EXTERN(int) git_index_name_add(git_index *index, + const char *ancestor, const char *ours, const char *theirs); + +/** + * Remove all filename conflict entries. + * + * @param index an existing index object + * @return 0 or an error code + */ +GIT_EXTERN(void) git_index_name_clear(git_index *index); + +/**@}*/ + +/** @name Resolve Undo (REUC) index entry manipulation. + * + * These functions work on the Resolve Undo index extension and contains + * data about the original files that led to a merge conflict. + */ +/**@{*/ + +/** + * Get the count of resolve undo entries currently in the index. + * + * @param index an existing index object + * @return integer of count of current resolve undo entries + */ +GIT_EXTERN(unsigned int) git_index_reuc_entrycount(git_index *index); + +/** + * Finds the resolve undo entry that points to the given path in the Git + * index. + * + * @param at_pos the address to which the position of the reuc entry is written (optional) + * @param index an existing index object + * @param path path to search + * @return 0 if found, < 0 otherwise (GIT_ENOTFOUND) + */ +GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path); + +/** + * Get a resolve undo entry from the index. + * + * The returned entry is read-only and should not be modified + * or freed by the caller. + * + * @param index an existing index object + * @param path path to search + * @return the resolve undo entry; NULL if not found + */ +GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path); + +/** + * Get a resolve undo entry from the index. + * + * The returned entry is read-only and should not be modified + * or freed by the caller. + * + * @param index an existing index object + * @param n the position of the entry + * @return a pointer to the resolve undo entry; NULL if out of bounds + */ +GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n); + +/** + * Adds a resolve undo entry for a file based on the given parameters. + * + * The resolve undo entry contains the OIDs of files that were involved + * in a merge conflict after the conflict has been resolved. This allows + * conflicts to be re-resolved later. + * + * If there exists a resolve undo entry for the given path in the index, + * it will be removed. + * + * This method will fail in bare index instances. + * + * @param index an existing index object + * @param path filename to add + * @param ancestor_mode mode of the ancestor file + * @param ancestor_id oid of the ancestor file + * @param our_mode mode of our file + * @param our_id oid of our file + * @param their_mode mode of their file + * @param their_id oid of their file + * @return 0 or an error code + */ +GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path, + int ancestor_mode, const git_oid *ancestor_id, + int our_mode, const git_oid *our_id, + int their_mode, const git_oid *their_id); + +/** + * Remove an resolve undo entry from the index + * + * @param index an existing index object + * @param n position of the resolve undo entry to remove + * @return 0 or an error code + */ +GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n); + +/** + * Remove all resolve undo entries from the index + * + * @param index an existing index object + * @return 0 or an error code + */ +GIT_EXTERN(void) git_index_reuc_clear(git_index *index); + +/**@}*/ + +/** @} */ +GIT_END_DECL +#endif + diff --git a/src/index.c b/src/index.c index 656fb5bc5..a935c00db 100644 --- a/src/index.c +++ b/src/index.c @@ -19,6 +19,7 @@ #include "git2/oid.h" #include "git2/blob.h" #include "git2/config.h" +#include "git2/sys/index.h" #define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7) #define short_entry_size(len) entry_size(struct entry_short, len) diff --git a/src/merge.c b/src/merge.c index 681f302f4..3595eb058 100644 --- a/src/merge.c +++ b/src/merge.c @@ -36,6 +36,7 @@ #include "git2/signature.h" #include "git2/config.h" #include "git2/tree.h" +#include "git2/sys/index.h" #define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0) diff --git a/tests-clar/index/names.c b/tests-clar/index/names.c index ffc984249..68615531a 100644 --- a/tests-clar/index/names.c +++ b/tests-clar/index/names.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "index.h" +#include "git2/sys/index.h" #include "git2/repository.h" #include "../reset/reset_helpers.h" diff --git a/tests-clar/index/reuc.c b/tests-clar/index/reuc.c index 4d5955a01..0e38a92f3 100644 --- a/tests-clar/index/reuc.c +++ b/tests-clar/index/reuc.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "index.h" +#include "git2/sys/index.h" #include "git2/repository.h" #include "../reset/reset_helpers.h" diff --git a/tests-clar/merge/merge_helpers.c b/tests-clar/merge/merge_helpers.c index 7cb1e53da..b2c70bea7 100644 --- a/tests-clar/merge/merge_helpers.c +++ b/tests-clar/merge/merge_helpers.c @@ -5,6 +5,7 @@ #include "merge_helpers.h" #include "merge.h" #include "git2/merge.h" +#include "git2/sys/index.h" int merge_trees_from_branches( git_index **index, git_repository *repo, diff --git a/tests-clar/merge/trees/trivial.c b/tests-clar/merge/trees/trivial.c index 54b07e74a..7d8d2cbf5 100644 --- a/tests-clar/merge/trees/trivial.c +++ b/tests-clar/merge/trees/trivial.c @@ -5,6 +5,7 @@ #include "../merge_helpers.h" #include "refs.h" #include "fileops.h" +#include "git2/sys/index.h" static git_repository *repo; From 3e199f428513fbf04b126d536409deae1c6d045f Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 04:18:46 -0700 Subject: [PATCH 117/384] Set error message for branch functions There were a couple of places where an error was being returned from branch related code but no error message was being set. --- src/branch.c | 14 ++++++++++++-- tests-clar/refs/branches/remote.c | 6 ++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/branch.c b/src/branch.c index 88f052529..ab661f422 100644 --- a/src/branch.c +++ b/src/branch.c @@ -185,7 +185,7 @@ int git_branch_move( git_buf_cstr(&old_config_section), git_buf_cstr(&new_config_section))) < 0) goto done; - + if ((error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force)) < 0) goto done; @@ -276,6 +276,8 @@ int git_branch_upstream__name( goto cleanup; if (!*remote_name || !*merge_name) { + giterr_set(GITERR_REFERENCE, + "branch '%s' does not have an upstream", canonical_branch_name); error = GIT_ENOTFOUND; goto cleanup; } @@ -342,6 +344,9 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical remote_name = remote_list.strings[i]; } else { git_remote_free(remote); + + giterr_set(GITERR_REFERENCE, + "Reference '%s' is ambiguous", canonical_branch_name); error = GIT_EAMBIGUOUS; goto cleanup; } @@ -354,6 +359,8 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical git_buf_clear(buf); error = git_buf_puts(buf, remote_name); } else { + giterr_set(GITERR_REFERENCE, + "Could not determine remote for '%s'", canonical_branch_name); error = GIT_ENOTFOUND; } @@ -490,8 +497,11 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name) local = 1; else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0) local = 0; - else + else { + giterr_set(GITERR_REFERENCE, + "Cannot set upstream for branch '%s'", shortname); return GIT_ENOTFOUND; + } /* * If it's local, the remote is "." and the branch name is diff --git a/tests-clar/refs/branches/remote.c b/tests-clar/refs/branches/remote.c index 6043828b3..c110adb33 100644 --- a/tests-clar/refs/branches/remote.c +++ b/tests-clar/refs/branches/remote.c @@ -49,16 +49,20 @@ void test_refs_branches_remote__no_matching_remote_returns_error(void) { const char *unknown = "refs/remotes/nonexistent/master"; + giterr_clear(); cl_git_fail_with(git_branch_remote_name( NULL, 0, g_repo, unknown), GIT_ENOTFOUND); + cl_assert(giterr_last() != NULL); } void test_refs_branches_remote__local_remote_returns_error(void) { const char *local = "refs/heads/master"; + giterr_clear(); cl_git_fail_with(git_branch_remote_name( NULL, 0, g_repo, local), GIT_ERROR); + cl_assert(giterr_last() != NULL); } void test_refs_branches_remote__ambiguous_remote_returns_error(void) @@ -75,6 +79,8 @@ void test_refs_branches_remote__ambiguous_remote_returns_error(void) git_remote_free(remote); + giterr_clear(); cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, remote_tracking_branch_name), GIT_EAMBIGUOUS); + cl_assert(giterr_last() != NULL); } From f6f48f9008a9aaba5cbcfc611b616d8ae332b5e3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 04:57:05 -0700 Subject: [PATCH 118/384] Simplify error reporting --- src/clone.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/clone.c b/src/clone.c index aeb7bbf5c..a19788699 100644 --- a/src/clone.c +++ b/src/clone.c @@ -387,18 +387,6 @@ static int setup_remotes_and_fetch( } -static bool path_is_okay(const char *path) -{ - /* The path must either not exist, or be an empty directory */ - if (!git_path_exists(path)) return true; - if (!git_path_is_empty_dir(path)) { - giterr_set(GITERR_INVALID, - "'%s' exists and is not an empty directory", path); - return false; - } - return true; -} - static bool should_checkout( git_repository *repo, bool is_bare, @@ -444,7 +432,10 @@ int git_clone( normalize_options(&normOptions, options); GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options"); - if (!path_is_okay(local_path)) { + /* Only clone to a new directory or an empty directory */ + if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) { + giterr_set(GITERR_INVALID, + "'%s' exists and is not an empty directory", path); return GIT_ERROR; } From ae99f5e2ab090c1c666dfbf36753b8c18ab88478 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 04:57:24 -0700 Subject: [PATCH 119/384] Make sure error messages get set --- src/index.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/index.c b/src/index.c index 2e2d373b5..3c5173888 100644 --- a/src/index.c +++ b/src/index.c @@ -496,8 +496,10 @@ const git_index_entry *git_index_get_bypath( git_vector_sort(&index->entries); - if (index_find(&pos, index, path, stage) < 0) + if (index_find(&pos, index, path, stage) < 0) { + giterr_set(GITERR_INDEX, "Index does not contain %s", path); return NULL; + } return git_index_get_byindex(index, pos); } @@ -778,8 +780,11 @@ int git_index_remove(git_index *index, const char *path, int stage) git_vector_sort(&index->entries); - if (index_find(&position, index, path, stage) < 0) + if (index_find(&position, index, path, stage) < 0) { + giterr_set(GITERR_INDEX, "Index does not contain %s at stage %d", + path, stage); return GIT_ENOTFOUND; + } entry = git_vector_get(&index->entries, position); if (entry != NULL) From 46779411f93589fe567817fbdd61304847aa83c3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 05:32:10 -0700 Subject: [PATCH 120/384] fix typo --- src/clone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clone.c b/src/clone.c index a19788699..36da9c276 100644 --- a/src/clone.c +++ b/src/clone.c @@ -435,7 +435,7 @@ int git_clone( /* Only clone to a new directory or an empty directory */ if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) { giterr_set(GITERR_INVALID, - "'%s' exists and is not an empty directory", path); + "'%s' exists and is not an empty directory", local_path); return GIT_ERROR; } From 155ee751143a98356ec72d9930d2a82ec750e466 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 05:34:01 -0700 Subject: [PATCH 121/384] Add error messages for failed submodule lookup --- src/submodule.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/submodule.c b/src/submodule.c index 0a22e3b13..f4fbcd35a 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -128,6 +128,10 @@ int git_submodule_lookup( git_buf_free(&path); } + giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ? + "No submodule named '%s'" : + "Submodule '%s' has not been added yet", name); + return error; } From 2ba55c1f0df3400c15dbdd7b21d1f3d354ab2e3c Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 1 May 2013 15:20:08 +0200 Subject: [PATCH 122/384] refdb: Proper namespace root --- src/refdb_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 5228cb811..6f2162e77 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -1011,7 +1011,7 @@ int git_refdb_backend_fs( git_buf_puts(&path, repository->path_repository); if (repository->namespace != NULL) - git_buf_printf(&path, "refs/%s/", repository->namespace); + git_buf_printf(&path, "refs/namespaces/%s/", repository->namespace); backend->path = git_buf_detach(&path); From e1807113c43b7c5008e2d5c8c449ae56c8dceeb4 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 1 May 2013 15:31:23 +0200 Subject: [PATCH 123/384] merge: Warning noise --- src/checkout.c | 1 + src/index.c | 3 +++ src/merge.c | 2 +- tests-clar/merge/merge_helpers.c | 6 +++--- tests-clar/merge/trees/treediff.c | 11 ----------- 5 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 96e15093c..defc21d2d 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -16,6 +16,7 @@ #include "git2/config.h" #include "git2/diff.h" #include "git2/submodule.h" +#include "git2/sys/index.h" #include "refs.h" #include "repository.h" diff --git a/src/index.c b/src/index.c index a935c00db..1771f2957 100644 --- a/src/index.c +++ b/src/index.c @@ -209,6 +209,9 @@ static int conflict_name_cmp(const void *a, const void *b) return strcmp(name_a->ours, name_b->ours); } +/** + * TODO: enable this when resolving case insensitive conflicts + */ static int conflict_name_icmp(const void *a, const void *b) { const git_index_name_entry *name_a = a; diff --git a/src/merge.c b/src/merge.c index 3595eb058..320be005a 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1269,7 +1269,7 @@ int git_merge_diff_list__find_differences( const git_tree *their_tree) { git_iterator *iterators[3] = {0}; - git_index_entry *items[3] = {0}, *best_cur_item, *cur_items[3]; + const git_index_entry *items[3] = {0}, *best_cur_item, *cur_items[3]; git_vector_cmp entry_compare = git_index_entry__cmp; struct merge_diff_df_data df_data = {0}; int cur_item_modified; diff --git a/tests-clar/merge/merge_helpers.c b/tests-clar/merge/merge_helpers.c index b2c70bea7..5c3421e7e 100644 --- a/tests-clar/merge/merge_helpers.c +++ b/tests-clar/merge/merge_helpers.c @@ -52,7 +52,7 @@ int merge_trees_from_branches( return 0; } -static void dump_index_entries(git_vector *index_entries) +void merge__dump_index_entries(git_vector *index_entries) { size_t i; const git_index_entry *index_entry; @@ -70,7 +70,7 @@ static void dump_index_entries(git_vector *index_entries) printf("\n"); } -static void dump_names(git_index *index) +void merge__dump_names(git_index *index) { size_t i; const git_index_name_entry *conflict_name; @@ -83,7 +83,7 @@ static void dump_names(git_index *index) printf("\n"); } -static void dump_reuc(git_index *index) +void merge__dump_reuc(git_index *index) { size_t i; const git_index_reuc_entry *reuc; diff --git a/tests-clar/merge/trees/treediff.c b/tests-clar/merge/trees/treediff.c index afd8ac3ca..5da6f7658 100644 --- a/tests-clar/merge/trees/treediff.c +++ b/tests-clar/merge/trees/treediff.c @@ -33,13 +33,6 @@ void test_merge_trees_treediff__cleanup(void) cl_git_sandbox_cleanup(); } -struct treediff_cb_data { - struct merge_index_conflict_data *conflict_data; - size_t conflict_data_len; - - size_t idx; -}; - static void test_find_differences( const char *ancestor_oidstr, const char *ours_oidstr, @@ -50,7 +43,6 @@ static void test_find_differences( git_merge_diff_list *merge_diff_list = git_merge_diff_list__alloc(repo); git_oid ancestor_oid, ours_oid, theirs_oid; git_tree *ancestor_tree, *ours_tree, *theirs_tree; - struct treediff_cb_data treediff_cb_data = {0}; git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; @@ -82,9 +74,6 @@ static void test_find_differences( */ cl_assert(treediff_conflict_data_len == merge_diff_list->conflicts.length); - - treediff_cb_data.conflict_data = treediff_conflict_data; - treediff_cb_data.conflict_data_len = treediff_conflict_data_len; cl_assert(merge_test_merge_conflicts(&merge_diff_list->conflicts, treediff_conflict_data, treediff_conflict_data_len)); From 0cc7d8df19c82a13fd9d7c48563f40580d366cd3 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 1 May 2013 09:50:40 -0500 Subject: [PATCH 124/384] allow empty dirs to exist when doing checkout --- src/checkout.c | 8 ++++++-- tests-clar/checkout/tree.c | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index defc21d2d..21f32d89a 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -467,6 +467,7 @@ static int checkout_action( int cmp = -1, act; int (*strcomp)(const char *, const char *) = data->diff->strcomp; int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp; + int error; /* move workdir iterator to follow along with deltas */ @@ -490,8 +491,11 @@ static int checkout_action( if (cmp == 0) { if (wd->mode == GIT_FILEMODE_TREE) { /* case 2 - entry prefixed by workdir tree */ - if (git_iterator_advance_into(&wd, workdir) < 0) - goto fail; + if ((error = git_iterator_advance_into(&wd, workdir)) < 0) { + if (error != GIT_ENOTFOUND || + git_iterator_advance(&wd, workdir) < 0) + goto fail; + } *wditem_ptr = wd; continue; diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index 0748b22e0..eb129f34e 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -501,3 +501,28 @@ void test_checkout_tree__issue_1397(void) git_object_free(tree); } + +void test_checkout_tree__can_write_to_empty_dirs(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_oid oid; + git_object *obj = NULL; + + assert_on_branch(g_repo, "master"); + + cl_git_pass(p_mkdir("testrepo/a", 0777)); + + /* do first checkout with FORCE because we don't know if testrepo + * base data is clean for a checkout or not + */ + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); + + cl_assert(git_path_isfile("testrepo/a/b.txt")); + + git_object_free(obj); +} From 8cddf9b83a542bd66fcf0c4a1b692d47ff6556c2 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 1 May 2013 18:21:10 +0200 Subject: [PATCH 125/384] refdb: Properly load namespaces --- src/refdb_fs.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 6f2162e77..85444b692 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -997,6 +997,43 @@ static void refdb_fs_backend__free(git_refdb_backend *_backend) git__free(backend); } +static int setup_namespace(git_buf *path, git_repository *repo) +{ + char *parts, *start, *end; + + /* Load the path to the repo first */ + git_buf_puts(path, repo->path_repository); + + /* if the repo is not namespaced, nothing else to do */ + if (repo->namespace == NULL) + return 0; + + parts = end = git__strdup(repo->namespace); + if (parts == NULL) + return -1; + + /** + * From `man gitnamespaces`: + * namespaces which include a / will expand to a hierarchy + * of namespaces; for example, GIT_NAMESPACE=foo/bar will store + * refs under refs/namespaces/foo/refs/namespaces/bar/ + */ + while ((start = git__strsep(&end, "/")) != NULL) { + git_buf_printf(path, "refs/namespaces/%s/", start); + } + + git_buf_printf(path, "refs/namespaces/%s/refs", end); + free(parts); + + /* Make sure that the folder with the namespace exists */ + if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0) + return -1; + + /* Return the root of the namespaced path, i.e. without the trailing '/refs' */ + git_buf_rtruncate_at_char(path, '/'); + return 0; +} + int git_refdb_backend_fs( git_refdb_backend **backend_out, git_repository *repository) @@ -1009,9 +1046,10 @@ int git_refdb_backend_fs( backend->repo = repository; - git_buf_puts(&path, repository->path_repository); - if (repository->namespace != NULL) - git_buf_printf(&path, "refs/namespaces/%s/", repository->namespace); + if (setup_namespace(&path, repository) < 0) { + git__free(backend); + return -1; + } backend->path = git_buf_detach(&path); From 3f663178ed856a333fd11d8dd57231ebe331baf2 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 13:38:56 -0700 Subject: [PATCH 126/384] More care catching and setting config errors --- src/config.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/config.c b/src/config.c index 2e1268ef3..3f475ea63 100644 --- a/src/config.c +++ b/src/config.c @@ -344,6 +344,13 @@ int git_config_delete_entry(git_config *cfg, const char *name) * Setters **************/ +static int config_error_nofiles(const char *name) +{ + giterr_set(GITERR_CONFIG, + "Cannot set value for '%s' when no config files exist", name); + return GIT_ENOTFOUND; +} + int git_config_set_int64(git_config *cfg, const char *name, int64_t value) { char str_value[32]; /* All numbers should fit in here */ @@ -373,12 +380,9 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) } internal = git_vector_get(&cfg->files, 0); - if (!internal) { + if (!internal) /* Should we auto-vivify .git/config? Tricky from this location */ - giterr_set(GITERR_CONFIG, "Cannot set value when no config files exist"); - return GIT_ENOTFOUND; - } - + return config_error_nofiles(name); file = internal->file; error = file->set(file, name, value); @@ -442,6 +446,12 @@ static int get_string_at_file(const char **out, const git_config_backend *file, return res; } +static int config_error_notfound(const char *name) +{ + giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name); + return GIT_ENOTFOUND; +} + static int get_string(const char **out, const git_config *cfg, const char *name) { file_internal *internal; @@ -454,7 +464,7 @@ static int get_string(const char **out, const git_config *cfg, const char *name) return res; } - return GIT_ENOTFOUND; + return config_error_notfound(name); } int git_config_get_bool(int *out, const git_config *cfg, const char *name) @@ -494,7 +504,7 @@ int git_config_get_entry(const git_config_entry **out, const git_config *cfg, co return ret; } - return GIT_ENOTFOUND; + return config_error_notfound(name); } int git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp, @@ -517,7 +527,7 @@ int git_config_get_multivar(const git_config *cfg, const char *name, const char return ret; } - return 0; + return (ret == GIT_ENOTFOUND) ? config_error_notfound(name) : 0; } int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value) @@ -526,6 +536,8 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex file_internal *internal; internal = git_vector_get(&cfg->files, 0); + if (!internal) + return config_error_nofiles(name); file = internal->file; return file->set_multivar(file, name, regexp, value); From 52c102b7f679674b14d046a62091f76431d3eb1b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 13:43:48 -0700 Subject: [PATCH 127/384] More care reporting diff patch iteration errors --- src/diff_output.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/diff_output.c b/src/diff_output.c index 64ff6b5be..bac8622c8 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -1611,6 +1611,12 @@ int git_diff_patch_line_stats( return 0; } +static int diff_error_outofrange(const char *thing) +{ + giterr_set(GITERR_INVALID, "Diff patch %s index out of range", thing); + return GIT_ENOTFOUND; +} + int git_diff_patch_get_hunk( const git_diff_range **range, const char **header, @@ -1628,7 +1634,8 @@ int git_diff_patch_get_hunk( if (header) *header = NULL; if (header_len) *header_len = 0; if (lines_in_hunk) *lines_in_hunk = 0; - return GIT_ENOTFOUND; + + return diff_error_outofrange("hunk"); } hunk = &patch->hunks[hunk_idx]; @@ -1648,7 +1655,7 @@ int git_diff_patch_num_lines_in_hunk( assert(patch); if (hunk_idx >= patch->hunks_size) - return GIT_ENOTFOUND; + return diff_error_outofrange("hunk"); else return (int)patch->hunks[hunk_idx].line_count; } @@ -1665,15 +1672,20 @@ int git_diff_patch_get_line_in_hunk( { diff_patch_hunk *hunk; diff_patch_line *line; + const char *thing; assert(patch); - if (hunk_idx >= patch->hunks_size) + if (hunk_idx >= patch->hunks_size) { + thing = "hunk"; goto notfound; + } hunk = &patch->hunks[hunk_idx]; - if (line_of_hunk >= hunk->line_count) + if (line_of_hunk >= hunk->line_count) { + thing = "link"; goto notfound; + } line = &patch->lines[hunk->line_start + line_of_hunk]; @@ -1692,7 +1704,7 @@ notfound: if (old_lineno) *old_lineno = -1; if (new_lineno) *new_lineno = -1; - return GIT_ENOTFOUND; + return diff_error_outofrange(thing); } static int print_to_buffer_cb( From e830c020f3915e272cf0810899f40dd5c84fbbf6 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 13:50:39 -0700 Subject: [PATCH 128/384] Report stat error when checking if file modified --- src/fileops.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fileops.c b/src/fileops.c index d6244711f..cc55207e0 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -988,8 +988,10 @@ int git_futils_filestamp_check( if (stamp == NULL) return 1; - if (p_stat(path, &st) < 0) + if (p_stat(path, &st) < 0) { + giterr_set(GITERR_OS, "Could not stat '%s'", path); return GIT_ENOTFOUND; + } if (stamp->mtime == (git_time_t)st.st_mtime && stamp->size == (git_off_t)st.st_size && From de19c4a9582b74eec1d510d4ded5889d3b3e7976 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 14:00:20 -0700 Subject: [PATCH 129/384] Set error when no merge base is found --- src/merge.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/merge.c b/src/merge.c index e0010d6a4..48ff80cd4 100644 --- a/src/merge.c +++ b/src/merge.c @@ -86,6 +86,7 @@ int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_ goto cleanup; if (!result) { + giterr_set(GITERR_MERGE, "No merge base found"); error = GIT_ENOTFOUND; goto cleanup; } @@ -131,7 +132,7 @@ int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const if (!result) { git_revwalk_free(walk); - giterr_clear(); + giterr_set(GITERR_MERGE, "No merge base found"); return GIT_ENOTFOUND; } From 734c6fc13430f770f7e98cf5d23b7f6e470c29ef Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 14:15:55 -0700 Subject: [PATCH 130/384] Report errors finding notes --- src/notes.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/notes.c b/src/notes.c index ef48ac88e..4ca2aaa63 100644 --- a/src/notes.c +++ b/src/notes.c @@ -13,6 +13,12 @@ #include "iterator.h" #include "signature.h" +static int note_error_notfound(void) +{ + giterr_set(GITERR_INVALID, "Note could not be found"); + return GIT_ENOTFOUND; +} + static int find_subtree_in_current_level( git_tree **out, git_repository *repo, @@ -26,7 +32,7 @@ static int find_subtree_in_current_level( *out = NULL; if (parent == NULL) - return GIT_ENOTFOUND; + return note_error_notfound(); for (i = 0; i < git_tree_entrycount(parent); i++) { entry = git_tree_entry_byindex(parent, i); @@ -44,7 +50,7 @@ static int find_subtree_in_current_level( return GIT_EEXISTS; } - return GIT_ENOTFOUND; + return note_error_notfound(); } static int find_subtree_r(git_tree **out, git_tree *root, @@ -56,9 +62,8 @@ static int find_subtree_r(git_tree **out, git_tree *root, *out = NULL; error = find_subtree_in_current_level(&subtree, repo, root, target, *fanout); - if (error == GIT_EEXISTS) { + if (error == GIT_EEXISTS) return git_tree_lookup(out, repo, git_tree_id(root)); - } if (error < 0) return error; @@ -85,7 +90,8 @@ static int find_blob(git_oid *blob, git_tree *tree, const char *target) return 0; } } - return GIT_ENOTFOUND; + + return note_error_notfound(); } static int tree_write( @@ -316,8 +322,8 @@ static int note_new(git_note **out, git_oid *note_oid, git_blob *blob) return 0; } -static int note_lookup(git_note **out, git_repository *repo, - git_tree *tree, const char *target) +static int note_lookup( + git_note **out, git_repository *repo, git_tree *tree, const char *target) { int error, fanout = 0; git_oid oid; @@ -382,6 +388,7 @@ static int note_get_default_ref(const char **out, git_repository *repo) ret = git_config_get_string(out, cfg, "core.notesRef"); if (ret == GIT_ENOTFOUND) { + giterr_clear(); *out = GIT_NOTES_DEFAULT_REF; return 0; } @@ -432,12 +439,10 @@ int git_note_read(git_note **out, git_repository *repo, target = git_oid_allocfmt(oid); GITERR_CHECK_ALLOC(target); - if ((error = retrieve_note_tree_and_commit(&tree, &commit, repo, ¬es_ref)) < 0) - goto cleanup; + if (!(error = retrieve_note_tree_and_commit( + &tree, &commit, repo, ¬es_ref))) + error = note_lookup(out, repo, tree, target); - error = note_lookup(out, repo, tree, target); - -cleanup: git__free(target); git_tree_free(tree); git_commit_free(commit); @@ -489,13 +494,11 @@ int git_note_remove(git_repository *repo, const char *notes_ref, target = git_oid_allocfmt(oid); GITERR_CHECK_ALLOC(target); - if ((error = retrieve_note_tree_and_commit(&tree, &commit, repo, ¬es_ref)) < 0) - goto cleanup; + if (!(error = retrieve_note_tree_and_commit( + &tree, &commit, repo, ¬es_ref))) + error = note_remove( + repo, author, committer, notes_ref, tree, target, &commit); - error = note_remove(repo, author, committer, notes_ref, - tree, target, &commit); - -cleanup: git__free(target); git_commit_free(commit); git_tree_free(tree); @@ -533,7 +536,7 @@ static int process_entry_path( const char* entry_path, git_oid *annotated_object_id) { - int error = -1; + int error = 0; size_t i = 0, j = 0, len; git_buf buf = GIT_BUF_INIT; From 8915a140cb996f84eeafadb99b195c33301c7d30 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 14:23:01 -0700 Subject: [PATCH 131/384] Report a couple object error conditions --- src/object.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/object.c b/src/object.c index b87a07404..43aecf434 100644 --- a/src/object.c +++ b/src/object.c @@ -122,8 +122,10 @@ int git_object_lookup_prefix( assert(repo && object_out && id); - if (len < GIT_OID_MINPREFIXLEN) + if (len < GIT_OID_MINPREFIXLEN) { + giterr_set(GITERR_OBJECT, "Ambiguous lookup - OID prefix is too short"); return GIT_EAMBIGUOUS; + } error = git_repository_odb__weakptr(&odb, repo); if (error < 0) @@ -311,18 +313,22 @@ int git_object_peel( git_object *source, *deref = NULL; int error; - if (target_type != GIT_OBJ_TAG && - target_type != GIT_OBJ_COMMIT && - target_type != GIT_OBJ_TREE && - target_type != GIT_OBJ_BLOB && - target_type != GIT_OBJ_ANY) - return GIT_EINVALIDSPEC; - assert(object && peeled); if (git_object_type(object) == target_type) return git_object_dup(peeled, (git_object *)object); + if (target_type != GIT_OBJ_TAG && + target_type != GIT_OBJ_COMMIT && + target_type != GIT_OBJ_TREE && + target_type != GIT_OBJ_BLOB && + target_type != GIT_OBJ_ANY) { + + giterr_set(GITERR_OBJECT, "Cannot peel to object type %d", + (int)target_type); + return GIT_EINVALIDSPEC; + } + source = (git_object *)object; while (!(error = dereference_object(&deref, source))) { From f063f578981bfb7f88880e9bdfbfd0a370dfefea Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 14:48:35 -0700 Subject: [PATCH 132/384] Catch some odd odb backend corner case errors There are some cases, particularly where no loaded ODB backends support a particular operation, where we would return an error code without having set an error. This catches those cases and reports that no ODB backends support the operation in question. --- src/odb.c | 65 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/src/odb.c b/src/odb.c index 07e1ea6eb..4ab86b14e 100644 --- a/src/odb.c +++ b/src/odb.c @@ -424,6 +424,14 @@ size_t git_odb_num_backends(git_odb *odb) return odb->backends.length; } +static int git_odb__error_unsupported_in_backend(const char *action) +{ + giterr_set(GITERR_ODB, + "Cannot %s - unsupported in the loaded odb backends", action); + return -1; +} + + int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos) { backend_internal *internal; @@ -436,6 +444,7 @@ int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos) return 0; } + giterr_set(GITERR_ODB, "No ODB backend loaded at index " PRIuZ, pos); return GIT_ENOTFOUND; } @@ -469,7 +478,7 @@ static int add_default_backends( return 0; } #endif - + /* add the loose object backend */ if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 || add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0) @@ -492,9 +501,8 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ int result = 0; /* Git reports an error, we just ignore anything deeper */ - if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) { + if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) return 0; - } if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0) return -1; @@ -684,7 +692,7 @@ int git_odb__read_header_or_object( int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) { - size_t i; + size_t i, reads = 0; int error; bool refreshed = false; git_rawobj raw; @@ -692,11 +700,6 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) assert(out && db && id); - if (db->backends.length == 0) { - giterr_set(GITERR_ODB, "Failed to lookup object: no backends loaded"); - return GIT_ENOTFOUND; - } - *out = git_cache_get_raw(odb_cache(db), id); if (*out != NULL) return 0; @@ -708,8 +711,10 @@ attempt_lookup: backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; - if (b->read != NULL) + if (b->read != NULL) { + ++reads; error = b->read(&raw.data, &raw.len, &raw.type, b, id); + } } if (error == GIT_ENOTFOUND && !refreshed) { @@ -720,8 +725,11 @@ attempt_lookup: goto attempt_lookup; } - if (error && error != GIT_PASSTHROUGH) + if (error && error != GIT_PASSTHROUGH) { + if (!reads) + return git_odb__error_notfound("no match for id", id); return error; + } if ((object = odb_object__alloc(id, &raw)) == NULL) return -1; @@ -841,10 +849,10 @@ int git_odb_write( if (!error || error == GIT_PASSTHROUGH) return 0; - /* if no backends were able to write the object directly, we try a streaming - * write to the backends; just write the whole object into the stream in one - * push */ - + /* if no backends were able to write the object directly, we try a + * streaming write to the backends; just write the whole object into the + * stream in one push + */ if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0) return error; @@ -858,7 +866,7 @@ int git_odb_write( int git_odb_open_wstream( git_odb_stream **stream, git_odb *db, size_t size, git_otype type) { - size_t i; + size_t i, writes = 0; int error = GIT_ERROR; assert(stream && db); @@ -871,21 +879,26 @@ int git_odb_open_wstream( if (internal->is_alternate) continue; - if (b->writestream != NULL) + if (b->writestream != NULL) { + ++writes; error = b->writestream(stream, b, size, type); - else if (b->write != NULL) + } else if (b->write != NULL) { + ++writes; error = init_fake_wstream(stream, b, size, type); + } } if (error == GIT_PASSTHROUGH) error = 0; + if (error < 0 && !writes) + error = git_odb__error_unsupported_in_backend("write object"); return error; } int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid) { - size_t i; + size_t i, reads = 0; int error = GIT_ERROR; assert(stream && db); @@ -894,19 +907,23 @@ int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oi backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; - if (b->readstream != NULL) + if (b->readstream != NULL) { + ++reads; error = b->readstream(stream, b, oid); + } } if (error == GIT_PASSTHROUGH) error = 0; + if (error < 0 && !reads) + error = git_odb__error_unsupported_in_backend("read object streamed"); return error; } int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer_progress_callback progress_cb, void *progress_payload) { - size_t i; + size_t i, writes = 0; int error = GIT_ERROR; assert(out && db); @@ -919,12 +936,16 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer if (internal->is_alternate) continue; - if (b->writepack != NULL) + if (b->writepack != NULL) { + ++writes; error = b->writepack(out, b, progress_cb, progress_payload); + } } if (error == GIT_PASSTHROUGH) error = 0; + if (error < 0 && !writes) + error = git_odb__error_unsupported_in_backend("write pack"); return error; } From 62caf3f38fa25537b5f824329d8f88875ee837f2 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 15:01:47 -0700 Subject: [PATCH 133/384] Report some errors returnable by push --- src/push.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/push.c b/src/push.c index 9b1e78c8e..0499d8648 100644 --- a/src/push.c +++ b/src/push.c @@ -303,7 +303,7 @@ static int revwalk(git_vector *commits, git_push *push) continue; if (!git_odb_exists(push->repo->_odb, &spec->roid)) { - giterr_clear(); + giterr_set(GITERR_REFERENCE, "Cannot push missing reference"); error = GIT_ENONFASTFORWARD; goto on_error; } @@ -313,7 +313,8 @@ static int revwalk(git_vector *commits, git_push *push) if (error == GIT_ENOTFOUND || (!error && !git_oid_equal(&base, &spec->roid))) { - giterr_clear(); + giterr_set(GITERR_REFERENCE, + "Cannot push non-fastforwardable reference"); error = GIT_ENONFASTFORWARD; goto on_error; } @@ -333,12 +334,13 @@ static int revwalk(git_vector *commits, git_push *push) while ((error = git_revwalk_next(&oid, rw)) == 0) { git_oid *o = git__malloc(GIT_OID_RAWSZ); - GITERR_CHECK_ALLOC(o); - git_oid_cpy(o, &oid); - if (git_vector_insert(commits, o) < 0) { + if (!o) { error = -1; goto on_error; } + git_oid_cpy(o, &oid); + if ((error = git_vector_insert(commits, o)) < 0) + goto on_error; } on_error: @@ -519,7 +521,7 @@ static int calculate_work(git_push *push) /* This is a create or update. Local ref must exist. */ if (git_reference_name_to_id( &spec->loid, push->repo, spec->lref) < 0) { - giterr_set(GIT_ENOTFOUND, "No such reference '%s'", spec->lref); + giterr_set(GITERR_REFERENCE, "No such reference '%s'", spec->lref); return -1; } } From 41e93563e7462bd37cf3a09b8aaac35736046482 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 15:08:12 -0700 Subject: [PATCH 134/384] Error messages for a couple other boundary conditions --- src/reflog.c | 4 +++- src/refs.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/reflog.c b/src/reflog.c index 8c133fe53..4cc20d2c7 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -483,8 +483,10 @@ int git_reflog_drop( entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx); - if (entry == NULL) + if (entry == NULL) { + giterr_set(GITERR_REFERENCE, "No reflog entry at index "PRIuZ, idx); return GIT_ENOTFOUND; + } reflog_entry_free(entry); diff --git a/src/refs.c b/src/refs.c index b85a2e828..8bba3941e 100644 --- a/src/refs.c +++ b/src/refs.c @@ -844,8 +844,10 @@ static int reference__update_terminal( git_reference *ref; int error = 0; - if (nesting > MAX_NESTING_LEVEL) + if (nesting > MAX_NESTING_LEVEL) { + giterr_set(GITERR_REFERENCE, "Reference chain too deep (%d)", nesting); return GIT_ENOTFOUND; + } error = git_reference_lookup(&ref, repo, ref_name); From bf6bebe22eff29c6b7ff744b809057acede4e615 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 15:23:40 -0700 Subject: [PATCH 135/384] Factor out some code that needed to clear errors A number of places were looking up option config values and then not clearing the error codes if the values were not found. This moves the repeated pattern into a shared routine and adds the extra call to giterr_clear() when needed. --- src/remote.c | 85 ++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/src/remote.c b/src/remote.c index 6eaaf8b49..cba6b2548 100644 --- a/src/remote.c +++ b/src/remote.c @@ -63,8 +63,10 @@ static int download_tags_value(git_remote *remote, git_config *cfg) else if (!error && !strcmp(val, "--tags")) remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; - if (error == GIT_ENOTFOUND) + if (error == GIT_ENOTFOUND) { + giterr_clear(); error = 0; + } return error; } @@ -210,6 +212,31 @@ static int refspec_cb(const git_config_entry *entry, void *payload) return add_refspec(data->remote, entry->value, data->fetch); } +static int get_optional_config( + git_config *config, git_buf *buf, git_config_foreach_cb cb, void *payload) +{ + int error = 0; + const char *key = git_buf_cstr(buf); + + if (git_buf_oom(buf)) + return -1; + + if (cb != NULL) + error = git_config_get_multivar(config, key, NULL, cb, payload); + else + error = git_config_get_string(payload, config, key); + + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + + if (error < 0) + error = -1; + + return error; +} + int git_remote_load(git_remote **out, git_repository *repo, const char *name) { git_remote *remote; @@ -250,7 +277,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) if ((error = git_config_get_string(&val, config, git_buf_cstr(&buf))) < 0) goto cleanup; - + if (strlen(val) == 0) { giterr_set(GITERR_INVALID, "Malformed remote '%s' - missing URL", name); error = -1; @@ -261,60 +288,32 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) remote->url = git__strdup(val); GITERR_CHECK_ALLOC(remote->url); + val = NULL; git_buf_clear(&buf); - if (git_buf_printf(&buf, "remote.%s.pushurl", name) < 0) { - error = -1; - goto cleanup; - } + git_buf_printf(&buf, "remote.%s.pushurl", name); - error = git_config_get_string(&val, config, git_buf_cstr(&buf)); - if (error == GIT_ENOTFOUND) { - val = NULL; - error = 0; - } - - if (error < 0) { - error = -1; + if ((error = get_optional_config(config, &buf, NULL, &val)) < 0) goto cleanup; - } if (val) { remote->pushurl = git__strdup(val); GITERR_CHECK_ALLOC(remote->pushurl); } - git_buf_clear(&buf); - if (git_buf_printf(&buf, "remote.%s.fetch", name) < 0) { - error = -1; - goto cleanup; - } - data.remote = remote; data.fetch = true; - error = git_config_get_multivar(config, git_buf_cstr(&buf), NULL, refspec_cb, &data); - if (error == GIT_ENOTFOUND) - error = 0; - - if (error < 0) { - error = -1; - goto cleanup; - } - git_buf_clear(&buf); - if (git_buf_printf(&buf, "remote.%s.push", name) < 0) { - error = -1; + git_buf_printf(&buf, "remote.%s.fetch", name); + + if ((error = get_optional_config(config, &buf, refspec_cb, &data)) < 0) goto cleanup; - } data.fetch = false; - error = git_config_get_multivar(config, git_buf_cstr(&buf), NULL, refspec_cb, &data); - if (error == GIT_ENOTFOUND) - error = 0; + git_buf_clear(&buf); + git_buf_printf(&buf, "remote.%s.push", name); - if (error < 0) { - error = -1; + if ((error = get_optional_config(config, &buf, refspec_cb, &data)) < 0) goto cleanup; - } if (download_tags_value(remote, config) < 0) goto cleanup; @@ -336,7 +335,7 @@ static int update_config_refspec(const git_remote *remote, git_config *config, i int push; const char *dir; size_t i; - int error = -1; + int error = 0; push = direction == GIT_DIRECTION_PUSH; dir = push ? "push" : "fetch"; @@ -345,9 +344,8 @@ static int update_config_refspec(const git_remote *remote, git_config *config, i return -1; /* Clear out the existing config */ - do { + while (!error) error = git_config_delete_entry(config, git_buf_cstr(&name)); - } while (!error); if (error != GIT_ENOTFOUND) return error; @@ -358,7 +356,8 @@ static int update_config_refspec(const git_remote *remote, git_config *config, i if (spec->push != push) continue; - if ((error = git_config_set_multivar(config, git_buf_cstr(&name), "", spec->string)) < 0) { + if ((error = git_config_set_multivar( + config, git_buf_cstr(&name), "", spec->string)) < 0) { goto cleanup; } } From 1a9e406c218e3d5a174bc4f8dce0333d73d7bbff Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 15:47:37 -0700 Subject: [PATCH 136/384] minor missing error message --- src/repository.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/repository.c b/src/repository.c index e6eaf753c..44e7ca3c4 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1598,6 +1598,7 @@ int git_repository_message(char *buffer, size_t len, git_repository *repo) if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) { if (errno == ENOENT) error = GIT_ENOTFOUND; + giterr_set(GITERR_OS, "Could not access message file"); } else if (buffer != NULL) { error = git_futils_readbuffer(&buf, git_buf_cstr(&path)); From f470b00b037cfcd40e19e88913a9a8b64d98288f Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 15:48:40 -0700 Subject: [PATCH 137/384] Fix one error not reported in revparse There are many paths through revparse that may return an error code without reporting an error, I believe. This fixes one of them. Because of the backtracking in revparse, it is pretty complicated to fix the others. --- src/revparse.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/revparse.c b/src/revparse.c index 8a22a04f3..e8cc32aff 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -17,7 +17,7 @@ static int disambiguate_refname(git_reference **out, git_repository *repo, const char *refname) { int error = 0, i; - bool fallbackmode = true; + bool fallbackmode = true, foundvalid = false; git_reference *ref; git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT; @@ -49,6 +49,7 @@ static int disambiguate_refname(git_reference **out, git_repository *repo, const error = GIT_EINVALIDSPEC; continue; } + foundvalid = true; error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1); @@ -63,6 +64,12 @@ static int disambiguate_refname(git_reference **out, git_repository *repo, const } cleanup: + if (error && !foundvalid) { + /* never found a valid reference name */ + giterr_set(GITERR_REFERENCE, + "Could not use '%s' as valid reference name", git_buf_cstr(&name)); + } + git_buf_free(&name); git_buf_free(&refnamebuf); return error; From 52c52737353a7ee7b653ab314d7b89ca6ddafe63 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 15:51:30 -0700 Subject: [PATCH 138/384] Clear error msg when we eat error silently --- src/stash.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stash.c b/src/stash.c index 355c5dc9c..19b29be77 100644 --- a/src/stash.c +++ b/src/stash.c @@ -587,8 +587,10 @@ int git_stash_foreach( const git_reflog_entry *entry; error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE); - if (error == GIT_ENOTFOUND) + if (error == GIT_ENOTFOUND) { + giterr_clear(); return 0; + } if (error < 0) goto cleanup; @@ -651,7 +653,7 @@ int git_stash_drop( const git_reflog_entry *entry; entry = git_reflog_entry_byindex(reflog, 0); - + git_reference_free(stash); error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1); } From 2f28219ce3da1387548b8c614d73ee01ef80f9d6 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 15:53:12 -0700 Subject: [PATCH 139/384] clarify where error message is set --- src/status.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/status.c b/src/status.c index ac6b4379b..73472ab14 100644 --- a/src/status.c +++ b/src/status.c @@ -141,7 +141,7 @@ int git_status_foreach_ext( /* if there is no HEAD, that's okay - we'll make an empty iterator */ if (((err = git_repository_head_tree(&head, repo)) < 0) && !(err == GIT_ENOTFOUND || err == GIT_EORPHANEDHEAD)) - return err; + return err; memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); @@ -238,7 +238,7 @@ static int get_one_status(const char *path, unsigned int status, void *data) p_fnmatch(sfi->expected, path, sfi->fnm_flags) != 0)) { sfi->ambiguous = true; - return GIT_EAMBIGUOUS; + return GIT_EAMBIGUOUS; /* giterr_set will be done by caller */ } return 0; From b60d95c714f5e68f07c5251aebbe72ba3ad5806d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 1 May 2013 15:55:54 -0700 Subject: [PATCH 140/384] clarify error propogation --- src/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree.c b/src/tree.c index 79cbcffcb..a48b322d4 100644 --- a/src/tree.c +++ b/src/tree.c @@ -149,7 +149,7 @@ static int tree_key_search( /* Initial homing search; find an entry on the tree with * the same prefix as the filename we're looking for */ if (git_vector_bsearch2(&homing, entries, &homing_search_cmp, &ksearch) < 0) - return GIT_ENOTFOUND; + return GIT_ENOTFOUND; /* just a signal error; not passed back to user */ /* We found a common prefix. Look forward as long as * there are entries that share the common prefix */ From 9d2f841a5d39fc25ce722a3904f6ebc9aa112222 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 2 May 2013 03:03:54 -0700 Subject: [PATCH 141/384] Add extra locking around packfile open We were still seeing a few issues in threaded access to packs. This adds extra locks around the opening of the mwindow to avoid a different race. --- src/pack.c | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/pack.c b/src/pack.c index f8b621ef8..47534f195 100644 --- a/src/pack.c +++ b/src/pack.c @@ -205,13 +205,18 @@ static int pack_index_check(const char *path, struct git_pack_file *p) if (fd < 0) return fd; - if (p_fstat(fd, &st) < 0 || - !S_ISREG(st.st_mode) || + if (p_fstat(fd, &st) < 0) { + p_close(fd); + giterr_set(GITERR_OS, "Unable to stat pack index '%s'", path); + return -1; + } + + if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size) || (idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20) { p_close(fd); - giterr_set(GITERR_OS, "Failed to check pack index."); + giterr_set(GITERR_ODB, "Invalid pack index '%s'", path); return -1; } @@ -402,7 +407,7 @@ int git_packfile_unpack_header( if (base == NULL) return GIT_EBUFS; - ret = packfile_unpack_header1(&used, size_p, type_p, base, left); + ret = packfile_unpack_header1(&used, size_p, type_p, base, left); git_mwindow_close(w_curs); if (ret == GIT_EBUFS) return ret; @@ -799,9 +804,6 @@ void git_packfile_free(struct git_pack_file *p) if (!p) return; - if (git_mutex_lock(&p->lock) < 0) - return; - cache_free(&p->bases); git_mwindow_free_all(&p->mwf); @@ -813,8 +815,6 @@ void git_packfile_free(struct git_pack_file *p) git__free(p->bad_object_sha1); - git_mutex_unlock(&p->lock); - git_mutex_free(&p->lock); git__free(p); } @@ -829,12 +829,19 @@ static int packfile_open(struct git_pack_file *p) if (!p->index_map.data && pack_index_open(p) < 0) return git_odb__error_notfound("failed to open packfile", NULL); + /* if mwf opened by another thread, return now */ + if (git_mutex_lock(&p->lock) < 0) + return packfile_error("failed to get lock for open"); + + if (p->mwf.fd >= 0) { + git_mutex_unlock(&p->lock); + return 0; + } + /* TODO: open with noatime */ p->mwf.fd = git_futils_open_ro(p->pack_name); - if (p->mwf.fd < 0) { - p->mwf.fd = -1; - return -1; - } + if (p->mwf.fd < 0) + goto cleanup; if (p_fstat(p->mwf.fd, &st) < 0 || git_mwindow_file_register(&p->mwf) < 0) @@ -875,13 +882,20 @@ static int packfile_open(struct git_pack_file *p) idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40; - if (git_oid__cmp(&sha1, (git_oid *)idx_sha1) == 0) - return 0; + if (git_oid__cmp(&sha1, (git_oid *)idx_sha1) != 0) + goto cleanup; + + git_mutex_unlock(&p->lock); + return 0; cleanup: giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name); + p_close(p->mwf.fd); p->mwf.fd = -1; + + git_mutex_unlock(&p->lock); + return -1; } From d82d66c96d11131440deb8f79443ee0b44d40eff Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 2 May 2013 03:05:21 -0700 Subject: [PATCH 142/384] Extra threading tests We need to hammer the packfile open phase harder in the thread tests, in addition to the cache API. --- tests-clar/object/cache.c | 58 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/tests-clar/object/cache.c b/tests-clar/object/cache.c index a3eba8737..e06760e2a 100644 --- a/tests-clar/object/cache.c +++ b/tests-clar/object/cache.c @@ -5,7 +5,7 @@ static git_repository *g_repo; void test_object_cache__initialize(void) { - cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); + g_repo = NULL; } void test_object_cache__cleanup(void) @@ -56,6 +56,7 @@ void test_object_cache__cache_everything(void) git_libgit2_opts( GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)32767); + cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); cl_git_pass(git_repository_odb(&odb, g_repo)); start = (int)git_cache_size(&g_repo->objects); @@ -105,6 +106,7 @@ void test_object_cache__cache_no_blobs(void) git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0); + cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); cl_git_pass(git_repository_odb(&odb, g_repo)); start = (int)git_cache_size(&g_repo->objects); @@ -189,8 +191,8 @@ static void *cache_raw(void *arg) return arg; } -#define REPEAT 50 -#define THREADCOUNT 20 +#define REPEAT 20 +#define THREADCOUNT 50 void test_object_cache__threadmania(void) { @@ -207,6 +209,8 @@ void test_object_cache__threadmania(void) for (try = 0; try < REPEAT; ++try) { + cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); + for (th = 0; th < THREADCOUNT; ++th) { data = git__malloc(2 * sizeof(int)); @@ -231,5 +235,53 @@ void test_object_cache__threadmania(void) } #endif + git_repository_free(g_repo); + g_repo = NULL; + } +} + +static void *cache_quick(void *arg) +{ + git_oid oid; + git_object *obj; + + cl_git_pass(git_oid_fromstr(&oid, g_data[4].sha)); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + cl_assert(g_data[4].type == git_object_type(obj)); + git_object_free(obj); + + return arg; +} + +void test_object_cache__fast_thread_rush(void) +{ + int try, th, data[THREADCOUNT*2]; +#ifdef GIT_THREADS + git_thread t[THREADCOUNT*2]; +#endif + + for (try = 0; try < REPEAT; ++try) { + cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); + + for (th = 0; th < THREADCOUNT*2; ++th) { + data[th] = th; +#ifdef GIT_THREADS + cl_git_pass( + git_thread_create(&t[th], NULL, cache_quick, &data[th])); +#else + cl_assert(cache_quick(&data[th]) == &data[th]); +#endif + } + +#ifdef GIT_THREADS + for (th = 0; th < THREADCOUNT*2; ++th) { + void *rval; + cl_git_pass(git_thread_join(t[th], &rval)); + cl_assert_equal_i(th, *((int *)rval)); + } +#endif + + git_repository_free(g_repo); + g_repo = NULL; } } From 81b7dec4ee21454775f932717273a90a46f78e1f Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 2 May 2013 03:06:34 -0700 Subject: [PATCH 143/384] Fix some compile warnings and trailing whitespace --- src/index.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/index.c b/src/index.c index 1771f2957..d4aa475a9 100644 --- a/src/index.c +++ b/src/index.c @@ -193,44 +193,46 @@ static int conflict_name_cmp(const void *a, const void *b) { const git_index_name_entry *name_a = a; const git_index_name_entry *name_b = b; - + if (name_a->ancestor && !name_b->ancestor) return 1; - + if (!name_a->ancestor && name_b->ancestor) return -1; - + if (name_a->ancestor) return strcmp(name_a->ancestor, name_b->ancestor); - + if (!name_a->ours || !name_b->ours) return 0; - + return strcmp(name_a->ours, name_b->ours); } /** * TODO: enable this when resolving case insensitive conflicts */ +#if 0 static int conflict_name_icmp(const void *a, const void *b) { const git_index_name_entry *name_a = a; const git_index_name_entry *name_b = b; - + if (name_a->ancestor && !name_b->ancestor) return 1; - + if (!name_a->ancestor && name_b->ancestor) return -1; - + if (name_a->ancestor) return strcasecmp(name_a->ancestor, name_b->ancestor); - + if (!name_a->ours || !name_b->ours) return 0; - + return strcasecmp(name_a->ours, name_b->ours); } +#endif static int reuc_srch(const void *key, const void *array_member) { From 8c535f3f6879c6796d8107d7eb80dd8b2105621b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 2 May 2013 03:34:56 -0700 Subject: [PATCH 144/384] Protect sha1_entry_pos call with mutex There is an occasional assertion failure in sha1_entry_pos from pack_entry_find_index when running threaded. Holding the mutex around the code that grabs the index_map data and processes it makes this assertion failure go away. --- src/pack.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/pack.c b/src/pack.c index 47534f195..1ffad29ae 100644 --- a/src/pack.c +++ b/src/pack.c @@ -1050,24 +1050,24 @@ static int pack_entry_find_offset( const git_oid *short_oid, size_t len) { - const uint32_t *level1_ofs = p->index_map.data; - const unsigned char *index = p->index_map.data; + const uint32_t *level1_ofs; + const unsigned char *index; unsigned hi, lo, stride; int pos, found = 0; const unsigned char *current = 0; *offset_out = 0; - if (index == NULL) { - int error; + if (!p->index_map.data && pack_index_open(p) < 0) + return git_odb__error_notfound("failed to open packfile", NULL); - if ((error = pack_index_open(p)) < 0) - return error; - assert(p->index_map.data); + if (git_mutex_lock(&p->lock) < 0) + return packfile_error("failed to get lock for finding entry offset"); - index = p->index_map.data; - level1_ofs = p->index_map.data; - } + assert(p->index_map.data); + + index = p->index_map.data; + level1_ofs = p->index_map.data; if (p->index_version > 1) { level1_ofs += 2; @@ -1093,6 +1093,8 @@ static int pack_entry_find_offset( /* Use git.git lookup code */ pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id); + git_mutex_unlock(&p->lock); + if (pos >= 0) { /* An object matching exactly the oid was found */ found = 1; From 7edb9071da8e78e8cf9aff969f1b8137bca3c33d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 2 May 2013 11:07:20 -0400 Subject: [PATCH 145/384] refdb_fs: do not require peeled packed refs to be tags Older versions of git would only write peeled entries for items under refs/tags/. Newer versions will write them for all refs, and we should be prepared to handle that. --- src/refdb_fs.c | 4 --- tests-clar/refs/peel.c | 31 ++++++++++++++++-- tests-clar/resources/peeled.git/HEAD | 1 + tests-clar/resources/peeled.git/config | 8 +++++ .../resources/peeled.git/objects/info/packs | 2 ++ ...4773eaf3fce1774755580e3dbb8d9f3a1adc45.idx | Bin 0 -> 1156 bytes ...773eaf3fce1774755580e3dbb8d9f3a1adc45.pack | Bin 0 -> 274 bytes tests-clar/resources/peeled.git/packed-refs | 6 ++++ .../resources/peeled.git/refs/heads/master | 1 + 9 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 tests-clar/resources/peeled.git/HEAD create mode 100644 tests-clar/resources/peeled.git/config create mode 100644 tests-clar/resources/peeled.git/objects/info/packs create mode 100644 tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx create mode 100644 tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack create mode 100644 tests-clar/resources/peeled.git/packed-refs create mode 100644 tests-clar/resources/peeled.git/refs/heads/master diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 85444b692..9ee3568da 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -132,10 +132,6 @@ static int packed_parse_peel( if (tag_ref == NULL) goto corrupt; - /* Ensure reference is a tag */ - if (git__prefixcmp(tag_ref->name, GIT_REFS_TAGS_DIR) != 0) - goto corrupt; - if (buffer + GIT_OID_HEXSZ > buffer_end) goto corrupt; diff --git a/tests-clar/refs/peel.c b/tests-clar/refs/peel.c index 34bd02ce0..f2fb6e259 100644 --- a/tests-clar/refs/peel.c +++ b/tests-clar/refs/peel.c @@ -1,19 +1,24 @@ #include "clar_libgit2.h" static git_repository *g_repo; +static git_repository *g_peel_repo; void test_refs_peel__initialize(void) { cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); + cl_git_pass(git_repository_open(&g_peel_repo, cl_fixture("peeled.git"))); } void test_refs_peel__cleanup(void) { git_repository_free(g_repo); g_repo = NULL; + git_repository_free(g_peel_repo); + g_peel_repo = NULL; } -static void assert_peel( +static void assert_peel_generic( + git_repository *repo, const char *ref_name, git_otype requested_type, const char* expected_sha, @@ -23,7 +28,7 @@ static void assert_peel( git_reference *ref; git_object *peeled; - cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name)); + cl_git_pass(git_reference_lookup(&ref, repo, ref_name)); cl_git_pass(git_reference_peel(&peeled, ref, requested_type)); @@ -36,6 +41,16 @@ static void assert_peel( git_reference_free(ref); } +static void assert_peel( + const char *ref_name, + git_otype requested_type, + const char* expected_sha, + git_otype expected_type) +{ + assert_peel_generic(g_repo, ref_name, requested_type, + expected_sha, expected_type); +} + static void assert_peel_error(int error, const char *ref_name, git_otype requested_type) { git_reference *ref; @@ -90,3 +105,15 @@ void test_refs_peel__can_peel_into_any_non_tag_object(void) assert_peel("refs/tags/test", GIT_OBJ_ANY, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT); } + +void test_refs_peel__can_peel_fully_peeled_packed_refs(void) +{ + assert_peel_generic(g_peel_repo, + "refs/tags/tag-inside-tags", GIT_OBJ_ANY, + "0df1a5865c8abfc09f1f2182e6a31be550e99f07", + GIT_OBJ_COMMIT); + assert_peel_generic(g_peel_repo, + "refs/foo/tag-outside-tags", GIT_OBJ_ANY, + "0df1a5865c8abfc09f1f2182e6a31be550e99f07", + GIT_OBJ_COMMIT); +} diff --git a/tests-clar/resources/peeled.git/HEAD b/tests-clar/resources/peeled.git/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests-clar/resources/peeled.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests-clar/resources/peeled.git/config b/tests-clar/resources/peeled.git/config new file mode 100644 index 000000000..88300524a --- /dev/null +++ b/tests-clar/resources/peeled.git/config @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true +[remote "origin"] + url = /home/peff/compile/libgit2/tests-clar/resources/peeled + fetch = +refs/*:refs/* + mirror = true diff --git a/tests-clar/resources/peeled.git/objects/info/packs b/tests-clar/resources/peeled.git/objects/info/packs new file mode 100644 index 000000000..0d88b32e5 --- /dev/null +++ b/tests-clar/resources/peeled.git/objects/info/packs @@ -0,0 +1,2 @@ +P pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack + diff --git a/tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx b/tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx new file mode 100644 index 0000000000000000000000000000000000000000..9b79e9b85f879d05623c23d8f2c6d73d6a70e807 GIT binary patch literal 1156 zcmexg;-AdGz`z8=r!Y~W~(S1(g3xJ#!=OZD+ z8cyM)c2uM$Rpkx0hi>N0C?BZ?xTt4JfokHS6|;Jse3~Ti?28bgta1e*{&H-gP8_(VeRdm99oB#j- literal 0 HcmV?d00001 diff --git a/tests-clar/resources/peeled.git/packed-refs b/tests-clar/resources/peeled.git/packed-refs new file mode 100644 index 000000000..ad053d550 --- /dev/null +++ b/tests-clar/resources/peeled.git/packed-refs @@ -0,0 +1,6 @@ +# pack-refs with: peeled fully-peeled +c2596aa0151888587ec5c0187f261e63412d9e11 refs/foo/tag-outside-tags +^0df1a5865c8abfc09f1f2182e6a31be550e99f07 +0df1a5865c8abfc09f1f2182e6a31be550e99f07 refs/heads/master +c2596aa0151888587ec5c0187f261e63412d9e11 refs/tags/tag-inside-tags +^0df1a5865c8abfc09f1f2182e6a31be550e99f07 diff --git a/tests-clar/resources/peeled.git/refs/heads/master b/tests-clar/resources/peeled.git/refs/heads/master new file mode 100644 index 000000000..76c15e203 --- /dev/null +++ b/tests-clar/resources/peeled.git/refs/heads/master @@ -0,0 +1 @@ +0df1a5865c8abfc09f1f2182e6a31be550e99f07 From 34bd59992e9e11107d16837b671f867e2a5e77ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 2 May 2013 17:14:05 +0200 Subject: [PATCH 146/384] Revert "Protect sha1_entry_pos call with mutex" This reverts commit 8c535f3f6879c6796d8107d7eb80dd8b2105621b. --- src/pack.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/pack.c b/src/pack.c index 1ffad29ae..47534f195 100644 --- a/src/pack.c +++ b/src/pack.c @@ -1050,24 +1050,24 @@ static int pack_entry_find_offset( const git_oid *short_oid, size_t len) { - const uint32_t *level1_ofs; - const unsigned char *index; + const uint32_t *level1_ofs = p->index_map.data; + const unsigned char *index = p->index_map.data; unsigned hi, lo, stride; int pos, found = 0; const unsigned char *current = 0; *offset_out = 0; - if (!p->index_map.data && pack_index_open(p) < 0) - return git_odb__error_notfound("failed to open packfile", NULL); + if (index == NULL) { + int error; - if (git_mutex_lock(&p->lock) < 0) - return packfile_error("failed to get lock for finding entry offset"); + if ((error = pack_index_open(p)) < 0) + return error; + assert(p->index_map.data); - assert(p->index_map.data); - - index = p->index_map.data; - level1_ofs = p->index_map.data; + index = p->index_map.data; + level1_ofs = p->index_map.data; + } if (p->index_version > 1) { level1_ofs += 2; @@ -1093,8 +1093,6 @@ static int pack_entry_find_offset( /* Use git.git lookup code */ pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id); - git_mutex_unlock(&p->lock); - if (pos >= 0) { /* An object matching exactly the oid was found */ found = 1; From 3bb00f3360bd11a48e1b04dc7dec971f0019891f Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 2 May 2013 17:17:46 +0200 Subject: [PATCH 147/384] refdb_fs: implement the fully-peeled trait --- src/refdb_fs.c | 7 ------- src/refs.h | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 9ee3568da..8a2d56327 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -677,13 +677,6 @@ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref) if (ref->flags & GIT_PACKREF_HAS_PEEL) return 0; - /* - * Only applies to tags, i.e. references - * in the /refs/tags folder - */ - if (git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) != 0) - return 0; - /* * Find the tagged object in the repository */ diff --git a/src/refs.h b/src/refs.h index 908e86f29..927bc83ce 100644 --- a/src/refs.h +++ b/src/refs.h @@ -26,7 +26,7 @@ #define GIT_SYMREF "ref: " #define GIT_PACKEDREFS_FILE "packed-refs" -#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled " +#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled" #define GIT_PACKEDREFS_FILE_MODE 0666 #define GIT_HEAD_FILE "HEAD" From f69db390fb5cbacbb1c63d146aef4e0fb6754ddf Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 2 May 2013 17:29:58 +0200 Subject: [PATCH 148/384] refdb_fs: store "cannot be peeled" flag for packed refs Fixes #1532 --- src/refdb_fs.c | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 8a2d56327..00d1c4fd5 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -26,8 +26,15 @@ GIT__USE_STRMAP; #define MAX_NESTING_LEVEL 10 enum { - GIT_PACKREF_HAS_PEEL = 1, - GIT_PACKREF_WAS_LOOSE = 2 + PACKREF_HAS_PEEL = 1, + PACKREF_WAS_LOOSE = 2, + PACKREF_CANNOT_PEEL = 4 +}; + +enum { + PEELING_NONE = 0, + PEELING_STANDARD, + PEELING_FULL }; struct packref { @@ -44,6 +51,7 @@ typedef struct refdb_fs_backend { char *path; git_refcache refcache; + int peeling_mode; } refdb_fs_backend; static int reference_read( @@ -150,6 +158,7 @@ static int packed_parse_peel( goto corrupt; } + tag_ref->flags |= PACKREF_HAS_PEEL; *buffer_out = buffer; return 0; @@ -201,6 +210,25 @@ static int packed_load(refdb_fs_backend *backend) buffer_start = (const char *)packfile.ptr; buffer_end = (const char *)(buffer_start) + packfile.size; + backend->peeling_mode = PEELING_NONE; + + if (buffer_start[0] == '#') { + static const char *traits_header = "# pack-refs with: "; + + if (git__prefixcmp(buffer_start, traits_header) == 0) { + const char *traits = buffer_start + strlen(traits_header); + const char *traits_end = strchr(traits, '\n'); + + if (strstr(traits, "fully-peeled") != NULL) { + backend->peeling_mode = PEELING_FULL; + } else if (strstr(traits, "peeled") != NULL) { + backend->peeling_mode = PEELING_STANDARD; + } + + buffer_start = traits_end + 1; + } + } + while (buffer_start < buffer_end && buffer_start[0] == '#') { buffer_start = strchr(buffer_start, '\n'); if (buffer_start == NULL) @@ -219,6 +247,8 @@ static int packed_load(refdb_fs_backend *backend) if (buffer_start[0] == '^') { if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0) goto parse_failed; + } else if (backend->peeling_mode == PEELING_FULL) { + ref->flags |= PACKREF_CANNOT_PEEL; } git_strmap_insert(ref_cache->packfile, ref->name, ref, err); @@ -291,7 +321,7 @@ static int loose_lookup_to_packfile( return -1; } - ref->flags = GIT_PACKREF_WAS_LOOSE; + ref->flags = PACKREF_WAS_LOOSE; *ref_out = ref; git_buf_free(&ref_file); @@ -674,7 +704,7 @@ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref) { git_object *object; - if (ref->flags & GIT_PACKREF_HAS_PEEL) + if (ref->flags & PACKREF_HAS_PEEL || ref->flags & PACKREF_CANNOT_PEEL) return 0; /* @@ -695,7 +725,7 @@ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref) * Find the object pointed at by this tag */ git_oid_cpy(&ref->peel, git_tag_target_id(tag)); - ref->flags |= GIT_PACKREF_HAS_PEEL; + ref->flags |= PACKREF_HAS_PEEL; /* * The reference has now cached the resolved OID, and is @@ -728,7 +758,7 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file) * This obviously only applies to tags. * The required peels have already been loaded into `ref->peel_target`. */ - if (ref->flags & GIT_PACKREF_HAS_PEEL) { + if (ref->flags & PACKREF_HAS_PEEL) { char peel[GIT_OID_HEXSZ + 1]; git_oid_fmt(peel, &ref->peel); peel[GIT_OID_HEXSZ] = 0; @@ -765,7 +795,7 @@ static int packed_remove_loose( for (i = 0; i < packing_list->length; ++i) { struct packref *ref = git_vector_get(packing_list, i); - if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0) + if ((ref->flags & PACKREF_WAS_LOOSE) == 0) continue; if (git_buf_joinpath(&full_path, backend->path, ref->name) < 0) From 1022db2b6860d602e79169906eee4f299855975b Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 2 May 2013 17:42:09 +0200 Subject: [PATCH 149/384] refdb_fs: Traits are always surrounded by spaces This makes parsing easier! :p --- src/refdb_fs.c | 4 ++-- src/refs.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 00d1c4fd5..6a6f589f0 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -219,9 +219,9 @@ static int packed_load(refdb_fs_backend *backend) const char *traits = buffer_start + strlen(traits_header); const char *traits_end = strchr(traits, '\n'); - if (strstr(traits, "fully-peeled") != NULL) { + if (strstr(traits, " fully-peeled ") != NULL) { backend->peeling_mode = PEELING_FULL; - } else if (strstr(traits, "peeled") != NULL) { + } else if (strstr(traits, " peeled ") != NULL) { backend->peeling_mode = PEELING_STANDARD; } diff --git a/src/refs.h b/src/refs.h index 927bc83ce..f487ee3fc 100644 --- a/src/refs.h +++ b/src/refs.h @@ -26,7 +26,7 @@ #define GIT_SYMREF "ref: " #define GIT_PACKEDREFS_FILE "packed-refs" -#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled" +#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled " #define GIT_PACKEDREFS_FILE_MODE 0666 #define GIT_HEAD_FILE "HEAD" From 822645f6298ae0ff86fa717a79c5b7e105bc4a0d Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 2 May 2013 17:48:49 +0200 Subject: [PATCH 150/384] refdb_fs: Only strstr the traits line --- src/refdb_fs.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 6a6f589f0..2c45eabb7 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -216,8 +216,13 @@ static int packed_load(refdb_fs_backend *backend) static const char *traits_header = "# pack-refs with: "; if (git__prefixcmp(buffer_start, traits_header) == 0) { - const char *traits = buffer_start + strlen(traits_header); - const char *traits_end = strchr(traits, '\n'); + char *traits = (char *)buffer_start + strlen(traits_header); + char *traits_end = strchr(traits, '\n'); + + if (traits_end == NULL) + goto parse_failed; + + *traits_end = '\0'; if (strstr(traits, " fully-peeled ") != NULL) { backend->peeling_mode = PEELING_FULL; From a591ed3ea9e46771510628f1f677f2f3791078d6 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 2 May 2013 12:06:46 -0400 Subject: [PATCH 151/384] refdb_fs: respect PEELING_STANDARD We only set our negative flag for PEELING_FULL; we can fall back to the lesser PEELING_STANDARD if our ref is in the refs/tags/ hierarchy. --- src/refdb_fs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 2c45eabb7..c0a32bae7 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -252,7 +252,9 @@ static int packed_load(refdb_fs_backend *backend) if (buffer_start[0] == '^') { if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0) goto parse_failed; - } else if (backend->peeling_mode == PEELING_FULL) { + } else if (backend->peeling_mode == PEELING_FULL || + (backend->peeling_mode == PEELING_STANDARD && + git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0)) { ref->flags |= PACKREF_CANNOT_PEEL; } From 0ddfcb40d5ddf2e6d74f061efcccd944d18460cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 2 May 2013 18:06:14 +0200 Subject: [PATCH 152/384] Switch to index_version as "git_pack_file is ready" flag We use p->index_map.data to check whether the struct has been set up and all the information about the index is stored there. This variable gets set up halfway through the setup process, however, and a thread can come along and use fields that haven't been written to yet. Crucially, pack_entry_find_offset() needs to read the index version (which is written after index_map) to know the offset and stride length to pass to sha1_entry_pos(). If these values are wrong, assertions in it will fail, as it will be reading bogus data. Make index_version the last field to be written and switch from using p->index_map.data to p->index_version as "git_pack_file is ready" flag as we can use it to know if every field has been written. --- src/pack.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pack.c b/src/pack.c index 47534f195..417d225f3 100644 --- a/src/pack.c +++ b/src/pack.c @@ -293,8 +293,8 @@ static int pack_index_check(const char *path, struct git_pack_file *p) } } - p->index_version = version; p->num_objects = nr; + p->index_version = version; return 0; } @@ -304,7 +304,7 @@ static int pack_index_open(struct git_pack_file *p) int error = 0; size_t name_len, base_len; - if (p->index_map.data) + if (p->index_version > -1) return 0; name_len = strlen(p->pack_name); @@ -320,7 +320,7 @@ static int pack_index_open(struct git_pack_file *p) if ((error = git_mutex_lock(&p->lock)) < 0) return error; - if (!p->index_map.data) + if (p->index_version == -1) error = pack_index_check(idx_name, p); git__free(idx_name); @@ -826,7 +826,7 @@ static int packfile_open(struct git_pack_file *p) git_oid sha1; unsigned char *idx_sha1; - if (!p->index_map.data && pack_index_open(p) < 0) + if (p->index_version == -1 && pack_index_open(p) < 0) return git_odb__error_notfound("failed to open packfile", NULL); /* if mwf opened by another thread, return now */ @@ -942,6 +942,7 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) p->mwf.size = st.st_size; p->pack_local = 1; p->mtime = (git_time_t)st.st_mtime; + p->index_version = -1; git_mutex_init(&p->lock); @@ -1058,7 +1059,7 @@ static int pack_entry_find_offset( *offset_out = 0; - if (index == NULL) { + if (p->index_version == -1) { int error; if ((error = pack_index_open(p)) < 0) From 0cce210a54b931462c402c1cb79091474d0b8577 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 2 May 2013 10:36:58 -0700 Subject: [PATCH 153/384] Use assert for peel target type check --- src/object.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/object.c b/src/object.c index 43aecf434..a6807f26b 100644 --- a/src/object.c +++ b/src/object.c @@ -318,16 +318,11 @@ int git_object_peel( if (git_object_type(object) == target_type) return git_object_dup(peeled, (git_object *)object); - if (target_type != GIT_OBJ_TAG && - target_type != GIT_OBJ_COMMIT && - target_type != GIT_OBJ_TREE && - target_type != GIT_OBJ_BLOB && - target_type != GIT_OBJ_ANY) { - - giterr_set(GITERR_OBJECT, "Cannot peel to object type %d", - (int)target_type); - return GIT_EINVALIDSPEC; - } + assert(target_type == GIT_OBJ_TAG || + target_type == GIT_OBJ_COMMIT || + target_type == GIT_OBJ_TREE || + target_type == GIT_OBJ_BLOB || + target_type == GIT_OBJ_ANY); source = (git_object *)object; From 4e7c15608f427466ef941cad45b27f1ad30bd25a Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 2 May 2013 14:58:40 -0500 Subject: [PATCH 154/384] puns are not funny; type punning especially so --- src/merge.c | 22 ++++++++++++++++------ tests-clar/merge/merge_helpers.c | 6 +++--- tests-clar/merge/merge_helpers.h | 7 ++----- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/merge.c b/src/merge.c index 320be005a..56290bfad 100644 --- a/src/merge.c +++ b/src/merge.c @@ -357,7 +357,7 @@ static int merge_conflict_resolve_trivial( git_merge_diff_list *diff_list, const git_merge_diff *conflict) { - int ancestor_empty, ours_empty, theirs_empty; + int ours_empty, theirs_empty; int ours_changed, theirs_changed, ours_theirs_differ; git_index_entry const *result = NULL; int error = 0; @@ -374,7 +374,6 @@ static int merge_conflict_resolve_trivial( conflict->their_status == GIT_DELTA_RENAMED) return 0; - ancestor_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry); ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry); theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry); @@ -678,6 +677,7 @@ static int index_entry_similarity_calc( { git_blob *blob; git_diff_file diff_file = {{{0}}}; + git_off_t blobsize; int error; *out = NULL; @@ -691,8 +691,14 @@ static int index_entry_similarity_calc( diff_file.mode = entry->mode; diff_file.flags = 0; + blobsize = git_blob_rawsize(blob); + + /* file too big for rename processing */ + if (!git__is_sizet(blobsize)) + return 0; + error = opts->metric->buffer_signature(out, &diff_file, - git_blob_rawcontent(blob), git_blob_rawsize(blob), + git_blob_rawcontent(blob), (size_t)blobsize, opts->metric->payload); git_blob_free(blob); @@ -1273,7 +1279,7 @@ int git_merge_diff_list__find_differences( git_vector_cmp entry_compare = git_index_entry__cmp; struct merge_diff_df_data df_data = {0}; int cur_item_modified; - size_t i; + size_t i, j; int error = 0; assert(diff_list && our_tree && their_tree); @@ -1290,7 +1296,9 @@ int git_merge_diff_list__find_differences( } while (true) { - memset(cur_items, 0x0, sizeof(git_index_entry *) * 3); + for (i = 0; i < 3; i++) + cur_items[i] = NULL; + best_cur_item = NULL; cur_item_modified = 0; @@ -1312,7 +1320,9 @@ int git_merge_diff_list__find_differences( * Found an item that sorts before our current item, make * our current item this one. */ - memset(cur_items, 0x0, sizeof(git_index_entry *) * 3); + for (j = 0; j < i; j++) + cur_items[j] = NULL; + cur_item_modified = 1; best_cur_item = items[i]; cur_items[i] = items[i]; diff --git a/tests-clar/merge/merge_helpers.c b/tests-clar/merge/merge_helpers.c index 5c3421e7e..71bb96781 100644 --- a/tests-clar/merge/merge_helpers.c +++ b/tests-clar/merge/merge_helpers.c @@ -149,9 +149,9 @@ static int name_entry_eq_merge_name_entry(const struct merge_name_entry *expecte static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual) { - if (!index_entry_eq_merge_index_entry((const struct merge_index_entry *)&expected->ancestor, &actual->ancestor_entry) || - !index_entry_eq_merge_index_entry((const struct merge_index_entry *)&expected->ours, &actual->our_entry) || - !index_entry_eq_merge_index_entry((const struct merge_index_entry *)&expected->theirs, &actual->their_entry)) + if (!index_entry_eq_merge_index_entry(&expected->ancestor.entry, &actual->ancestor_entry) || + !index_entry_eq_merge_index_entry(&expected->ours.entry, &actual->our_entry) || + !index_entry_eq_merge_index_entry(&expected->theirs.entry, &actual->their_entry)) return 0; if (expected->ours.status != actual->our_status || diff --git a/tests-clar/merge/merge_helpers.h b/tests-clar/merge/merge_helpers.h index 1a0b8921b..cb718e01a 100644 --- a/tests-clar/merge/merge_helpers.h +++ b/tests-clar/merge/merge_helpers.h @@ -18,11 +18,8 @@ struct merge_name_entry { }; struct merge_index_with_status { - uint16_t mode; - char oid_str[41]; - int stage; - char path[128]; - unsigned int status; + struct merge_index_entry entry; + unsigned int status; }; struct merge_reuc_entry { From 5e151329fbb048dfc74f37432f619a4e331d24f7 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 2 May 2013 15:19:49 -0500 Subject: [PATCH 155/384] braces --- tests-clar/merge/trees/treediff.c | 360 +++++++++++++++--------------- 1 file changed, 180 insertions(+), 180 deletions(-) diff --git a/tests-clar/merge/trees/treediff.c b/tests-clar/merge/trees/treediff.c index 5da6f7658..06ea94e0d 100644 --- a/tests-clar/merge/trees/treediff.c +++ b/tests-clar/merge/trees/treediff.c @@ -90,51 +90,51 @@ void test_merge_trees_treediff__simple(void) { struct merge_index_conflict_data treediff_conflict_data[] = { { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt", GIT_DELTA_ADDED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE }, { - { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt", GIT_DELTA_MODIFIED }, - { 0100644, "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe", 0, "automergeable.txt", GIT_DELTA_MODIFIED }, + { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_BOTH_MODIFIED }, { - { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt", GIT_DELTA_MODIFIED }, + { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_NONE }, { - { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt", GIT_DELTA_MODIFIED }, - { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE }, { - { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt", GIT_DELTA_MODIFIED }, - { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt", GIT_DELTA_MODIFIED }, + { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_BOTH_MODIFIED }, { - { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, + { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_NONE }, { - { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE }, }; @@ -146,142 +146,142 @@ void test_merge_trees_treediff__df_conflicts(void) { struct merge_index_conflict_data treediff_conflict_data[] = { { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 0, "dir-10", GIT_DELTA_ADDED }, - { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 0, "dir-10", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 0, "dir-10" }, GIT_DELTA_ADDED }, + { { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 0, "dir-10" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_BOTH_ADDED, }, { - { 0100644, "242591eb280ee9eeb2ce63524b9a8b9bc4cb515d", 0, "dir-10/file.txt", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, + { { 0100644, "242591eb280ee9eeb2ce63524b9a8b9bc4cb515d", 0, "dir-10/file.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_BOTH_DELETED, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, + { { 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_NONE, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 0, "dir-7", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 0, "dir-7" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_DIRECTORY_FILE, }, { - { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 0, "dir-7/file.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 0, "dir-7/file.txt", GIT_DELTA_MODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, + { { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 0, "dir-7/file.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 0, "dir-7/file.txt" }, GIT_DELTA_MODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_DF_CHILD, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8", GIT_DELTA_ADDED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" }, GIT_DELTA_ADDED }, + { {0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 0, "dir-9", GIT_DELTA_ADDED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 0, "dir-9" }, GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_DIRECTORY_FILE, }, { - { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 0, "dir-9/file.txt", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 0, "dir-9/file.txt", GIT_DELTA_MODIFIED }, + { { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 0, "dir-9/file.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 0, "dir-9/file.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_DF_CHILD, }, { - { 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1", GIT_DELTA_UNMODIFIED }, - { 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, + { { 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_NONE, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 0, "file-2", GIT_DELTA_UNMODIFIED }, - { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 0, "file-2", GIT_DELTA_MODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, + { { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 0, "file-2" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 0, "file-2" }, GIT_DELTA_MODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_DIRECTORY_FILE, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_DF_CHILD, }, { - { 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3", GIT_DELTA_UNMODIFIED }, + { { 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new", GIT_DELTA_ADDED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" }, GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 0, "file-4", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 0, "file-4", GIT_DELTA_MODIFIED }, + { { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 0, "file-4" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 0, "file-4" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_DIRECTORY_FILE, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new", GIT_DELTA_ADDED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" }, GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_DF_CHILD, }, { - { 0100644, "ac4045f965119e6998f4340ed0f411decfb3ec05", 0, "file-5", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, + { { 0100644, "ac4045f965119e6998f4340ed0f411decfb3ec05", 0, "file-5" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_BOTH_DELETED, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 0, "file-5/new", GIT_DELTA_ADDED }, - { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 0, "file-5/new", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 0, "file-5/new" }, GIT_DELTA_ADDED }, + { { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 0, "file-5/new" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_BOTH_ADDED, }, }; @@ -293,58 +293,58 @@ void test_merge_trees_treediff__strict_renames(void) { struct merge_index_conflict_data treediff_conflict_data[] = { { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt", GIT_DELTA_ADDED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt", GIT_DELTA_MODIFIED }, - { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt", GIT_DELTA_MODIFIED }, - { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt", GIT_DELTA_MODIFIED }, - { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "renamed-in-branch.txt", GIT_DELTA_RENAMED }, + { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "renamed-in-branch.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "renamed.txt", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "renamed.txt" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "copied.txt", GIT_DELTA_RENAMED }, + { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "copied.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_NONE, }, }; @@ -356,128 +356,128 @@ void test_merge_trees_treediff__rename_conflicts(void) { struct merge_index_conflict_data treediff_conflict_data[] = { { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt", GIT_DELTA_ADDED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" }, GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-rewritten-in-ours.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 0, "0b-rewritten-in-ours.txt", GIT_DELTA_MODIFIED }, - { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 0, "0b-rewritten-in-ours.txt", GIT_DELTA_MODIFIED }, + { { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-rewritten-in-ours.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 0, "0b-rewritten-in-ours.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 0, "0b-rewritten-in-ours.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_BOTH_MODIFIED, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-rewritten-in-theirs.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt", GIT_DELTA_MODIFIED }, - { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 0, "0c-rewritten-in-theirs.txt", GIT_DELTA_MODIFIED }, + { { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_BOTH_MODIFIED, }, { - { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-renamed-in-ours-edited-in-theirs.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-newname-in-ours-edited-in-theirs.txt", GIT_DELTA_RENAMED }, - { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-renamed-in-ours-edited-in-theirs.txt", GIT_DELTA_MODIFIED }, + { { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-renamed-in-ours-edited-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-newname-in-ours-edited-in-theirs.txt" }, GIT_DELTA_RENAMED }, + { { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-renamed-in-ours-edited-in-theirs.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_RENAMED_MODIFIED, }, { - { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt", GIT_DELTA_RENAMED }, - { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" }, GIT_DELTA_RENAMED }, + { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-renamed-in-theirs-edited-in-ours.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-renamed-in-theirs-edited-in-ours.txt", GIT_DELTA_MODIFIED }, - { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt", GIT_DELTA_RENAMED }, + { { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-renamed-in-theirs-edited-in-ours.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-renamed-in-theirs-edited-in-ours.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_RENAMED_MODIFIED, }, { - { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt", GIT_DELTA_RENAMED }, + { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-renamed-in-both.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt", GIT_DELTA_RENAMED }, - { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt", GIT_DELTA_RENAMED }, + { { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-renamed-in-both.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" }, GIT_DELTA_RENAMED }, + { { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_BOTH_RENAMED, }, { - { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-renamed-in-ours-deleted-in-theirs.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt", GIT_DELTA_RENAMED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, + { { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-renamed-in-ours-deleted-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" }, GIT_DELTA_RENAMED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_RENAMED_DELETED, }, { - { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-renamed-in-theirs-deleted-in-ours.txt", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt", GIT_DELTA_RENAMED }, + { { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-renamed-in-theirs-deleted-in-ours.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_RENAMED_DELETED, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_RENAMED_ADDED, }, { - { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-renamed-in-ours-added-in-theirs.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt", GIT_DELTA_RENAMED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, + { { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-renamed-in-ours-added-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt" }, GIT_DELTA_RENAMED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_RENAMED_ADDED, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt", GIT_DELTA_ADDED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt" }, GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_RENAMED_ADDED, }, { - { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-renamed-in-theirs-added-in-ours.txt", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt", GIT_DELTA_RENAMED }, + { { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-renamed-in-theirs-added-in-ours.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_RENAMED_ADDED, }, { - { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-ours.txt", GIT_DELTA_RENAMED }, - { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-theirs.txt", GIT_DELTA_RENAMED }, + { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-ours.txt" }, GIT_DELTA_RENAMED }, + { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-theirs.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2, }, { - { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed.txt", GIT_DELTA_RENAMED }, - { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed.txt" }, GIT_DELTA_RENAMED }, + { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1, }, { - { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed.txt", GIT_DELTA_RENAMED }, + { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1, }, }; @@ -490,51 +490,51 @@ void test_merge_trees_treediff__best_renames(void) { struct merge_index_conflict_data treediff_conflict_data[] = { { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt", GIT_DELTA_ADDED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt", GIT_DELTA_MODIFIED }, - { 0100644, "45299c1ca5e07bba1fd90843056fb559f96b1f5a", 0, "renamed-90.txt", GIT_DELTA_RENAMED }, + { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "45299c1ca5e07bba1fd90843056fb559f96b1f5a", 0, "renamed-90.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_RENAMED_MODIFIED, }, { - { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt", GIT_DELTA_MODIFIED }, - { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED }, - { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt", GIT_DELTA_MODIFIED }, - { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED }, + { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, { - { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt",GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_DELETED }, - { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt", GIT_DELTA_UNMODIFIED }, + { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" },GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_DELETED }, + { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_MODIFIED_DELETED, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "5843febcb23480df0b5edb22a21c59c772bb8e29", 0, "renamed-50.txt", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "5843febcb23480df0b5edb22a21c59c772bb8e29", 0, "renamed-50.txt" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_NONE, }, { - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0, "", 0, "", GIT_DELTA_UNMODIFIED }, - { 0100644, "a77a56a49f8f3ae242e02717f18ebbc60c5cc543", 0, "renamed-75.txt", GIT_DELTA_ADDED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, + { { 0100644, "a77a56a49f8f3ae242e02717f18ebbc60c5cc543", 0, "renamed-75.txt" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_NONE, }, }; From d80416384f67474cd98a8fafe2cc1e4f1f5fa38b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 2 May 2013 17:22:13 -0500 Subject: [PATCH 156/384] fix some leaks --- src/diff.c | 3 ++- src/index.c | 1 + src/transports/smart_protocol.c | 6 ++++++ tests-clar/commit/parse.c | 4 ---- tests-clar/submodule/status.c | 2 ++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/diff.c b/src/diff.c index a154e67e8..fbff1a6fb 100644 --- a/src/diff.c +++ b/src/diff.c @@ -716,7 +716,7 @@ static int diff_scan_inside_untracked_dir( error = git_iterator_advance(&info->nitem, info->new_iter); } - return error; + goto done; } /* look for actual untracked file */ @@ -747,6 +747,7 @@ static int diff_scan_inside_untracked_dir( break; } +done: git_buf_free(&base); return error; diff --git a/src/index.c b/src/index.c index d4aa475a9..ee659f8ab 100644 --- a/src/index.c +++ b/src/index.c @@ -349,6 +349,7 @@ static void index_free(git_index *index) { git_index_clear(index); git_vector_free(&index->entries); + git_vector_free(&index->names); git_vector_free(&index->reuc); git__free(index->index_file_path); diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index a5ad1e422..765b914b7 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -23,10 +23,16 @@ int git_smart__store_refs(transport_smart *t, int flushes) int error, flush = 0, recvd; const char *line_end; git_pkt *pkt; + git_pkt_ref *ref; + size_t i; /* Clear existing refs in case git_remote_connect() is called again * after git_remote_disconnect(). */ + git_vector_foreach(refs, i, ref) { + git__free(ref->head.name); + git__free(ref); + } git_vector_clear(refs); do { diff --git a/tests-clar/commit/parse.c b/tests-clar/commit/parse.c index 8de4401dc..415860a6e 100644 --- a/tests-clar/commit/parse.c +++ b/tests-clar/commit/parse.c @@ -386,10 +386,6 @@ This commit has a few LF at the start of the commit message"; \n\ This commit has a few LF at the start of the commit message"; - commit = (git_commit*)git__malloc(sizeof(git_commit)); - memset(commit, 0x0, sizeof(git_commit)); - commit->object.repo = g_repo; - cl_git_pass(parse_commit(&commit, buffer)); cl_assert_equal_s(message, git_commit_message(commit)); git_commit__free(commit); diff --git a/tests-clar/submodule/status.c b/tests-clar/submodule/status.c index fca84af63..88f388052 100644 --- a/tests-clar/submodule/status.c +++ b/tests-clar/submodule/status.c @@ -409,4 +409,6 @@ void test_submodule_status__untracked_dirs_containing_ignored_files(void) GIT_SUBMODULE_STATUS_IN_WD; cl_assert(status == expected); + + git_buf_free(&path); } From 297758dce3bd012b17da379996dca4413d9d651c Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Fri, 3 May 2013 10:37:33 -0400 Subject: [PATCH 157/384] Added ssh transport file --- include/git2/transport.h | 11 ++ src/transport.c | 6 +- src/transports/ssh.c | 356 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 src/transports/ssh.c diff --git a/include/git2/transport.h b/include/git2/transport.h index 5e9968363..9b9ecc5fc 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -319,6 +319,17 @@ GIT_EXTERN(int) git_smart_subtransport_git( git_smart_subtransport **out, git_transport* owner); +/** + * Create an instance of the ssh subtransport. + * + * @param out The newly created subtransport + * @param owner The smart transport to own this subtransport + * @return 0 or an error code + */ +GIT_EXTERN(int) git_smart_subtransport_ssh( + git_smart_subtransport **out, + git_transport* owner); + /* *** End interface for subtransports for the smart transport *** */ diff --git a/src/transport.c b/src/transport.c index adb6d5355..7b7a5aa85 100644 --- a/src/transport.c +++ b/src/transport.c @@ -23,14 +23,16 @@ static transport_definition dummy_transport_definition = { NULL, 1, git_transpor static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1 }; static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0 }; +static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0 }; static transport_definition transports[] = { {"git://", 1, git_transport_smart, &git_subtransport_definition}, {"http://", 1, git_transport_smart, &http_subtransport_definition}, {"https://", 1, git_transport_smart, &http_subtransport_definition}, {"file://", 1, git_transport_local, NULL}, - {"git+ssh://", 1, git_transport_dummy, NULL}, - {"ssh+git://", 1, git_transport_dummy, NULL}, + {"git+ssh://", 1, git_transport_smart, &git_smart_subtransport_ssh}, + {"ssh+git://", 1, git_transport_smart, &git_smart_subtransport_ssh}, + {"git@", 1, git_transport_smart, &git_smart_subtransport_ssh}, {NULL, 0, 0} }; diff --git a/src/transports/ssh.c b/src/transports/ssh.c new file mode 100644 index 000000000..a7f21f554 --- /dev/null +++ b/src/transports/ssh.c @@ -0,0 +1,356 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "git2.h" +#include "buffer.h" +#include "netops.h" + +#include + +#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport) + +static const char prefix_git_ssh[] = "git+ssh://"; +static const char prefix_ssh_git[] = "ssh+git://"; +static const char prefix_git[] = "git@"; +static const char cmd_uploadpack[] = "git-upload-pack"; +static const char cmd_receivepack[] = "git-receive-pack"; + +typedef struct { + git_smart_subtransport_stream parent; + gitno_socket socket; + const char *cmd; + char *url; + unsigned sent_command : 1; +} ssh_stream; + +typedef struct { + git_smart_subtransport parent; + git_transport *owner; + git_stream *current_stream; +} ssh_subtransport; + +/* + * Create a git protocol request. + * + * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0 + */ +static int gen_proto(git_buf *request, const char *cmd, const char *url) +{ + char *delim, *repo; + char host[] = "host="; + size_t len; + + delim = strchr(url, '/'); + if (delim == NULL) { + giterr_set(GITERR_NET, "Malformed URL"); + return -1; + } + + repo = delim; + + delim = strchr(url, ':'); + if (delim == NULL) + delim = strchr(url, '/'); + + len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1; + + git_buf_grow(request, len); + git_buf_printf(request, "%04x%s %s%c%s", + (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host); + git_buf_put(request, url, delim - url); + git_buf_putc(request, '\0'); + + if (git_buf_oom(request)) + return -1; + + return 0; +} + +static int send_command(git_stream *s) +{ + int error; + git_buf request = GIT_BUF_INIT; + + error = gen_proto(&request, s->cmd, s->url); + if (error < 0) + goto cleanup; + + /* It looks like negative values are errors here, and positive values + * are the number of bytes sent. */ + error = gitno_send(&s->socket, request.ptr, request.size, 0); + + if (error >= 0) + s->sent_command = 1; + +cleanup: + git_buf_free(&request); + return error; +} + +static int git_stream_read( + git_smart_subtransport_stream *stream, + char *buffer, + size_t buf_size, + size_t *bytes_read) +{ + git_stream *s = (git_stream *)stream; + gitno_buffer buf; + + *bytes_read = 0; + + if (!s->sent_command && send_command(s) < 0) + return -1; + + gitno_buffer_setup(&s->socket, &buf, buffer, buf_size); + + if (gitno_recv(&buf) < 0) + return -1; + + *bytes_read = buf.offset; + + return 0; +} + +static int git_stream_write( + git_smart_subtransport_stream *stream, + const char *buffer, + size_t len) +{ + git_stream *s = (git_stream *)stream; + + if (!s->sent_command && send_command(s) < 0) + return -1; + + return gitno_send(&s->socket, buffer, len, 0); +} + +static void git_stream_free(git_smart_subtransport_stream *stream) +{ + git_stream *s = (git_stream *)stream; + ssh_subtransport *t = OWNING_SUBTRANSPORT(s); + int ret; + + GIT_UNUSED(ret); + + t->current_stream = NULL; + + if (s->socket.socket) { + ret = gitno_close(&s->socket); + assert(!ret); + } + + git__free(s->url); + git__free(s); +} + +static int git_stream_alloc( + ssh_subtransport *t, + const char *url, + const char *cmd, + git_smart_subtransport_stream **stream) +{ + git_stream *s; + + if (!stream) + return -1; + + s = git__calloc(sizeof(git_stream), 1); + GITERR_CHECK_ALLOC(s); + + s->parent.subtransport = &t->parent; + s->parent.read = git_stream_read; + s->parent.write = git_stream_write; + s->parent.free = git_stream_free; + + s->cmd = cmd; + s->url = git__strdup(url); + + if (!s->url) { + git__free(s); + return -1; + } + + *stream = &s->parent; + return 0; +} + +static int _git_uploadpack_ls( + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) +{ + char *host, *port, *user=NULL, *pass=NULL; + git_stream *s; + + *stream = NULL; + + if (!git__prefixcmp(url, prefix_git)) + url += strlen(prefix_git); + + if (git_stream_alloc(t, url, cmd_uploadpack, stream) < 0) + return -1; + + s = (git_stream *)*stream; + + if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) + goto on_error; + + if (gitno_connect(&s->socket, host, port, 0) < 0) + goto on_error; + + t->current_stream = s; + git__free(host); + git__free(port); + git__free(user); + git__free(pass); + return 0; + +on_error: + if (*stream) + git_stream_free(*stream); + + git__free(host); + git__free(port); + return -1; +} + +static int _git_uploadpack( + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) +{ + GIT_UNUSED(url); + + if (t->current_stream) { + *stream = &t->current_stream->parent; + return 0; + } + + giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK"); + return -1; +} + +static int _git_receivepack_ls( + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) +{ + char *host, *port, *user=NULL, *pass=NULL; + git_stream *s; + + *stream = NULL; + + if (!git__prefixcmp(url, prefix_git)) + url += strlen(prefix_git); + + if (git_stream_alloc(t, url, cmd_receivepack, stream) < 0) + return -1; + + s = (git_stream *)*stream; + + if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) + goto on_error; + + if (gitno_connect(&s->socket, host, port, 0) < 0) + goto on_error; + + t->current_stream = s; + git__free(host); + git__free(port); + git__free(user); + git__free(pass); + return 0; + +on_error: + if (*stream) + git_stream_free(*stream); + + git__free(host); + git__free(port); + return -1; +} + +static int _git_receivepack( + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) +{ + GIT_UNUSED(url); + + if (t->current_stream) { + *stream = &t->current_stream->parent; + return 0; + } + + giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK"); + return -1; +} + +static int _git_action( + git_smart_subtransport_stream **stream, + git_smart_subtransport *subtransport, + const char *url, + git_smart_service_t action) +{ + ssh_subtransport *t = (ssh_subtransport *) subtransport; + + switch (action) { + case GIT_SERVICE_UPLOADPACK_LS: + return _git_uploadpack_ls(t, url, stream); + + case GIT_SERVICE_UPLOADPACK: + return _git_uploadpack(t, url, stream); + + case GIT_SERVICE_RECEIVEPACK_LS: + return _git_receivepack_ls(t, url, stream); + + case GIT_SERVICE_RECEIVEPACK: + return _git_receivepack(t, url, stream); + } + + *stream = NULL; + return -1; +} + +static int _git_close(git_smart_subtransport *subtransport) +{ + ssh_subtransport *t = (ssh_subtransport *) subtransport; + + assert(!t->current_stream); + + GIT_UNUSED(t); + + return 0; +} + +static void _git_free(git_smart_subtransport *subtransport) +{ + ssh_subtransport *t = (ssh_subtransport *) subtransport; + + assert(!t->current_stream); + + git__free(t); +} + +int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owner) +{ + ssh_subtransport *t; + + if (!out) + return -1; + + t = git__calloc(sizeof(ssh_subtransport), 1); + GITERR_CHECK_ALLOC(t); + + t->owner = owner; + t->parent.action = _git_action; + t->parent.close = _git_close; + t->parent.free = _git_free; + + *out = (git_smart_subtransport *) t; + return 0; +} From 8ae55d940f0687988fcf22f5e775339cf070dbc2 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Fri, 3 May 2013 10:53:59 -0400 Subject: [PATCH 158/384] Renaming --- src/transports/ssh.c | 98 ++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index a7f21f554..b8dc9b1b6 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -30,7 +30,7 @@ typedef struct { typedef struct { git_smart_subtransport parent; git_transport *owner; - git_stream *current_stream; + ssh_stream *current_stream; } ssh_subtransport; /* @@ -70,7 +70,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) return 0; } -static int send_command(git_stream *s) +static int send_command(ssh_stream *s) { int error; git_buf request = GIT_BUF_INIT; @@ -91,13 +91,13 @@ cleanup: return error; } -static int git_stream_read( - git_smart_subtransport_stream *stream, - char *buffer, - size_t buf_size, - size_t *bytes_read) +static int ssh_stream_read( + git_smart_subtransport_stream *stream, + char *buffer, + size_t buf_size, + size_t *bytes_read) { - git_stream *s = (git_stream *)stream; + ssh_stream *s = (ssh_stream *)stream; gitno_buffer buf; *bytes_read = 0; @@ -115,12 +115,12 @@ static int git_stream_read( return 0; } -static int git_stream_write( - git_smart_subtransport_stream *stream, - const char *buffer, - size_t len) +static int ssh_stream_write( + git_smart_subtransport_stream *stream, + const char *buffer, + size_t len) { - git_stream *s = (git_stream *)stream; + ssh_stream *s = (ssh_stream *)stream; if (!s->sent_command && send_command(s) < 0) return -1; @@ -128,9 +128,9 @@ static int git_stream_write( return gitno_send(&s->socket, buffer, len, 0); } -static void git_stream_free(git_smart_subtransport_stream *stream) +static void ssh_stream_free(git_smart_subtransport_stream *stream) { - git_stream *s = (git_stream *)stream; + ssh_stream *s = (ssh_stream *)stream; ssh_subtransport *t = OWNING_SUBTRANSPORT(s); int ret; @@ -147,24 +147,24 @@ static void git_stream_free(git_smart_subtransport_stream *stream) git__free(s); } -static int git_stream_alloc( - ssh_subtransport *t, - const char *url, - const char *cmd, - git_smart_subtransport_stream **stream) +static int ssh_stream_alloc( + ssh_subtransport *t, + const char *url, + const char *cmd, + git_smart_subtransport_stream **stream) { - git_stream *s; + ssh_stream *s; if (!stream) return -1; - s = git__calloc(sizeof(git_stream), 1); + s = git__calloc(sizeof(ssh_stream), 1); GITERR_CHECK_ALLOC(s); s->parent.subtransport = &t->parent; - s->parent.read = git_stream_read; - s->parent.write = git_stream_write; - s->parent.free = git_stream_free; + s->parent.read = ssh_stream_read; + s->parent.write = ssh_stream_write; + s->parent.free = ssh_stream_free; s->cmd = cmd; s->url = git__strdup(url); @@ -179,22 +179,22 @@ static int git_stream_alloc( } static int _git_uploadpack_ls( - ssh_subtransport *t, - const char *url, - git_smart_subtransport_stream **stream) + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) { char *host, *port, *user=NULL, *pass=NULL; - git_stream *s; + ssh_stream *s; *stream = NULL; if (!git__prefixcmp(url, prefix_git)) url += strlen(prefix_git); - if (git_stream_alloc(t, url, cmd_uploadpack, stream) < 0) + if (ssh_stream_alloc(t, url, cmd_uploadpack, stream) < 0) return -1; - s = (git_stream *)*stream; + s = (ssh_stream *)*stream; if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) goto on_error; @@ -211,7 +211,7 @@ static int _git_uploadpack_ls( on_error: if (*stream) - git_stream_free(*stream); + ssh_stream_free(*stream); git__free(host); git__free(port); @@ -219,9 +219,9 @@ on_error: } static int _git_uploadpack( - ssh_subtransport *t, - const char *url, - git_smart_subtransport_stream **stream) + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) { GIT_UNUSED(url); @@ -235,22 +235,22 @@ static int _git_uploadpack( } static int _git_receivepack_ls( - ssh_subtransport *t, - const char *url, - git_smart_subtransport_stream **stream) + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) { char *host, *port, *user=NULL, *pass=NULL; - git_stream *s; + ssh_stream *s; *stream = NULL; if (!git__prefixcmp(url, prefix_git)) url += strlen(prefix_git); - if (git_stream_alloc(t, url, cmd_receivepack, stream) < 0) + if (ssh_stream_alloc(t, url, cmd_receivepack, stream) < 0) return -1; - s = (git_stream *)*stream; + s = (ssh_stream *)*stream; if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) goto on_error; @@ -267,7 +267,7 @@ static int _git_receivepack_ls( on_error: if (*stream) - git_stream_free(*stream); + ssh_stream_free(*stream); git__free(host); git__free(port); @@ -275,9 +275,9 @@ on_error: } static int _git_receivepack( - ssh_subtransport *t, - const char *url, - git_smart_subtransport_stream **stream) + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) { GIT_UNUSED(url); @@ -291,10 +291,10 @@ static int _git_receivepack( } static int _git_action( - git_smart_subtransport_stream **stream, - git_smart_subtransport *subtransport, - const char *url, - git_smart_service_t action) + git_smart_subtransport_stream **stream, + git_smart_subtransport *subtransport, + const char *url, + git_smart_service_t action) { ssh_subtransport *t = (ssh_subtransport *) subtransport; From b641c00eebb3c60e8719c0dfc55dde91ca30a5d2 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 3 May 2013 17:35:50 +0200 Subject: [PATCH 159/384] clar: Always generate the test suite --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f6b06bf1..5a228e342 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -337,7 +337,7 @@ IF (BUILD_CLAR) ADD_CUSTOM_COMMAND( OUTPUT ${CLAR_PATH}/clar.suite - COMMAND ${PYTHON_EXECUTABLE} generate.py -xonline . + COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline . DEPENDS ${SRC_TEST} WORKING_DIRECTORY ${CLAR_PATH} ) From d04c384036ec6f843bbbb45a87d9d451c35cf29a Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Fri, 3 May 2013 14:53:23 -0400 Subject: [PATCH 160/384] Adding ssh transport logic --- src/transport.c | 6 +-- src/transports/ssh.c | 100 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 86 insertions(+), 20 deletions(-) diff --git a/src/transport.c b/src/transport.c index 7b7a5aa85..0d2a860fe 100644 --- a/src/transport.c +++ b/src/transport.c @@ -30,9 +30,9 @@ static transport_definition transports[] = { {"http://", 1, git_transport_smart, &http_subtransport_definition}, {"https://", 1, git_transport_smart, &http_subtransport_definition}, {"file://", 1, git_transport_local, NULL}, - {"git+ssh://", 1, git_transport_smart, &git_smart_subtransport_ssh}, - {"ssh+git://", 1, git_transport_smart, &git_smart_subtransport_ssh}, - {"git@", 1, git_transport_smart, &git_smart_subtransport_ssh}, + {"git+ssh://", 1, git_transport_smart, &ssh_subtransport_definition}, + {"ssh+git://", 1, git_transport_smart, &ssh_subtransport_definition}, + {"git@", 1, git_transport_smart, &ssh_subtransport_definition}, {NULL, 0, 0} }; diff --git a/src/transports/ssh.c b/src/transports/ssh.c index b8dc9b1b6..8f36a6549 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -13,8 +13,7 @@ #define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport) -static const char prefix_git_ssh[] = "git+ssh://"; -static const char prefix_ssh_git[] = "ssh+git://"; +static const char prefix_ssh[] = "ssh://"; static const char prefix_git[] = "git@"; static const char cmd_uploadpack[] = "git-upload-pack"; static const char cmd_receivepack[] = "git-receive-pack"; @@ -36,7 +35,7 @@ typedef struct { /* * Create a git protocol request. * - * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0 + * For example: 0035git-upload-pack /libgit2/libgit2\0 */ static int gen_proto(git_buf *request, const char *cmd, const char *url) { @@ -178,6 +177,38 @@ static int ssh_stream_alloc( return 0; } +/* Temp */ +static int gitssh_extract_url_parts( + char **host, + char **username, + char **path, + const char *url) +{ + char *colon, *at; + const char *start; + + colon = strchr(url, ':'); + at = strchr(url, '@'); + + if (colon == NULL) { + giterr_set(GITERR_NET, "Malformed URL: missing :"); + return -1; + } + + start = url; + if (at) { + start = at+1; + *username = git__substrdup(url, at - url); + } else { + *username = "git"; + } + + *host = git__substrdup(start, colon - start); + *path = colon+1; + + return 0; +} + static int _git_uploadpack_ls( ssh_subtransport *t, const char *url, @@ -188,8 +219,8 @@ static int _git_uploadpack_ls( *stream = NULL; - if (!git__prefixcmp(url, prefix_git)) - url += strlen(prefix_git); + if (!git__prefixcmp(url, prefix_ssh)) + url += strlen(prefix_ssh); if (ssh_stream_alloc(t, url, cmd_uploadpack, stream) < 0) return -1; @@ -239,30 +270,65 @@ static int _git_receivepack_ls( const char *url, git_smart_subtransport_stream **stream) { - char *host, *port, *user=NULL, *pass=NULL; + char *host, *path, *user=NULL; ssh_stream *s; *stream = NULL; - - if (!git__prefixcmp(url, prefix_git)) - url += strlen(prefix_git); - if (ssh_stream_alloc(t, url, cmd_receivepack, stream) < 0) return -1; s = (ssh_stream *)*stream; - if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) + if (gitssh_extract_url_parts(&host, &user, &path, url) < 0) goto on_error; - if (gitno_connect(&s->socket, host, port, 0) < 0) + if (gitno_connect(&s->socket, host, "22", 0) < 0) goto on_error; + LIBSSH2_SESSION* session = libssh2_session_init(); + if (!session) + goto on_error; + + int rc = 0; + do { + rc = libssh2_session_startup(session, s->socket.socket); + } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); + + if (0 != rc) { + goto on_error; + } + + libssh2_trace(session, 0x1FF); + libssh2_session_set_blocking(session, 1); + + do { + rc = libssh2_userauth_publickey_fromfile_ex( + session, + user, + strlen(user), + NULL, + "/Users/bradfordmorgan/.ssh/id_rsa", + NULL + ); + } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); + + if (0 != rc) { + goto on_error; + } + + LIBSSH2_CHANNEL* channel = NULL; + do { + channel = libssh2_channel_open_session(session); + } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); + + if (!channel) { + goto on_error; + } + + libssh2_channel_set_blocking(channel, 1); + t->current_stream = s; git__free(host); - git__free(port); - git__free(user); - git__free(pass); return 0; on_error: @@ -270,7 +336,7 @@ on_error: ssh_stream_free(*stream); git__free(host); - git__free(port); + git__free(path); return -1; } @@ -336,7 +402,7 @@ static void _git_free(git_smart_subtransport *subtransport) git__free(t); } -int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owner) +int git_smart_subtransport_ssh(git_smart_subtransport **out, git_transport *owner) { ssh_subtransport *t; From dfec726bbae0e699b78db8f1b63372134c8467a6 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 3 May 2013 23:30:54 +0200 Subject: [PATCH 161/384] odb: Do not error out if an alternate ODB is missing --- src/odb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/odb.c b/src/odb.c index 07e1ea6eb..7b5e555a2 100644 --- a/src/odb.c +++ b/src/odb.c @@ -457,6 +457,9 @@ static int add_default_backends( inode = 0; #else if (p_stat(objects_dir, &st) < 0) { + if (as_alternates) + return 0; + giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir); return -1; } From e09d18eed66d0239bea73af51e586f6ae651fe49 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 3 May 2013 18:39:44 -0500 Subject: [PATCH 162/384] allow checkout to proceed when a dir to be removed is in use (win32) --- include/git2/checkout.h | 3 ++ src/checkout.c | 3 ++ src/fileops.c | 4 +-- src/win32/posix_w32.c | 13 +++++++- tests-clar/checkout/tree.c | 63 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 3 deletions(-) diff --git a/include/git2/checkout.h b/include/git2/checkout.h index d3e971b43..6798bf31c 100644 --- a/include/git2/checkout.h +++ b/include/git2/checkout.h @@ -134,6 +134,9 @@ typedef enum { /** Treat pathspec as simple list of exact match file paths */ GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13), + /** Ignore directories in use, they will be left empty */ + GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES = (1u << 18), + /** * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED */ diff --git a/src/checkout.c b/src/checkout.c index 21f32d89a..e9ec2bdab 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -955,6 +955,9 @@ static int checkout_remove_the_old( uint32_t flg = GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS; + if (data->opts.checkout_strategy & GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES) + flg |= GIT_RMDIR_SKIP_NONEMPTY; + git_buf_truncate(&data->path, data->workdir_len); git_vector_foreach(&data->diff->deltas, i, delta) { diff --git a/src/fileops.c b/src/fileops.c index d6244711f..36f601706 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -444,7 +444,7 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) if (data->error < 0) { if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 && - (errno == ENOTEMPTY || errno == EEXIST)) + (errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY)) data->error = 0; else futils__error_cannot_rmdir(path->ptr, NULL); @@ -480,7 +480,7 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path) if (en == ENOENT || en == ENOTDIR) { giterr_clear(); error = 0; - } else if (en == ENOTEMPTY || en == EEXIST) { + } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { giterr_clear(); error = GIT_ITEROVER; } else { diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 4d56299f7..a817d4245 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -314,9 +314,20 @@ int p_chmod(const char* path, mode_t mode) int p_rmdir(const char* path) { + int error; wchar_t buf[GIT_WIN_PATH]; git__utf8_to_16(buf, GIT_WIN_PATH, path); - return _wrmdir(buf); + + error = _wrmdir(buf); + + /* _wrmdir() is documented to return EACCES if "A program has an open + * handle to the directory." This sounds like what everybody else calls + * EBUSY. Let's convert appropriate error codes. + */ + if (GetLastError() == ERROR_SHARING_VIOLATION) + errno = EBUSY; + + return error; } int p_hide_directory__w32(const char *path) diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index eb129f34e..67357a942 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -526,3 +526,66 @@ void test_checkout_tree__can_write_to_empty_dirs(void) git_object_free(obj); } + +void test_checkout_tree__fails_when_dir_in_use(void) +{ +#ifdef GIT_WIN32 + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_oid oid; + git_object *obj = NULL; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); + + cl_assert(git_path_isfile("testrepo/a/b.txt")); + + git_object_free(obj); + + cl_git_pass(p_chdir("testrepo/a")); + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + cl_git_fail(git_checkout_tree(g_repo, obj, &opts)); + + cl_git_pass(p_chdir("../..")); + + cl_assert(git_path_is_empty_dir("testrepo/a")); +#endif +} + +void test_checkout_tree__can_continue_when_dir_in_use(void) +{ +#ifdef GIT_WIN32 + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_oid oid; + git_object *obj = NULL; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE | + GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES; + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); + + cl_assert(git_path_isfile("testrepo/a/b.txt")); + + git_object_free(obj); + + cl_git_pass(p_chdir("testrepo/a")); + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); + + cl_git_pass(p_chdir("../..")); + + cl_assert(git_path_is_empty_dir("testrepo/a")); +#endif +} From 6e286e8dc59874db30b6fbb0ca5d32d4a2b5642c Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Sat, 4 May 2013 01:04:23 -0700 Subject: [PATCH 163/384] Remove obsolete test for peel type Peeling to an invalid type is now checked via an assert so this test is no longer relevant. --- tests-clar/object/peel.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests-clar/object/peel.c b/tests-clar/object/peel.c index bb0bbd096..b6c9c7a3b 100644 --- a/tests-clar/object/peel.c +++ b/tests-clar/object/peel.c @@ -103,8 +103,3 @@ void test_object_peel__target_any_object_for_type_change(void) /* fail to peel blob */ assert_peel_error(GIT_ENOTFOUND, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_ANY); } - -void test_object_peel__should_use_a_well_known_type(void) -{ - assert_peel_error(GIT_EINVALIDSPEC, "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ__EXT2); -} From f7158cd79b836d791b188f67b6d54064afb8ed31 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Fri, 3 May 2013 16:31:16 -0400 Subject: [PATCH 164/384] Push working over ssh --- include/git2/transport.h | 25 +++++++++++++ src/transports/cred.c | 77 ++++++++++++++++++++++++++++++++++++++++ src/transports/ssh.c | 74 ++++++++++++++++++++++++-------------- 3 files changed, 149 insertions(+), 27 deletions(-) diff --git a/include/git2/transport.h b/include/git2/transport.h index 9b9ecc5fc..31572c16a 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -27,6 +27,7 @@ GIT_BEGIN_DECL typedef enum { /* git_cred_userpass_plaintext */ GIT_CREDTYPE_USERPASS_PLAINTEXT = 1, + GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE = 2, } git_credtype_t; /* The base structure for all credential types */ @@ -43,6 +44,14 @@ typedef struct git_cred_userpass_plaintext { char *password; } git_cred_userpass_plaintext; +/* A plaintext username and password */ +typedef struct git_cred_ssh_keyfile_passphrase { + git_cred parent; + char *publickey; + char *privatekey; + char *passphrase; +} git_cred_ssh_keyfile_passphrase; + /** * Creates a new plain-text username and password credential object. * The supplied credential parameter will be internally duplicated. @@ -57,6 +66,22 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new( const char *username, const char *password); +/** + * Creates a new ssh key file and passphrase credential object. + * The supplied credential parameter will be internally duplicated. + * + * @param out The newly created credential object. + * @param publickey The path to the public key of the credential. + * @param privatekey The path to the private key of the credential. + * @param passphrase The passphrase of the credential. + * @return 0 for success or an error code for failure + */ +GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new( + git_cred **out, + const char *publickey, + const char *privatekey, + const char *passphrase); + /** * Signature of a function which acquires a credential object. * diff --git a/src/transports/cred.c b/src/transports/cred.c index ecb026062..83820ec05 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -58,3 +58,80 @@ int git_cred_userpass_plaintext_new( *cred = &c->parent; return 0; } + +static void ssh_keyfile_passphrase_free(struct git_cred *cred) +{ + git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred; + size_t pass_len = strlen(c->passphrase); + + if (c->publickey) { + git__free(c->publickey); + } + + git__free(c->privatekey); + + if (c->passphrase) { + /* Zero the memory which previously held the passphrase */ + memset(c->passphrase, 0x0, pass_len); + git__free(c->passphrase); + } + + memset(c, 0, sizeof(*c)); + + git__free(c); +} + +int git_cred_ssh_keyfile_passphrase_new( + git_cred **cred, + const char *publickey, + const char *privatekey, + const char *passphrase) +{ + git_cred_ssh_keyfile_passphrase *c; + + if (!cred) + return -1; + + c = git__malloc(sizeof(git_cred_ssh_keyfile_passphrase)); + GITERR_CHECK_ALLOC(c); + + c->parent.credtype = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE; + c->parent.free = ssh_keyfile_passphrase_free; + + c->privatekey = git__strdup(privatekey); + + if (!c->privatekey) { + git__free(c); + return -1; + } + + if (publickey) { + c->publickey = git__strdup(publickey); + + if (!c->publickey) { + git__free(c->privatekey); + git__free(c); + return -1; + } + } else { + c->publickey = NULL; + } + + if (passphrase) { + c->passphrase = git__strdup(passphrase); + + if (!c->passphrase) { + git__free(c->privatekey); + if (c->publickey) { + git__free(c->publickey); + } + git__free(c); + return -1; + } + } else { + c->passphrase = NULL; + } + + *cred = &c->parent; + return 0; +} diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 8f36a6549..935fe580a 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -8,6 +8,7 @@ #include "git2.h" #include "buffer.h" #include "netops.h" +#include "smart.h" #include @@ -21,6 +22,8 @@ static const char cmd_receivepack[] = "git-receive-pack"; typedef struct { git_smart_subtransport_stream parent; gitno_socket socket; + LIBSSH2_SESSION *session; + LIBSSH2_CHANNEL *channel; const char *cmd; char *url; unsigned sent_command : 1; @@ -28,8 +31,9 @@ typedef struct { typedef struct { git_smart_subtransport parent; - git_transport *owner; + transport_smart *owner; ssh_stream *current_stream; + git_cred *cred; } ssh_subtransport; /* @@ -40,27 +44,19 @@ typedef struct { static int gen_proto(git_buf *request, const char *cmd, const char *url) { char *delim, *repo; - char host[] = "host="; size_t len; - delim = strchr(url, '/'); + delim = strchr(url, ':'); if (delim == NULL) { giterr_set(GITERR_NET, "Malformed URL"); return -1; } - repo = delim; - - delim = strchr(url, ':'); - if (delim == NULL) - delim = strchr(url, '/'); - - len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1; + repo = delim+1; + len = strlen(cmd) + 1 + 1 + strlen(repo) + 1; git_buf_grow(request, len); - git_buf_printf(request, "%04x%s %s%c%s", - (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host); - git_buf_put(request, url, delim - url); + git_buf_printf(request, "%s '%s'", cmd, repo); git_buf_putc(request, '\0'); if (git_buf_oom(request)) @@ -78,12 +74,18 @@ static int send_command(ssh_stream *s) if (error < 0) goto cleanup; - /* It looks like negative values are errors here, and positive values - * are the number of bytes sent. */ - error = gitno_send(&s->socket, request.ptr, request.size, 0); + error = libssh2_channel_process_startup( + s->channel, + "exec", + (uint32_t)sizeof("exec") - 1, + request.ptr, + request.size + ); + + if (0 != error) + goto cleanup; - if (error >= 0) - s->sent_command = 1; + s->sent_command = 1; cleanup: git_buf_free(&request); @@ -97,19 +99,18 @@ static int ssh_stream_read( size_t *bytes_read) { ssh_stream *s = (ssh_stream *)stream; - gitno_buffer buf; *bytes_read = 0; if (!s->sent_command && send_command(s) < 0) return -1; - gitno_buffer_setup(&s->socket, &buf, buffer, buf_size); + int rc = libssh2_channel_read(s->channel, buffer, buf_size); - if (gitno_recv(&buf) < 0) + if (rc < 0) return -1; - *bytes_read = buf.offset; + *bytes_read = rc; return 0; } @@ -124,7 +125,12 @@ static int ssh_stream_write( if (!s->sent_command && send_command(s) < 0) return -1; - return gitno_send(&s->socket, buffer, len, 0); + int rc = libssh2_channel_write(s->channel, buffer, len); + if (rc < 0) { + return -1; + } + + return rc; } static void ssh_stream_free(git_smart_subtransport_stream *stream) @@ -285,6 +291,17 @@ static int _git_receivepack_ls( if (gitno_connect(&s->socket, host, "22", 0) < 0) goto on_error; + if (t->owner->cred_acquire_cb(&t->cred, + t->owner->url, + user, + GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE, + t->owner->cred_acquire_payload) < 0) + return -1; + + assert(t->cred); + + git_cred_ssh_keyfile_passphrase *cred = (git_cred_ssh_keyfile_passphrase *)t->cred; + LIBSSH2_SESSION* session = libssh2_session_init(); if (!session) goto on_error; @@ -306,9 +323,9 @@ static int _git_receivepack_ls( session, user, strlen(user), - NULL, - "/Users/bradfordmorgan/.ssh/id_rsa", - NULL + cred->publickey, + cred->privatekey, + cred->passphrase ); } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); @@ -327,6 +344,9 @@ static int _git_receivepack_ls( libssh2_channel_set_blocking(channel, 1); + s->session = session; + s->channel = channel; + t->current_stream = s; git__free(host); return 0; @@ -412,7 +432,7 @@ int git_smart_subtransport_ssh(git_smart_subtransport **out, git_transport *owne t = git__calloc(sizeof(ssh_subtransport), 1); GITERR_CHECK_ALLOC(t); - t->owner = owner; + t->owner = (transport_smart *)owner; t->parent.action = _git_action; t->parent.close = _git_close; t->parent.free = _git_free; From 6f748f3885d7c99cfc7df431bf0c2ca84b70016b Mon Sep 17 00:00:00 2001 From: Jameson Miller Date: Sat, 4 May 2013 12:14:40 -0400 Subject: [PATCH 165/384] Do not write tagopt configuration option on clone by default --- src/clone.c | 58 +++++++++++++++++++++--------------- tests-clar/clone/nonetwork.c | 18 +++++++++++ tests-clar/online/clone.c | 3 ++ 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/clone.c b/src/clone.c index aeb7bbf5c..9cde6f6bd 100644 --- a/src/clone.c +++ b/src/clone.c @@ -358,31 +358,41 @@ static int setup_remotes_and_fetch( git_remote *origin; /* Construct an origin remote */ - if (!create_and_configure_origin(&origin, repo, url, options)) { - git_remote_set_update_fetchhead(origin, 0); + if ((retcode = create_and_configure_origin(&origin, repo, url, options)) < 0) + goto on_error; - /* Connect and download everything */ - if (!git_remote_connect(origin, GIT_DIRECTION_FETCH)) { - if (!(retcode = git_remote_download(origin, options->fetch_progress_cb, - options->fetch_progress_payload))) { - /* Create "origin/foo" branches for all remote branches */ - if (!git_remote_update_tips(origin)) { - /* Point HEAD to the requested branch */ - if (options->checkout_branch) { - if (!update_head_to_branch(repo, options)) - retcode = 0; - } - /* Point HEAD to the same ref as the remote's head */ - else if (!update_head_to_remote(repo, origin)) { - retcode = 0; - } - } - } - git_remote_disconnect(origin); - } - git_remote_free(origin); - } + git_remote_set_update_fetchhead(origin, 0); + /* If the download_tags value has not been specified, then make sure to + * download tags as well. It is set here because we want to download tags + * on the initial clone, but do not want to persist the value in the + * configuration file. + */ + if (origin->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_AUTO && + ((retcode = git_remote_add_fetch(origin, "refs/tags/*:refs/tags/*")) < 0)) + goto on_error; + + /* Connect and download everything */ + if ((retcode = git_remote_connect(origin, GIT_DIRECTION_FETCH)) < 0) + goto on_error; + + if ((retcode = git_remote_download(origin, options->fetch_progress_cb, + options->fetch_progress_payload)) < 0) + goto on_error; + + /* Create "origin/foo" branches for all remote branches */ + if ((retcode = git_remote_update_tips(origin)) < 0) + goto on_error; + + /* Point HEAD to the requested branch */ + if (options->checkout_branch) + retcode = update_head_to_branch(repo, options); + /* Point HEAD to the same ref as the remote's head */ + else + retcode = update_head_to_remote(repo, origin); + +on_error: + git_remote_free(origin); return retcode; } @@ -425,7 +435,7 @@ static void normalize_options(git_clone_options *dst, const git_clone_options *s /* Provide defaults for null pointers */ if (!dst->remote_name) dst->remote_name = "origin"; - if (!dst->remote_autotag) dst->remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_ALL; + if (!dst->remote_autotag) dst->remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; } int git_clone( diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c index 545fe3a06..8b17fd998 100644 --- a/tests-clar/clone/nonetwork.c +++ b/tests-clar/clone/nonetwork.c @@ -172,6 +172,7 @@ void test_clone_nonetwork__custom_push_spec(void) void test_clone_nonetwork__custom_autotag(void) { + git_remote *origin; git_strarray tags = {0}; g_options.remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_NONE; @@ -180,6 +181,23 @@ void test_clone_nonetwork__custom_autotag(void) cl_git_pass(git_tag_list(&tags, g_repo)); cl_assert_equal_sz(0, tags.count); + cl_git_pass(git_remote_load(&origin, g_repo, "origin")); + cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_NONE, origin->download_tags); + + git_strarray_free(&tags); +} + +void test_clone_nonetwork__custom_autotag_tags_all(void) +{ + git_strarray tags = {0}; + git_remote *origin; + + g_options.remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_ALL; + cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); + + cl_git_pass(git_remote_load(&origin, g_repo, "origin")); + cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_ALL, origin->download_tags); + git_strarray_free(&tags); } diff --git a/tests-clar/online/clone.c b/tests-clar/online/clone.c index c1a9a9a88..aa12e47c9 100644 --- a/tests-clar/online/clone.c +++ b/tests-clar/online/clone.c @@ -3,6 +3,7 @@ #include "git2/clone.h" #include "git2/cred_helpers.h" #include "repository.h" +#include "remote.h" #define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository" #define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository" @@ -42,6 +43,8 @@ void test_online_clone__network_full(void) cl_assert(!git_repository_is_bare(g_repo)); cl_git_pass(git_remote_load(&origin, g_repo, "origin")); + cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, origin->download_tags); + git_remote_free(origin); } From 00a4c4793890e9a8f6ad6f538d445111d6c73d54 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 4 May 2013 12:04:39 -0500 Subject: [PATCH 166/384] p_stat() should follow symlinks on windows --- src/win32/posix_w32.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 4d56299f7..da147ad0c 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -295,7 +295,18 @@ int p_getcwd(char *buffer_out, size_t size) int p_stat(const char* path, struct stat* buf) { - return do_lstat(path, buf, 0); + char target[GIT_WIN_PATH]; + int error = 0; + + error = do_lstat(path, buf, 0); + + /* We need not do this in a loop to unwind chains of symlinks since + * p_readlink calls GetFinalPathNameByHandle which does it for us. */ + if (error >= 0 && S_ISLNK(buf->st_mode) && + (error = p_readlink(path, target, GIT_WIN_PATH)) >= 0) + error = do_lstat(target, buf, 0); + + return error; } int p_chdir(const char* path) From 58ba0a4eba9b1c0050d2aaa0eecad21c244fae4a Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Sun, 5 May 2013 08:34:56 -0400 Subject: [PATCH 167/384] Cleanup --- src/transports/ssh.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 935fe580a..cc27c8db0 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -26,6 +26,7 @@ typedef struct { LIBSSH2_CHANNEL *channel; const char *cmd; char *url; + char *path; unsigned sent_command : 1; } ssh_stream; @@ -39,21 +40,11 @@ typedef struct { /* * Create a git protocol request. * - * For example: 0035git-upload-pack /libgit2/libgit2\0 + * For example: git-upload-pack '/libgit2/libgit2' */ -static int gen_proto(git_buf *request, const char *cmd, const char *url) +static int gen_proto(git_buf *request, const char *cmd, const char *repo) { - char *delim, *repo; - size_t len; - - delim = strchr(url, ':'); - if (delim == NULL) { - giterr_set(GITERR_NET, "Malformed URL"); - return -1; - } - - repo = delim+1; - len = strlen(cmd) + 1 + 1 + strlen(repo) + 1; + int len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1; git_buf_grow(request, len); git_buf_printf(request, "%s '%s'", cmd, repo); @@ -70,7 +61,7 @@ static int send_command(ssh_stream *s) int error; git_buf request = GIT_BUF_INIT; - error = gen_proto(&request, s->cmd, s->url); + error = gen_proto(&request, s->cmd, s->path); if (error < 0) goto cleanup; @@ -276,7 +267,7 @@ static int _git_receivepack_ls( const char *url, git_smart_subtransport_stream **stream) { - char *host, *path, *user=NULL; + char *host, *user=NULL; ssh_stream *s; *stream = NULL; @@ -285,7 +276,7 @@ static int _git_receivepack_ls( s = (ssh_stream *)*stream; - if (gitssh_extract_url_parts(&host, &user, &path, url) < 0) + if (gitssh_extract_url_parts(&host, &user, &s->path, url) < 0) goto on_error; if (gitno_connect(&s->socket, host, "22", 0) < 0) @@ -356,7 +347,6 @@ on_error: ssh_stream_free(*stream); git__free(host); - git__free(path); return -1; } From 22595b84808735798eaeed3cb571372844ba5538 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Sun, 5 May 2013 08:43:58 -0400 Subject: [PATCH 168/384] Added ssh stream cleanup --- src/transports/ssh.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index cc27c8db0..a186c22c6 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -97,7 +97,6 @@ static int ssh_stream_read( return -1; int rc = libssh2_channel_read(s->channel, buffer, buf_size); - if (rc < 0) return -1; @@ -134,6 +133,16 @@ static void ssh_stream_free(git_smart_subtransport_stream *stream) t->current_stream = NULL; + if (s->channel) { + libssh2_channel_close(s->channel); + libssh2_channel_free(s->channel); + s->channel = NULL; + } + + if (s->session) { + libssh2_session_free(s->session), s->session = NULL; + } + if (s->socket.socket) { ret = gitno_close(&s->socket); assert(!ret); From 120b0122c576feeb74c6f6d50e572af0e0f6b7d6 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Sun, 5 May 2013 09:03:49 -0400 Subject: [PATCH 169/384] Refactoring --- src/transports/ssh.c | 102 ++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 59 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index a186c22c6..6e99e003d 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -215,72 +215,18 @@ static int gitssh_extract_url_parts( return 0; } -static int _git_uploadpack_ls( +static int _git_ssh_setup_conn( ssh_subtransport *t, const char *url, - git_smart_subtransport_stream **stream) -{ - char *host, *port, *user=NULL, *pass=NULL; - ssh_stream *s; - - *stream = NULL; - - if (!git__prefixcmp(url, prefix_ssh)) - url += strlen(prefix_ssh); - - if (ssh_stream_alloc(t, url, cmd_uploadpack, stream) < 0) - return -1; - - s = (ssh_stream *)*stream; - - if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) - goto on_error; - - if (gitno_connect(&s->socket, host, port, 0) < 0) - goto on_error; - - t->current_stream = s; - git__free(host); - git__free(port); - git__free(user); - git__free(pass); - return 0; - -on_error: - if (*stream) - ssh_stream_free(*stream); - - git__free(host); - git__free(port); - return -1; -} - -static int _git_uploadpack( - ssh_subtransport *t, - const char *url, - git_smart_subtransport_stream **stream) -{ - GIT_UNUSED(url); - - if (t->current_stream) { - *stream = &t->current_stream->parent; - return 0; - } - - giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK"); - return -1; -} - -static int _git_receivepack_ls( - ssh_subtransport *t, - const char *url, - git_smart_subtransport_stream **stream) + const char *cmd, + git_smart_subtransport_stream **stream +) { char *host, *user=NULL; ssh_stream *s; *stream = NULL; - if (ssh_stream_alloc(t, url, cmd_receivepack, stream) < 0) + if (ssh_stream_alloc(t, url, cmd, stream) < 0) return -1; s = (ssh_stream *)*stream; @@ -359,6 +305,44 @@ on_error: return -1; } +static int _git_uploadpack_ls( + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) +{ + if (_git_ssh_setup_conn(t, url, cmd_uploadpack, stream) < 0) + return -1; + + return 0; +} + +static int _git_uploadpack( + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) +{ + GIT_UNUSED(url); + + if (t->current_stream) { + *stream = &t->current_stream->parent; + return 0; + } + + giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK"); + return -1; +} + +static int _git_receivepack_ls( + ssh_subtransport *t, + const char *url, + git_smart_subtransport_stream **stream) +{ + if (_git_ssh_setup_conn(t, url, cmd_receivepack, stream) < 0) + return -1; + + return 0; +} + static int _git_receivepack( ssh_subtransport *t, const char *url, From ce72e399d26a7b246f6425657b0c8a55d40159c9 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Sun, 5 May 2013 16:45:38 +0200 Subject: [PATCH 170/384] commit: guard create() against not owned trees --- src/commit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commit.c b/src/commit.c index 46c02c292..aec3e1e63 100644 --- a/src/commit.c +++ b/src/commit.c @@ -149,6 +149,7 @@ int git_commit_create( const git_oid **parent_oids; assert(parent_count >= 0); + assert(git_object_owner((const git_object *)tree) == repo); parent_oids = git__malloc(parent_count * sizeof(git_oid *)); GITERR_CHECK_ALLOC(parent_oids); From 467cbec73d1c34c3f6c1e7ea66e1276e5e44299b Mon Sep 17 00:00:00 2001 From: nulltoken Date: Sun, 5 May 2013 16:48:34 +0200 Subject: [PATCH 171/384] commit: make create_from_oids() accept plain oid --- include/git2/sys/commit.h | 5 +++-- src/commit.c | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/git2/sys/commit.h b/include/git2/sys/commit.h index 096e028c5..34a12fb15 100644 --- a/include/git2/sys/commit.h +++ b/include/git2/sys/commit.h @@ -25,8 +25,9 @@ GIT_BEGIN_DECL * * See documentation for `git_commit_create()` for information about the * parameters, as the meaning is identical excepting that `tree` and - * `parents` now take `git_oid`. This is a dangerous API in that the - * `parents` list of `git_oid`s in not checked for validity. + * `parents` now take `git_oid`. This is a dangerous API in that nor + * the `tree`, neither the `parents` list of `git_oid`s are checked for + * validity. */ GIT_EXTERN(int) git_commit_create_from_oids( git_oid *oid, diff --git a/src/commit.c b/src/commit.c index aec3e1e63..3dc647c9b 100644 --- a/src/commit.c +++ b/src/commit.c @@ -96,7 +96,6 @@ int git_commit_create_from_oids( git_odb *odb; assert(oid && repo && tree && parent_count >= 0); - assert(git_object_owner((const git_object *)tree) == repo); git_oid__writebuf(&commit, "tree ", tree); From 7261d9837eb2ec521349a4e897d3236b35dbf094 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Sun, 5 May 2013 13:36:11 -0400 Subject: [PATCH 172/384] Added support for ssh:// urls --- src/transport.c | 15 +++++----- src/transports/ssh.c | 68 ++++++++++++++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/src/transport.c b/src/transport.c index 0d2a860fe..6a8e67df6 100644 --- a/src/transport.c +++ b/src/transport.c @@ -18,21 +18,22 @@ typedef struct transport_definition { void *param; } transport_definition; -static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL }; -static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL }; - static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1 }; static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0 }; static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0 }; +static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL }; +#ifdef GIT_WIN32 +static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL }; +#endif +static transport_definition ssh_transport_definition = { "ssh://", 1, git_transport_smart, &ssh_subtransport_definition }; + static transport_definition transports[] = { {"git://", 1, git_transport_smart, &git_subtransport_definition}, {"http://", 1, git_transport_smart, &http_subtransport_definition}, {"https://", 1, git_transport_smart, &http_subtransport_definition}, {"file://", 1, git_transport_local, NULL}, - {"git+ssh://", 1, git_transport_smart, &ssh_subtransport_definition}, - {"ssh+git://", 1, git_transport_smart, &ssh_subtransport_definition}, - {"git@", 1, git_transport_smart, &ssh_subtransport_definition}, + {"ssh://", 1, git_transport_smart, &ssh_subtransport_definition}, {NULL, 0, 0} }; @@ -75,7 +76,7 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void * /* It could be a SSH remote path. Check to see if there's a : * SSH is an unsupported transport mechanism in this version of libgit2 */ if (!definition && strrchr(url, ':')) - definition = &dummy_transport_definition; + definition = &ssh_transport_definition; /* Check to see if the path points to a file on the local file system */ if (!definition && git_path_exists(url) && git_path_isdir(url)) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 6e99e003d..6e81c256c 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -15,7 +15,7 @@ #define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport) static const char prefix_ssh[] = "ssh://"; -static const char prefix_git[] = "git@"; +static const char default_user[] = "git"; static const char cmd_uploadpack[] = "git-upload-pack"; static const char cmd_receivepack[] = "git-receive-pack"; @@ -26,7 +26,6 @@ typedef struct { LIBSSH2_CHANNEL *channel; const char *cmd; char *url; - char *path; unsigned sent_command : 1; } ssh_stream; @@ -42,8 +41,21 @@ typedef struct { * * For example: git-upload-pack '/libgit2/libgit2' */ -static int gen_proto(git_buf *request, const char *cmd, const char *repo) +static int gen_proto(git_buf *request, const char *cmd, const char *url) { + char *repo; + + if (!git__prefixcmp(url, prefix_ssh)) { + url = url + strlen(prefix_ssh); + repo = strchr(url, '/'); + } else { + repo = strchr(url, ':'); + } + + if (!repo) { + return -1; + } + int len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1; git_buf_grow(request, len); @@ -61,7 +73,7 @@ static int send_command(ssh_stream *s) int error; git_buf request = GIT_BUF_INIT; - error = gen_proto(&request, s->cmd, s->path); + error = gen_proto(&request, s->cmd, s->url); if (error < 0) goto cleanup; @@ -183,18 +195,15 @@ static int ssh_stream_alloc( return 0; } -/* Temp */ -static int gitssh_extract_url_parts( +static int git_ssh_extract_url_parts( char **host, char **username, - char **path, const char *url) { char *colon, *at; const char *start; colon = strchr(url, ':'); - at = strchr(url, '@'); if (colon == NULL) { giterr_set(GITERR_NET, "Malformed URL: missing :"); @@ -202,15 +211,15 @@ static int gitssh_extract_url_parts( } start = url; + at = strchr(url, '@'); if (at) { start = at+1; *username = git__substrdup(url, at - url); } else { - *username = "git"; + *username = git__strdup(default_user); } *host = git__substrdup(start, colon - start); - *path = colon+1; return 0; } @@ -222,7 +231,8 @@ static int _git_ssh_setup_conn( git_smart_subtransport_stream **stream ) { - char *host, *user=NULL; + char *host, *port, *user=NULL, *pass=NULL; + const char *default_port = "22"; ssh_stream *s; *stream = NULL; @@ -231,21 +241,35 @@ static int _git_ssh_setup_conn( s = (ssh_stream *)*stream; - if (gitssh_extract_url_parts(&host, &user, &s->path, url) < 0) + if (!git__prefixcmp(url, prefix_ssh)) { + url = url + strlen(prefix_ssh); + if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0) + return -1; + } else { + if (git_ssh_extract_url_parts(&host, &user, url) < 0) + goto on_error; + port = git__strdup(default_port); + } + + if (gitno_connect(&s->socket, host, port, 0) < 0) goto on_error; - if (gitno_connect(&s->socket, host, "22", 0) < 0) - goto on_error; - - if (t->owner->cred_acquire_cb(&t->cred, - t->owner->url, - user, - GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE, - t->owner->cred_acquire_payload) < 0) - return -1; - + if (user && pass) { + git_cred_userpass_plaintext_new(&t->cred, user, pass); + } else { + if (t->owner->cred_acquire_cb(&t->cred, + t->owner->url, + user, + GIT_CREDTYPE_USERPASS_PLAINTEXT | GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE, + t->owner->cred_acquire_payload) < 0) + return -1; + } assert(t->cred); + if (!user) { + user = git__strdup(default_user); + } + git_cred_ssh_keyfile_passphrase *cred = (git_cred_ssh_keyfile_passphrase *)t->cred; LIBSSH2_SESSION* session = libssh2_session_init(); From c0cef9e0d6b0de734bbc67b6b627ab21117a9e27 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Sun, 5 May 2013 13:58:18 -0400 Subject: [PATCH 173/384] Added username and password auth for ssh --- src/transports/ssh.c | 58 +++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 6e81c256c..8a2f9e3bb 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -224,6 +224,43 @@ static int git_ssh_extract_url_parts( return 0; } +static int _git_ssh_authenticate_session( + LIBSSH2_SESSION* session, + const char *user, + git_cred* cred +) +{ + int rc; + do { + switch (cred->credtype) { + case GIT_CREDTYPE_USERPASS_PLAINTEXT: { + git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; + rc = libssh2_userauth_password( + session, + c->username, + c->password + ); + break; + } + case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: { + git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred; + rc = libssh2_userauth_publickey_fromfile( + session, + user, + c->publickey, + c->privatekey, + c->passphrase + ); + break; + } + default: + rc = -1; + } + } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); + + return rc; +} + static int _git_ssh_setup_conn( ssh_subtransport *t, const char *url, @@ -244,7 +281,7 @@ static int _git_ssh_setup_conn( if (!git__prefixcmp(url, prefix_ssh)) { url = url + strlen(prefix_ssh); if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0) - return -1; + goto on_error; } else { if (git_ssh_extract_url_parts(&host, &user, url) < 0) goto on_error; @@ -270,8 +307,6 @@ static int _git_ssh_setup_conn( user = git__strdup(default_user); } - git_cred_ssh_keyfile_passphrase *cred = (git_cred_ssh_keyfile_passphrase *)t->cred; - LIBSSH2_SESSION* session = libssh2_session_init(); if (!session) goto on_error; @@ -288,20 +323,9 @@ static int _git_ssh_setup_conn( libssh2_trace(session, 0x1FF); libssh2_session_set_blocking(session, 1); - do { - rc = libssh2_userauth_publickey_fromfile_ex( - session, - user, - strlen(user), - cred->publickey, - cred->privatekey, - cred->passphrase - ); - } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); - - if (0 != rc) { - goto on_error; - } + if (_git_ssh_authenticate_session(session, user, t->cred) < 0) { + goto on_error; + } LIBSSH2_CHANNEL* channel = NULL; do { From d97669593ae0b423d9a380bba43047d3778f0d6e Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Sun, 5 May 2013 14:05:03 -0400 Subject: [PATCH 174/384] Cleanup --- src/transports/ssh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 8a2f9e3bb..252a802a7 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -210,12 +210,12 @@ static int git_ssh_extract_url_parts( return -1; } - start = url; at = strchr(url, '@'); if (at) { start = at+1; *username = git__substrdup(url, at - url); } else { + start = url; *username = git__strdup(default_user); } From 3eed595e853baf336dd83613189964500bd3730c Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Sun, 5 May 2013 14:24:05 -0400 Subject: [PATCH 175/384] Refactoring --- src/transports/ssh.c | 93 +++++++++++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 27 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 252a802a7..e2f03fd28 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -254,13 +254,50 @@ static int _git_ssh_authenticate_session( break; } default: - rc = -1; + rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED; } } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); return rc; } +static int _git_ssh_session_create +( + LIBSSH2_SESSION** session, + gitno_socket socket +) +{ + if (!session) { + return -1; + } + + LIBSSH2_SESSION* s = libssh2_session_init(); + if (!s) + return -1; + + int rc = 0; + do { + rc = libssh2_session_startup(s, socket.socket); + } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); + + if (0 != rc) { + goto on_error; + } + + libssh2_session_set_blocking(s, 1); + + *session = s; + + return 0; + +on_error: + if (s) { + libssh2_session_free(s), s = NULL; + } + + return -1; +} + static int _git_ssh_setup_conn( ssh_subtransport *t, const char *url, @@ -268,9 +305,11 @@ static int _git_ssh_setup_conn( git_smart_subtransport_stream **stream ) { - char *host, *port, *user=NULL, *pass=NULL; - const char *default_port = "22"; + char *host, *port=NULL, *user=NULL, *pass=NULL; + const char *default_port="22"; ssh_stream *s; + LIBSSH2_SESSION* session=NULL; + LIBSSH2_CHANNEL* channel=NULL; *stream = NULL; if (ssh_stream_alloc(t, url, cmd, stream) < 0) @@ -307,34 +346,15 @@ static int _git_ssh_setup_conn( user = git__strdup(default_user); } - LIBSSH2_SESSION* session = libssh2_session_init(); - if (!session) - goto on_error; - - int rc = 0; - do { - rc = libssh2_session_startup(session, s->socket.socket); - } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); - - if (0 != rc) { - goto on_error; - } - - libssh2_trace(session, 0x1FF); - libssh2_session_set_blocking(session, 1); - - if (_git_ssh_authenticate_session(session, user, t->cred) < 0) { + if (_git_ssh_session_create(&session, s->socket) < 0) goto on_error; - } - LIBSSH2_CHANNEL* channel = NULL; - do { - channel = libssh2_channel_open_session(session); - } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); + if (_git_ssh_authenticate_session(session, user, t->cred) < 0) + goto on_error; - if (!channel) { + channel = libssh2_channel_open_session(session); + if (!channel) goto on_error; - } libssh2_channel_set_blocking(channel, 1); @@ -343,6 +363,14 @@ static int _git_ssh_setup_conn( t->current_stream = s; git__free(host); + git__free(port); + if (user) { + git__free(user); + } + if (pass) { + git__free(pass); + } + return 0; on_error: @@ -350,6 +378,17 @@ on_error: ssh_stream_free(*stream); git__free(host); + git__free(port); + if (user) { + git__free(user); + } + if (pass) { + git__free(pass); + } + if (session) { + libssh2_session_free(session), session = NULL; + } + return -1; } From 67a7136c7ba8c6ee90d9f64a9f5c02ddca5c064b Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Sun, 5 May 2013 14:24:47 -0400 Subject: [PATCH 176/384] Renaming --- src/transports/ssh.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index e2f03fd28..97a285a02 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -446,7 +446,7 @@ static int _git_receivepack( return -1; } -static int _git_action( +static int _ssh_action( git_smart_subtransport_stream **stream, git_smart_subtransport *subtransport, const char *url, @@ -472,7 +472,7 @@ static int _git_action( return -1; } -static int _git_close(git_smart_subtransport *subtransport) +static int _ssh_close(git_smart_subtransport *subtransport) { ssh_subtransport *t = (ssh_subtransport *) subtransport; @@ -483,7 +483,7 @@ static int _git_close(git_smart_subtransport *subtransport) return 0; } -static void _git_free(git_smart_subtransport *subtransport) +static void _ssh_free(git_smart_subtransport *subtransport) { ssh_subtransport *t = (ssh_subtransport *) subtransport; @@ -503,9 +503,9 @@ int git_smart_subtransport_ssh(git_smart_subtransport **out, git_transport *owne GITERR_CHECK_ALLOC(t); t->owner = (transport_smart *)owner; - t->parent.action = _git_action; - t->parent.close = _git_close; - t->parent.free = _git_free; + t->parent.action = _ssh_action; + t->parent.close = _ssh_close; + t->parent.free = _ssh_free; *out = (git_smart_subtransport *) t; return 0; From 7621519f73dfa529dab4d48532559a5e321f6526 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Sun, 5 May 2013 14:46:28 -0400 Subject: [PATCH 177/384] Cleanup --- src/transports/ssh.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 97a285a02..9ee13becf 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -378,16 +378,14 @@ on_error: ssh_stream_free(*stream); git__free(host); - git__free(port); - if (user) { + if (port) + git__free(port); + if (user) git__free(user); - } - if (pass) { + if (pass) git__free(pass); - } - if (session) { + if (session) libssh2_session_free(session), session = NULL; - } return -1; } From 72662202ac4230fba8642f8b2844359377c16326 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Mon, 6 May 2013 15:31:26 +0300 Subject: [PATCH 178/384] branch.h: fix typo in docs --- include/git2/branch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/git2/branch.h b/include/git2/branch.h index b15171360..d1838a63a 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -237,7 +237,7 @@ GIT_EXTERN(int) git_branch_is_head( * * @return Number of characters in the reference name * including the trailing NUL byte; GIT_ENOTFOUND - * when no remote matching remote was gound, + * when no remote matching remote was found, * GIT_EAMBIGUOUS when the branch maps to several remotes, * otherwise an error code. */ From 3d42e9a31e49547e852459cf372984b896d731dd Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Mon, 6 May 2013 20:32:20 +0300 Subject: [PATCH 179/384] git_branch_set_upstream with local branches Currently git_branch_set_upstream when passed a local branch creates invalid configuration, for ex. if we setup branch 'tracking_master' to track local 'master' libgit2 generates the following config ``` [branch "track_master"] remote = . merge = .refs/heads/track_master ``` The merge value is invalid and calling git_branch_upstream on 'tracking_master' results in invalid reference error. It should do: ``` [branch "track_master"] remote = . merge = refs/heads/master ``` --- src/branch.c | 3 ++- tests-clar/refs/branches/upstream.c | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/branch.c b/src/branch.c index ab661f422..830294941 100644 --- a/src/branch.c +++ b/src/branch.c @@ -521,7 +521,8 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name) goto on_error; if (local) { - if (git_buf_puts(&value, git_reference_name(branch)) < 0) + git_buf_clear(&value); + if (git_buf_puts(&value, git_reference_name(upstream)) < 0) goto on_error; } else { /* Get the remoe-tracking branch's refname in its repo */ diff --git a/tests-clar/refs/branches/upstream.c b/tests-clar/refs/branches/upstream.c index 2d0ebd240..648acb44d 100644 --- a/tests-clar/refs/branches/upstream.c +++ b/tests-clar/refs/branches/upstream.c @@ -103,6 +103,7 @@ void test_refs_branches_upstream__set_unset_upstream(void) repository = cl_git_sandbox_init("testrepo.git"); + /* remote */ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test")); cl_git_pass(git_branch_set_upstream(branch, "test/master")); @@ -112,6 +113,17 @@ void test_refs_branches_upstream__set_unset_upstream(void) cl_git_pass(git_config_get_string(&value, config, "branch.test.merge")); cl_assert_equal_s(value, "refs/heads/master"); + /* local */ + cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test")); + cl_git_pass(git_branch_set_upstream(branch, "master")); + + cl_git_pass(git_repository_config(&config, repository)); + cl_git_pass(git_config_get_string(&value, config, "branch.test.remote")); + cl_assert_equal_s(value, "."); + cl_git_pass(git_config_get_string(&value, config, "branch.test.merge")); + cl_assert_equal_s(value, "refs/heads/master"); + + /* unset */ cl_git_pass(git_branch_set_upstream(branch, NULL)); cl_git_fail_with(git_config_get_string(&value, config, "branch.test.merge"), GIT_ENOTFOUND); cl_git_fail_with(git_config_get_string(&value, config, "branch.test.remote"), GIT_ENOTFOUND); From 6e8659969a9e8e74cb9531454861d256e819e2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Tue, 7 May 2013 07:33:35 +0200 Subject: [PATCH 180/384] Add git_commit_owner to the public interface Just like git_tree_owner, etc. --- include/git2/commit.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/git2/commit.h b/include/git2/commit.h index f536ac7c8..a420ba635 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -69,6 +69,14 @@ GIT_EXTERN(void) git_commit_free(git_commit *commit); */ GIT_EXTERN(const git_oid *) git_commit_id(const git_commit *commit); +/** + * Get the repository that contains the commit. + * + * @param commit A previously loaded commit. + * @return Repository that contains this commit. + */ +GIT_EXTERN(git_repository *) git_commit_owner(const git_commit *commit); + /** * Get the encoding for the message of a commit, * as a string representing a standard encoding name. From 7f8cf6fefdd380f992f1100440f0eb030f6b6633 Mon Sep 17 00:00:00 2001 From: Sebastian Bauer Date: Tue, 7 May 2013 09:15:12 +0200 Subject: [PATCH 181/384] Fixed qsort_r() problem when targeting AmigaOS. We fall back to the libgit2-provided insert sort as done for other platforms. --- src/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.c b/src/util.c index 8c8bc1a6c..49530f8c8 100644 --- a/src/util.c +++ b/src/util.c @@ -685,7 +685,7 @@ static int GIT_STDLIB_CALL git__qsort_r_glue_cmp( void git__qsort_r( void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) { -#if defined(__MINGW32__) || defined(__OpenBSD__) +#if defined(__MINGW32__) || defined(__OpenBSD__) || defined(AMIGA) git__insertsort_r(els, nel, elsize, NULL, cmp, payload); #elif defined(GIT_WIN32) git__qsort_r_glue glue = { cmp, payload }; From e35e2684f693f28afb7a8f28028b4cb8bdd19f49 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 7 May 2013 04:32:17 -0700 Subject: [PATCH 182/384] Add GIT_DIFF_LINE_CONTEXT_EOFNL This adds a new line origin constant for the special line that is used when both files end without a newline. In the course of writing the tests for this, I was having problems with modifying a file but not having diff notice because it was the same size and modified less than one second from the start of the test, so I decided to start working on nanosecond timestamp support. This commit doesn't contain the nanosecond support, but it contains the reorganization of maybe_modified and the hooks so that if the nanosecond data were being read by stat() (or rather being copied by git_index_entry__init_from_stat), then the nsec would be taken into account. This new stuff could probably use some more tests, although there is some amount of it here. --- include/git2/diff.h | 6 +- src/diff.c | 129 ++++++++++++++++++++------------- src/diff.h | 1 + src/diff_output.c | 2 +- tests-clar/diff/diff_helpers.c | 11 +-- tests-clar/diff/patch.c | 37 ++++++++-- 6 files changed, 117 insertions(+), 69 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 0ef47c018..e10b65f7b 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -356,8 +356,10 @@ typedef enum { GIT_DIFF_LINE_CONTEXT = ' ', GIT_DIFF_LINE_ADDITION = '+', GIT_DIFF_LINE_DELETION = '-', - GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< Removed line w/o LF & added one with */ - GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */ + + GIT_DIFF_LINE_CONTEXT_EOFNL = '=', /**< Both files have no LF at end */ + GIT_DIFF_LINE_ADD_EOFNL = '>', /**< Old has no LF at end, new does */ + GIT_DIFF_LINE_DEL_EOFNL = '<', /**< Old has LF at end, new does not */ /* The following values will only be sent to a `git_diff_data_cb` when * the content of a diff is being formatted (eg. through diff --git a/src/diff.c b/src/diff.c index fbff1a6fb..e0dff9c95 100644 --- a/src/diff.c +++ b/src/diff.c @@ -389,6 +389,9 @@ static int diff_list_apply_options( /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ + /* Set GIT_DIFFCAPS_TRUST_NANOSECS on a platform basis */ + diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_NANOSECS; + /* If not given explicit `opts`, check `diff.xyz` configs */ if (!opts) { diff->opts.context_lines = config_int(cfg, "diff.context", 3); @@ -529,6 +532,13 @@ cleanup: return result; } +static bool diff_time_eq( + const git_index_time *a, const git_index_time *b, bool use_nanos) +{ + return a->seconds == a->seconds && + (!use_nanos || a->nanoseconds == b->nanoseconds); +} + typedef struct { git_repository *repo; git_iterator *old_iter; @@ -540,11 +550,51 @@ typedef struct { #define MODE_BITS_MASK 0000777 +static int maybe_modified_submodule( + git_delta_t *status, + git_oid *found_oid, + git_diff_list *diff, + diff_in_progress *info) +{ + int error = 0; + git_submodule *sub; + unsigned int sm_status = 0; + const git_oid *sm_oid; + + *status = GIT_DELTA_UNMODIFIED; + + if (!DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) && + !(error = git_submodule_lookup( + &sub, diff->repo, info->nitem->path)) && + git_submodule_ignore(sub) != GIT_SUBMODULE_IGNORE_ALL && + !(error = git_submodule_status(&sm_status, sub))) + { + /* check IS_WD_UNMODIFIED because this case is only used + * when the new side of the diff is the working directory + */ + if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status)) + *status = GIT_DELTA_MODIFIED; + + /* grab OID while we are here */ + if (git_oid_iszero(&info->nitem->oid) && + (sm_oid = git_submodule_wd_id(sub)) != NULL) + git_oid_cpy(found_oid, sm_oid); + } + + /* GIT_EEXISTS means a dir with .git in it was found - ignore it */ + if (error == GIT_EEXISTS) { + giterr_clear(); + error = 0; + } + + return error; +} + static int maybe_modified( git_diff_list *diff, diff_in_progress *info) { - git_oid noid, *use_noid = NULL; + git_oid noid; git_delta_t status = GIT_DELTA_MODIFIED; const git_index_entry *oitem = info->oitem; const git_index_entry *nitem = info->nitem; @@ -560,6 +610,8 @@ static int maybe_modified( &matched_pathspec)) return 0; + memset(&noid, 0, sizeof(noid)); + /* on platforms with no symlinks, preserve mode of existing symlinks */ if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir && !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS)) @@ -600,55 +652,30 @@ static int maybe_modified( * circumstances that can accelerate things or need special handling */ else if (git_oid_iszero(&nitem->oid) && new_is_workdir) { + bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0); + bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0); + + status = GIT_DELTA_UNMODIFIED; + /* TODO: add check against index file st_mtime to avoid racy-git */ - /* if the stat data looks exactly alike, then assume the same */ - if (omode == nmode && - oitem->file_size == nitem->file_size && - (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) || - (oitem->ctime.seconds == nitem->ctime.seconds)) && - oitem->mtime.seconds == nitem->mtime.seconds && - (!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) || - (oitem->dev == nitem->dev)) && - oitem->ino == nitem->ino && - oitem->uid == nitem->uid && - oitem->gid == nitem->gid) - status = GIT_DELTA_UNMODIFIED; - - else if (S_ISGITLINK(nmode)) { - int err; - git_submodule *sub; - - if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES)) - status = GIT_DELTA_UNMODIFIED; - else if ((err = git_submodule_lookup(&sub, diff->repo, nitem->path)) < 0) { - if (err == GIT_EEXISTS) - status = GIT_DELTA_UNMODIFIED; - else - return err; - } else if (git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL) - status = GIT_DELTA_UNMODIFIED; - else { - unsigned int sm_status = 0; - if (git_submodule_status(&sm_status, sub) < 0) - return -1; - - /* check IS_WD_UNMODIFIED because this case is only used - * when the new side of the diff is the working directory - */ - status = GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status) - ? GIT_DELTA_UNMODIFIED : GIT_DELTA_MODIFIED; - - /* grab OID while we are here */ - if (git_oid_iszero(&nitem->oid)) { - const git_oid *sm_oid = git_submodule_wd_id(sub); - if (sm_oid != NULL) { - git_oid_cpy(&noid, sm_oid); - use_noid = &noid; - } - } - } + if (S_ISGITLINK(nmode)) { + if (maybe_modified_submodule(&status, &noid, diff, info) < 0) + return -1; } + + /* if the stat data looks different, then mark modified - this just + * means that the OID will be recalculated below to confirm change + */ + else if (omode != nmode || + oitem->file_size != nitem->file_size || + !diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) || + (use_ctime && + !diff_time_eq(&oitem->ctime, &nitem->ctime, use_nanos)) || + oitem->ino != nitem->ino || + oitem->uid != nitem->uid || + oitem->gid != nitem->gid) + status = GIT_DELTA_MODIFIED; } /* if mode is GITLINK and submodules are ignored, then skip */ @@ -660,11 +687,10 @@ static int maybe_modified( * haven't calculated the OID of the new item, then calculate it now */ if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) { - if (!use_noid) { + if (git_oid_iszero(&noid)) { if (git_diff__oid_for_file(diff->repo, nitem->path, nitem->mode, nitem->file_size, &noid) < 0) return -1; - use_noid = &noid; } /* if oid matches, then mark unmodified (except submodules, where @@ -672,12 +698,13 @@ static int maybe_modified( * matches between the index and the workdir HEAD) */ if (omode == nmode && !S_ISGITLINK(omode) && - git_oid_equal(&oitem->oid, use_noid)) + git_oid_equal(&oitem->oid, &noid)) status = GIT_DELTA_UNMODIFIED; } return diff_delta__from_two( - diff, status, oitem, omode, nitem, nmode, use_noid, matched_pathspec); + diff, status, oitem, omode, nitem, nmode, + git_oid_iszero(&noid) ? NULL : &noid, matched_pathspec); } static bool entry_is_prefixed( diff --git a/src/diff.h b/src/diff.h index 48e20d1e3..16df431ed 100644 --- a/src/diff.h +++ b/src/diff.h @@ -26,6 +26,7 @@ enum { GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */ GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */ GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */ + GIT_DIFFCAPS_TRUST_NANOSECS = (1 << 5), /* use stat time nanoseconds */ }; enum { diff --git a/src/diff_output.c b/src/diff_output.c index bac8622c8..dda0f534d 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -726,7 +726,7 @@ static int diff_patch_cb(void *priv, mmbuffer_t *bufs, int len) char origin = (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL : (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL : - GIT_DIFF_LINE_CONTEXT; + GIT_DIFF_LINE_CONTEXT_EOFNL; if (ctxt->data_cb != NULL && ctxt->data_cb(patch->delta, &ctxt->range, diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c index e7f97c034..75eda0520 100644 --- a/tests-clar/diff/diff_helpers.c +++ b/tests-clar/diff/diff_helpers.c @@ -97,20 +97,15 @@ int diff_line_cb( e->lines++; switch (line_origin) { case GIT_DIFF_LINE_CONTEXT: + case GIT_DIFF_LINE_CONTEXT_EOFNL: /* techically not a line */ e->line_ctxt++; break; case GIT_DIFF_LINE_ADDITION: - e->line_adds++; - break; - case GIT_DIFF_LINE_ADD_EOFNL: - /* technically not a line add, but we'll count it as such */ + case GIT_DIFF_LINE_ADD_EOFNL: /* technically not a line add */ e->line_adds++; break; case GIT_DIFF_LINE_DELETION: - e->line_dels++; - break; - case GIT_DIFF_LINE_DEL_EOFNL: - /* technically not a line delete, but we'll count it as such */ + case GIT_DIFF_LINE_DEL_EOFNL: /* technically not a line delete */ e->line_dels++; break; default: diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c index 40b191dd5..c9a13f7fb 100644 --- a/tests-clar/diff/patch.c +++ b/tests-clar/diff/patch.c @@ -323,12 +323,12 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) } static void check_single_patch_stats( - git_repository *repo, size_t hunks, size_t adds, size_t dels) + git_repository *repo, size_t hunks, size_t adds, size_t dels, size_t ctxt) { git_diff_list *diff; git_diff_patch *patch; const git_diff_delta *delta; - size_t actual_adds, actual_dels; + size_t actual_ctxt, actual_adds, actual_dels; cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL)); @@ -339,9 +339,10 @@ static void check_single_patch_stats( cl_assert_equal_i((int)hunks, (int)git_diff_patch_num_hunks(patch)); - cl_git_pass( - git_diff_patch_line_stats(NULL, &actual_adds, &actual_dels, patch)); + cl_git_pass( git_diff_patch_line_stats( + &actual_ctxt, &actual_adds, &actual_dels, patch) ); + cl_assert_equal_sz(ctxt, actual_ctxt); cl_assert_equal_sz(adds, actual_adds); cl_assert_equal_sz(dels, actual_dels); @@ -369,14 +370,14 @@ void test_diff_patch__line_counts_with_eofnl(void) git_buf_consume(&content, end); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 1, 0, 1); + check_single_patch_stats(g_repo, 1, 0, 1, 3); /* remove trailing whitespace */ git_buf_rtrim(&content); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 2, 1, 2); + check_single_patch_stats(g_repo, 2, 1, 2, 6); /* add trailing whitespace */ @@ -388,7 +389,29 @@ void test_diff_patch__line_counts_with_eofnl(void) cl_git_pass(git_buf_putc(&content, '\n')); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 1, 1, 1); + check_single_patch_stats(g_repo, 1, 1, 1, 3); + + /* no trailing whitespace as context line */ + + { + /* walk back a couple lines, make space and insert char */ + char *scan = content.ptr + content.size; + int i; + + for (i = 0; i < 5; ++i) { + for (--scan; scan > content.ptr && *scan != '\n'; --scan) + /* seek to prev \n */; + } + cl_assert(scan > content.ptr); + + /* overwrite trailing \n with right-shifted content */ + memmove(scan + 1, scan, content.size - (scan - content.ptr) - 1); + /* insert '#' char into space we created */ + scan[1] = '#'; + } + cl_git_rewritefile("renames/songof7cities.txt", content.ptr); + + check_single_patch_stats(g_repo, 1, 1, 1, 6); git_buf_free(&content); git_config_free(cfg); From fd96f98e142cdb129f447ace189b2086951be313 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 7 May 2013 04:36:42 -0700 Subject: [PATCH 183/384] More tests for files with no newline at end --- tests-clar/diff/patch.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c index c9a13f7fb..6181ffbaf 100644 --- a/tests-clar/diff/patch.c +++ b/tests-clar/diff/patch.c @@ -323,7 +323,9 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) } static void check_single_patch_stats( - git_repository *repo, size_t hunks, size_t adds, size_t dels, size_t ctxt) + git_repository *repo, size_t hunks, + size_t adds, size_t dels, size_t ctxt, + const char *expected) { git_diff_list *diff; git_diff_patch *patch; @@ -346,6 +348,13 @@ static void check_single_patch_stats( cl_assert_equal_sz(adds, actual_adds); cl_assert_equal_sz(dels, actual_dels); + if (expected != NULL) { + char *text; + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected, text); + git__free(text); + } + git_diff_patch_free(patch); git_diff_list_free(diff); } @@ -370,14 +379,14 @@ void test_diff_patch__line_counts_with_eofnl(void) git_buf_consume(&content, end); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 1, 0, 1, 3); + check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL); /* remove trailing whitespace */ git_buf_rtrim(&content); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 2, 1, 2, 6); + check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL); /* add trailing whitespace */ @@ -389,7 +398,7 @@ void test_diff_patch__line_counts_with_eofnl(void) cl_git_pass(git_buf_putc(&content, '\n')); cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 1, 1, 1, 3); + check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL); /* no trailing whitespace as context line */ @@ -411,7 +420,23 @@ void test_diff_patch__line_counts_with_eofnl(void) } cl_git_rewritefile("renames/songof7cities.txt", content.ptr); - check_single_patch_stats(g_repo, 1, 1, 1, 6); + check_single_patch_stats( + g_repo, 1, 1, 1, 6, + /* below is pasted output of 'git diff' with fn context removed */ + "diff --git a/songof7cities.txt b/songof7cities.txt\n" + "index 378a7d9..3d0154e 100644\n" + "--- a/songof7cities.txt\n" + "+++ b/songof7cities.txt\n" + "@@ -42,7 +42,7 @@\n" + " \n" + " To the sound of trumpets shall their seed restore my Cities\n" + " Wealthy and well-weaponed, that once more may I behold\n" + "-All the world go softly when it walks before my Cities,\n" + "+#All the world go softly when it walks before my Cities,\n" + " And the horses and the chariots fleeing from them as of old!\n" + " \n" + " -- Rudyard Kipling\n" + "\\ No newline at end of file\n"); git_buf_free(&content); git_config_free(cfg); From d63eec6946341e5efa0514bbf2904db2ded2a294 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 7 May 2013 04:44:08 -0700 Subject: [PATCH 184/384] Improve diff function docs --- include/git2/diff.h | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index e10b65f7b..1feddd7a2 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -490,6 +490,8 @@ typedef struct { /** * Deallocate a diff list. + * + * @param diff The previously created diff list; cannot be used after free. */ GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff); @@ -499,12 +501,14 @@ GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff); * This is equivalent to `git diff ` * * The first tree will be used for the "old_file" side of the delta and the - * second tree will be used for the "new_file" side of the delta. + * second tree will be used for the "new_file" side of the delta. You can + * pass NULL to indicate an empty tree, although it is an error to pass + * NULL for both the `old_tree` and `new_tree`. * * @param diff Output pointer to a git_diff_list pointer to be allocated. * @param repo The repository containing the trees. - * @param old_tree A git_tree object to diff from. - * @param new_tree A git_tree object to diff to. + * @param old_tree A git_tree object to diff from, or NULL for empty tree. + * @param new_tree A git_tree object to diff to, or NULL for empty tree. * @param opts Structure with options to influence diff or NULL for defaults. */ GIT_EXTERN(int) git_diff_tree_to_tree( @@ -525,7 +529,7 @@ GIT_EXTERN(int) git_diff_tree_to_tree( * * @param diff Output pointer to a git_diff_list pointer to be allocated. * @param repo The repository containing the tree and index. - * @param old_tree A git_tree object to diff from. + * @param old_tree A git_tree object to diff from, or NULL for empty tree. * @param index The index to diff with; repo index used if NULL. * @param opts Structure with options to influence diff or NULL for defaults. */ @@ -584,7 +588,7 @@ GIT_EXTERN(int) git_diff_index_to_workdir( * * @param diff A pointer to a git_diff_list pointer that will be allocated. * @param repo The repository containing the tree. - * @param old_tree A git_tree object to diff from. + * @param old_tree A git_tree object to diff from, or NULL for empty tree. * @param opts Structure with options to influence diff or NULL for defaults. */ GIT_EXTERN(int) git_diff_tree_to_workdir( @@ -928,7 +932,14 @@ GIT_EXTERN(int) git_diff_patch_to_str( * to 1 and no call to the hunk_cb nor line_cb will be made (unless you pass * `GIT_DIFF_FORCE_TEXT` of course). * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @param old_blob Blob for old side of diff, or NULL for empty blob + * @param new_blob Blob for new side of diff, or NULL for empty blob + * @param options Options for diff, or NULL for default options + * @param file_cb Callback for "file"; made once if there is a diff; can be NULL + * @param hunk_cb Callback for each hunk in diff; can be NULL + * @param line_cb Callback for each line in diff; can be NULL + * @param payload Payload passed to each callback function + * @return 0 on success, GIT_EUSER on non-zero callback return, or error code */ GIT_EXTERN(int) git_diff_blobs( const git_blob *old_blob, @@ -951,7 +962,15 @@ GIT_EXTERN(int) git_diff_blobs( * entire content of the buffer added). Passing NULL to the buffer will do * the reverse, with GIT_DELTA_REMOVED and blob content removed. * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @param old_blob Blob for old side of diff, or NULL for empty blob + * @param buffer Raw data for new side of diff + * @param buffer_len Length of raw data for new side of diff + * @param options Options for diff, or NULL for default options + * @param file_cb Callback for "file"; made once if there is a diff; can be NULL + * @param hunk_cb Callback for each hunk in diff; can be NULL + * @param line_cb Callback for each line in diff; can be NULL + * @param payload Payload passed to each callback function + * @return 0 on success, GIT_EUSER on non-zero callback return, or error code */ GIT_EXTERN(int) git_diff_blob_to_buffer( const git_blob *old_blob, From c2f602f8a5265a4be6383127435a18a8be98c9fe Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 7 May 2013 07:15:39 -0700 Subject: [PATCH 185/384] Fix line numbering for patches with eofnl When a patch contained an eofnl change (i.e. the last line either gained or lost a newline), the oldno and newno line number values for the lines in the last hunk of the patch were not useful. This makes them behave in a more expected manner. --- src/diff_output.c | 2 + tests-clar/diff/patch.c | 128 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 6 deletions(-) diff --git a/src/diff_output.c b/src/diff_output.c index dda0f534d..2214ae1b5 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -930,11 +930,13 @@ static int diff_patch_line_cb( switch (line_origin) { case GIT_DIFF_LINE_ADDITION: + case GIT_DIFF_LINE_DEL_EOFNL: line->oldno = -1; line->newno = patch->newno; patch->newno += line->lines; break; case GIT_DIFF_LINE_DELETION: + case GIT_DIFF_LINE_ADD_EOFNL: line->oldno = patch->oldno; line->newno = -1; patch->oldno += line->lines; diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c index 6181ffbaf..f9e913a74 100644 --- a/tests-clar/diff/patch.c +++ b/tests-clar/diff/patch.c @@ -226,6 +226,7 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) size_t hdrlen, hunklen, textlen; char origin; int oldno, newno; + git_buf old_content = GIT_BUF_INIT, actual = GIT_BUF_INIT; const char *new_content = "The Song of Seven Cities\n------------------------\n\nI WAS Lord of Cities very sumptuously builded.\nSeven roaring Cities paid me tribute from afar.\nIvory their outposts were--the guardrooms of them gilded,\nAnd garrisoned with Amazons invincible in war.\n\nThis is some new text;\nNot as good as the old text;\nBut here it is.\n\nSo they warred and trafficked only yesterday, my Cities.\nTo-day there is no mark or mound of where my Cities stood.\nFor the River rose at midnight and it washed away my Cities.\nThey are evened with Atlantis and the towns before the Flood.\n\nRain on rain-gorged channels raised the water-levels round them,\nFreshet backed on freshet swelled and swept their world from sight,\nTill the emboldened floods linked arms and, flashing forward, drowned them--\nDrowned my Seven Cities and their peoples in one night!\n\nLow among the alders lie their derelict foundations,\nThe beams wherein they trusted and the plinths whereon they built--\nMy rulers and their treasure and their unborn populations,\nDead, destroyed, aborted, and defiled with mud and silt!\n\nAnother replacement;\nBreaking up the poem;\nGenerating some hunks.\n\nTo the sound of trumpets shall their seed restore my Cities\nWealthy and well-weaponed, that once more may I behold\nAll the world go softly when it walks before my Cities,\nAnd the horses and the chariots fleeing from them as of old!\n\n -- Rudyard Kipling\n"; g_repo = cl_git_sandbox_init("renames"); @@ -233,6 +234,9 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) cl_git_pass(git_config_new(&cfg)); git_repository_set_config(g_repo, cfg); + cl_git_pass( + git_futils_readbuffer(&old_content, "renames/songof7cities.txt")); + cl_git_rewritefile("renames/songof7cities.txt", new_content); cl_git_pass(git_repository_head_tree(&head, g_repo)); @@ -263,21 +267,24 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) cl_git_pass(git_diff_patch_get_line_in_hunk( &origin, &text, &textlen, &oldno, &newno, patch, 0, 0)); cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin); - cl_assert(strncmp("Ivory their outposts were--the guardrooms of them gilded,\n", text, textlen) == 0); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s("Ivory their outposts were--the guardrooms of them gilded,\n", actual.ptr); cl_assert_equal_i(6, oldno); cl_assert_equal_i(6, newno); cl_git_pass(git_diff_patch_get_line_in_hunk( &origin, &text, &textlen, &oldno, &newno, patch, 0, 3)); cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin); - cl_assert(strncmp("All the world went softly when it walked before my Cities--\n", text, textlen) == 0); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s("All the world went softly when it walked before my Cities--\n", actual.ptr); cl_assert_equal_i(9, oldno); cl_assert_equal_i(-1, newno); cl_git_pass(git_diff_patch_get_line_in_hunk( &origin, &text, &textlen, &oldno, &newno, patch, 0, 12)); cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin); - cl_assert(strncmp("This is some new text;\n", text, textlen) == 0); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s("This is some new text;\n", actual.ptr); cl_assert_equal_i(-1, oldno); cl_assert_equal_i(9, newno); @@ -298,26 +305,103 @@ void test_diff_patch__hunks_have_correct_line_numbers(void) cl_git_pass(git_diff_patch_get_line_in_hunk( &origin, &text, &textlen, &oldno, &newno, patch, 1, 0)); cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin); - cl_assert(strncmp("My rulers and their treasure and their unborn populations,\n", text, textlen) == 0); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s("My rulers and their treasure and their unborn populations,\n", actual.ptr); cl_assert_equal_i(31, oldno); cl_assert_equal_i(25, newno); cl_git_pass(git_diff_patch_get_line_in_hunk( &origin, &text, &textlen, &oldno, &newno, patch, 1, 3)); cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin); - cl_assert(strncmp("The Daughters of the Palace whom they cherished in my Cities,\n", text, textlen) == 0); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s("The Daughters of the Palace whom they cherished in my Cities,\n", actual.ptr); cl_assert_equal_i(34, oldno); cl_assert_equal_i(-1, newno); cl_git_pass(git_diff_patch_get_line_in_hunk( &origin, &text, &textlen, &oldno, &newno, patch, 1, 12)); cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin); - cl_assert(strncmp("Another replacement;\n", text, textlen) == 0); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s("Another replacement;\n", actual.ptr); cl_assert_equal_i(-1, oldno); cl_assert_equal_i(28, newno); git_diff_patch_free(patch); git_diff_list_free(diff); + + /* Let's check line numbers when there is no newline */ + + git_buf_rtrim(&old_content); + cl_git_rewritefile("renames/songof7cities.txt", old_content.ptr); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt)); + + cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); + + cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0)); + + cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(patch)); + + /* check hunk 0 */ + + cl_git_pass( + git_diff_patch_get_hunk(&range, &hdr, &hdrlen, &hunklen, patch, 0)); + + cl_assert_equal_i(6, (int)hunklen); + + cl_assert_equal_i(46, (int)range->old_start); + cl_assert_equal_i(4, (int)range->old_lines); + cl_assert_equal_i(46, (int)range->new_start); + cl_assert_equal_i(4, (int)range->new_lines); + + cl_assert_equal_i(6, (int)git_diff_patch_num_lines_in_hunk(patch, 0)); + + cl_git_pass(git_diff_patch_get_line_in_hunk( + &origin, &text, &textlen, &oldno, &newno, patch, 0, 1)); + cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s("And the horses and the chariots fleeing from them as of old!\n", actual.ptr); + cl_assert_equal_i(47, oldno); + cl_assert_equal_i(47, newno); + + cl_git_pass(git_diff_patch_get_line_in_hunk( + &origin, &text, &textlen, &oldno, &newno, patch, 0, 2)); + cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s("\n", actual.ptr); + cl_assert_equal_i(48, oldno); + cl_assert_equal_i(48, newno); + + cl_git_pass(git_diff_patch_get_line_in_hunk( + &origin, &text, &textlen, &oldno, &newno, patch, 0, 3)); + cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s(" -- Rudyard Kipling\n", actual.ptr); + cl_assert_equal_i(49, oldno); + cl_assert_equal_i(-1, newno); + + cl_git_pass(git_diff_patch_get_line_in_hunk( + &origin, &text, &textlen, &oldno, &newno, patch, 0, 4)); + cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s(" -- Rudyard Kipling", actual.ptr); + cl_assert_equal_i(-1, oldno); + cl_assert_equal_i(49, newno); + + cl_git_pass(git_diff_patch_get_line_in_hunk( + &origin, &text, &textlen, &oldno, &newno, patch, 0, 5)); + cl_assert_equal_i(GIT_DIFF_LINE_DEL_EOFNL, (int)origin); + cl_git_pass(git_buf_set(&actual, text, textlen)); + cl_assert_equal_s("\n\\ No newline at end of file\n", actual.ptr); + cl_assert_equal_i(-1, oldno); + cl_assert_equal_i(49, newno); + + git_diff_patch_free(patch); + git_diff_list_free(diff); + + git_buf_free(&actual); + git_buf_free(&old_content); git_tree_free(head); git_config_free(cfg); } @@ -355,6 +439,38 @@ static void check_single_patch_stats( git__free(text); } + /* walk lines in hunk with basic sanity checks */ + for (; hunks > 0; --hunks) { + size_t i, max_i; + int lastoldno = -1, oldno, lastnewno = -1, newno; + char origin; + + max_i = git_diff_patch_num_lines_in_hunk(patch, hunks - 1); + + for (i = 0; i < max_i; ++i) { + int expected = 1; + + cl_git_pass(git_diff_patch_get_line_in_hunk( + &origin, NULL, NULL, &oldno, &newno, patch, hunks - 1, i)); + + if (origin == GIT_DIFF_LINE_ADD_EOFNL || + origin == GIT_DIFF_LINE_DEL_EOFNL || + origin == GIT_DIFF_LINE_CONTEXT_EOFNL) + expected = 0; + + if (oldno >= 0) { + if (lastoldno >= 0) + cl_assert_equal_i(expected, oldno - lastoldno); + lastoldno = oldno; + } + if (newno >= 0) { + if (lastnewno >= 0) + cl_assert_equal_i(expected, newno - lastnewno); + lastnewno = newno; + } + } + } + git_diff_patch_free(patch); git_diff_list_free(diff); } From 505b5d0c815a9b8f78bf7267e57ea433f04d0b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 7 May 2013 16:01:22 +0200 Subject: [PATCH 186/384] remote: correctly interpret tagopt '--tags' When tagopt is set to '--tags', we should only take the default tags refspec into account and ignore any configured ones. Bring the code into compliance. --- src/fetch.c | 12 +++++++++--- src/remote.c | 28 ++++++++++++++++++---------- tests-clar/network/remote/local.c | 17 +++++++++++++++++ 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/fetch.c b/src/fetch.c index 8ae34bddf..b5ec69777 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -34,10 +34,16 @@ static int filter_ref__cb(git_remote_head *head, void *payload) if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) p->found_head = 1; - else if (git_remote__matching_refspec(p->remote, head->name)) + else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { + /* + * If tagopt is --tags, then we only use the default + * tags refspec and ignore the remote's + */ + if (git_refspec_src_matches(p->tagspec, head->name)) match = 1; - else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL && - git_refspec_src_matches(p->tagspec, head->name)) + else + return 0; + } else if (git_remote__matching_refspec(p->remote, head->name)) match = 1; if (!match) diff --git a/src/remote.c b/src/remote.c index cba6b2548..692537636 100644 --- a/src/remote.c +++ b/src/remote.c @@ -957,30 +957,38 @@ on_error: int git_remote_update_tips(git_remote *remote) { - git_refspec *spec; + git_refspec *spec, tagspec; git_vector refs; + int error; size_t i; + + if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) + return -1; + if (git_vector_init(&refs, 16, NULL) < 0) return -1; - if (git_remote_ls(remote, store_refs, &refs) < 0) - goto on_error; + if ((error = git_remote_ls(remote, store_refs, &refs)) < 0) + goto out; + + if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { + error = update_tips_for_spec(remote, &tagspec, &refs); + goto out; + } git_vector_foreach(&remote->refspecs, i, spec) { if (spec->push) continue; - if (update_tips_for_spec(remote, spec, &refs) < 0) - goto on_error; + if ((error = update_tips_for_spec(remote, spec, &refs)) < 0) + goto out; } +out: + git_refspec__free(&tagspec); git_vector_free(&refs); - return 0; - -on_error: - git_vector_free(&refs); - return -1; + return error; } int git_remote_connected(git_remote *remote) diff --git a/tests-clar/network/remote/local.c b/tests-clar/network/remote/local.c index 74ef63dc9..3cb8a25d6 100644 --- a/tests-clar/network/remote/local.c +++ b/tests-clar/network/remote/local.c @@ -141,3 +141,20 @@ void test_network_remote_local__shorthand_fetch_refspec1(void) cl_git_fail(git_reference_lookup(&ref, repo, "refs/tags/hard_tag")); } + +void test_network_remote_local__tagopt(void) +{ + git_reference *ref; + + connect_to_local_repository(cl_fixture("testrepo.git")); + git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL); + + cl_git_pass(git_remote_download(remote, NULL, NULL)); + cl_git_pass(git_remote_update_tips(remote)); + + + cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master")); + + cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/hard_tag")); + git_reference_free(ref); +} From 0f938c6b8fbddc8810b05603c29751c0b5b7323c Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 7 May 2013 09:59:53 -0700 Subject: [PATCH 187/384] Fix win32 type warnings --- src/remote.c | 2 +- tests-clar/merge/trees/trivial.c | 2 +- tests-clar/network/remote/remotes.c | 12 ++++++------ tests-clar/object/cache.c | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/remote.c b/src/remote.c index cba6b2548..9873fe6de 100644 --- a/src/remote.c +++ b/src/remote.c @@ -292,7 +292,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) git_buf_clear(&buf); git_buf_printf(&buf, "remote.%s.pushurl", name); - if ((error = get_optional_config(config, &buf, NULL, &val)) < 0) + if ((error = get_optional_config(config, &buf, NULL, (void *)&val)) < 0) goto cleanup; if (val) { diff --git a/tests-clar/merge/trees/trivial.c b/tests-clar/merge/trees/trivial.c index 7d8d2cbf5..e6096e2dc 100644 --- a/tests-clar/merge/trees/trivial.c +++ b/tests-clar/merge/trees/trivial.c @@ -67,7 +67,7 @@ static int merge_trivial(git_index **index, const char *ours, const char *theirs static int merge_trivial_conflict_entrycount(git_index *index) { const git_index_entry *entry; - size_t count = 0; + int count = 0; size_t i; for (i = 0; i < git_index_entrycount(index); i++) { diff --git a/tests-clar/network/remote/remotes.c b/tests-clar/network/remote/remotes.c index 4c24db8eb..21f27bcc6 100644 --- a/tests-clar/network/remote/remotes.c +++ b/tests-clar/network/remote/remotes.c @@ -118,13 +118,13 @@ void test_network_remote_remotes__add_fetchspec(void) cl_git_pass(git_remote_add_fetch(_remote, "refs/*:refs/*")); size++; - cl_assert_equal_i(size, git_remote_refspec_count(_remote)); + cl_assert_equal_i((int)size, (int)git_remote_refspec_count(_remote)); _refspec = git_remote_get_refspec(_remote, size - 1); cl_assert_equal_s(git_refspec_src(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*"); - cl_assert_equal_i(_refspec->push, false); + cl_assert_equal_b(_refspec->push, false); } void test_network_remote_remotes__add_pushspec(void) @@ -135,14 +135,14 @@ void test_network_remote_remotes__add_pushspec(void) cl_git_pass(git_remote_add_push(_remote, "refs/*:refs/*")); size++; - cl_assert_equal_i(size, git_remote_refspec_count(_remote)); + cl_assert_equal_i((int)size, (int)git_remote_refspec_count(_remote)); _refspec = git_remote_get_refspec(_remote, size - 1); cl_assert_equal_s(git_refspec_src(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*"); cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*"); - cl_assert_equal_i(_refspec->push, true); + cl_assert_equal_b(_refspec->push, true); } void test_network_remote_remotes__save(void) @@ -169,12 +169,12 @@ void test_network_remote_remotes__save(void) cl_git_pass(git_remote_load(&_remote, _repo, "upstream")); cl_git_pass(git_remote_get_fetch_refspecs(&array, _remote)); - cl_assert_equal_i(1, array.count); + cl_assert_equal_i(1, (int)array.count); cl_assert_equal_s(fetch_refspec, array.strings[0]); git_strarray_free(&array); cl_git_pass(git_remote_get_push_refspecs(&array, _remote)); - cl_assert_equal_i(1, array.count); + cl_assert_equal_i(1, (int)array.count); cl_assert_equal_s(push_refspec, array.strings[0]); cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2"); cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/libgit2_push"); diff --git a/tests-clar/object/cache.c b/tests-clar/object/cache.c index e06760e2a..b927b2514 100644 --- a/tests-clar/object/cache.c +++ b/tests-clar/object/cache.c @@ -80,7 +80,7 @@ void test_object_cache__cache_everything(void) cl_assert_equal_i(count + 1, (int)git_cache_size(&g_repo->objects)); } - cl_assert_equal_i(i, git_cache_size(&g_repo->objects) - start); + cl_assert_equal_i(i, (int)git_cache_size(&g_repo->objects) - start); git_odb_free(odb); @@ -135,7 +135,7 @@ void test_object_cache__cache_no_blobs(void) } } - cl_assert_equal_i(nonblobs, git_cache_size(&g_repo->objects) - start); + cl_assert_equal_i(nonblobs, (int)git_cache_size(&g_repo->objects) - start); git_odb_free(odb); } From c36565c0eea7b97726cffaeaf9abe2a3279ff70b Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Tue, 7 May 2013 13:43:10 -0400 Subject: [PATCH 188/384] Added SSH public key authentication --- include/git2/transport.h | 32 +++++++++++++++++++++++++- src/transports/cred.c | 49 ++++++++++++++++++++++++++++++++++++++++ src/transports/ssh.c | 12 ++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/include/git2/transport.h b/include/git2/transport.h index 31572c16a..cc29f8b0e 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -11,6 +11,8 @@ #include "net.h" #include "types.h" +#include + /** * @file git2/transport.h * @brief Git transport interfaces and functions @@ -28,6 +30,7 @@ typedef enum { /* git_cred_userpass_plaintext */ GIT_CREDTYPE_USERPASS_PLAINTEXT = 1, GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE = 2, + GIT_CREDTYPE_SSH_PUBLICKEY = 3, } git_credtype_t; /* The base structure for all credential types */ @@ -44,7 +47,7 @@ typedef struct git_cred_userpass_plaintext { char *password; } git_cred_userpass_plaintext; -/* A plaintext username and password */ +/* A ssh key file and passphrase */ typedef struct git_cred_ssh_keyfile_passphrase { git_cred parent; char *publickey; @@ -52,6 +55,15 @@ typedef struct git_cred_ssh_keyfile_passphrase { char *passphrase; } git_cred_ssh_keyfile_passphrase; +/* A ssh public key and authentication callback */ +typedef struct git_cred_ssh_publickey { + git_cred parent; + char *publickey; + size_t publickey_len; + void *sign_callback; + void *sign_data; +} git_cred_ssh_publickey; + /** * Creates a new plain-text username and password credential object. * The supplied credential parameter will be internally duplicated. @@ -82,6 +94,24 @@ GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new( const char *privatekey, const char *passphrase); +/** + * Creates a new ssh public key credential object. + * The supplied credential parameter will be internally duplicated. + * + * @param out The newly created credential object. + * @param publickey The bytes of the public key. + * @param publickey_len The length of the public key in bytes. + * @param sign_callback The callback method for authenticating. + * @param sign_data The abstract data sent to the sign_callback method. + * @return 0 for success or an error code for failure + */ +GIT_EXTERN(int) git_cred_ssh_publickey_new( + git_cred **out, + const char *publickey, + size_t publickey_len, + LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), + void *sign_data); + /** * Signature of a function which acquires a credential object. * diff --git a/src/transports/cred.c b/src/transports/cred.c index 83820ec05..5d5e745ff 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -135,3 +135,52 @@ int git_cred_ssh_keyfile_passphrase_new( *cred = &c->parent; return 0; } + +static void ssh_publickey_free(struct git_cred *cred) +{ + git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred; + + git__free(c->publickey); + + c->sign_callback = NULL; + c->sign_data = NULL; + + memset(c, 0, sizeof(*c)); + + git__free(c); +} + +int git_cred_ssh_publickey_new( + git_cred **cred, + const char *publickey, + size_t publickey_len, + LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), + void *sign_data) +{ + git_cred_ssh_publickey *c; + + if (!cred) + return -1; + + c = git__malloc(sizeof(git_cred_ssh_publickey)); + GITERR_CHECK_ALLOC(c); + + c->parent.credtype = GIT_CREDTYPE_SSH_PUBLICKEY; + c->parent.free = ssh_publickey_free; + + c->publickey = git__malloc(publickey_len); + memcpy(c->publickey, publickey, publickey_len); + + if (!c->publickey) { + git__free(c); + return -1; + } + + c->publickey_len = publickey_len; + + c->sign_callback = sign_callback; + c->sign_data = sign_data; + + *cred = &c->parent; + return 0; +} diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 9ee13becf..a1df6c492 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -253,6 +253,18 @@ static int _git_ssh_authenticate_session( ); break; } + case GIT_CREDTYPE_SSH_PUBLICKEY: { + git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred; + rc = libssh2_userauth_publickey( + session, + user, + (const unsigned char *)c->publickey, + c->publickey_len, + c->sign_callback, + &c->sign_data + ); + break; + } default: rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED; } From 574b86b72294e4b0fd08281f4c19e013f0d9b137 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Tue, 7 May 2013 13:53:23 -0400 Subject: [PATCH 189/384] Fixed compilation issues when libssh2 is missing --- include/git2/transport.h | 6 ++++++ src/transport.c | 13 +++++++++++-- src/transports/cred.c | 2 ++ src/transports/ssh.c | 4 ++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/include/git2/transport.h b/include/git2/transport.h index cc29f8b0e..c455e9ee0 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -11,7 +11,9 @@ #include "net.h" #include "types.h" +#ifdef GIT_SSH #include +#endif /** * @file git2/transport.h @@ -47,6 +49,7 @@ typedef struct git_cred_userpass_plaintext { char *password; } git_cred_userpass_plaintext; +#ifdef GIT_SSH /* A ssh key file and passphrase */ typedef struct git_cred_ssh_keyfile_passphrase { git_cred parent; @@ -63,6 +66,7 @@ typedef struct git_cred_ssh_publickey { void *sign_callback; void *sign_data; } git_cred_ssh_publickey; +#endif /** * Creates a new plain-text username and password credential object. @@ -78,6 +82,7 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new( const char *username, const char *password); +#ifdef GIT_SSH /** * Creates a new ssh key file and passphrase credential object. * The supplied credential parameter will be internally duplicated. @@ -111,6 +116,7 @@ GIT_EXTERN(int) git_cred_ssh_publickey_new( size_t publickey_len, LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), void *sign_data); +#endif /** * Signature of a function which acquires a credential object. diff --git a/src/transport.c b/src/transport.c index 6a8e67df6..37c244c97 100644 --- a/src/transport.c +++ b/src/transport.c @@ -20,20 +20,25 @@ typedef struct transport_definition { static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1 }; static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0 }; +#ifdef GIT_SSH static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0 }; +#endif static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL }; -#ifdef GIT_WIN32 +#ifdef GIT_SSH +static transport_definition ssh_transport_definition = { "ssh://", 1, git_transport_smart, &ssh_subtransport_definition }; +#else static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL }; #endif -static transport_definition ssh_transport_definition = { "ssh://", 1, git_transport_smart, &ssh_subtransport_definition }; static transport_definition transports[] = { {"git://", 1, git_transport_smart, &git_subtransport_definition}, {"http://", 1, git_transport_smart, &http_subtransport_definition}, {"https://", 1, git_transport_smart, &http_subtransport_definition}, {"file://", 1, git_transport_local, NULL}, +#ifdef GIT_SSH {"ssh://", 1, git_transport_smart, &ssh_subtransport_definition}, +#endif {NULL, 0, 0} }; @@ -76,7 +81,11 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void * /* It could be a SSH remote path. Check to see if there's a : * SSH is an unsupported transport mechanism in this version of libgit2 */ if (!definition && strrchr(url, ':')) +#ifdef GIT_SSH definition = &ssh_transport_definition; +#else + definition = &dummy_transport_definition; +#endif /* Check to see if the path points to a file on the local file system */ if (!definition && git_path_exists(url) && git_path_isdir(url)) diff --git a/src/transports/cred.c b/src/transports/cred.c index 5d5e745ff..e4d205ea2 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -59,6 +59,7 @@ int git_cred_userpass_plaintext_new( return 0; } +#ifdef GIT_SSH static void ssh_keyfile_passphrase_free(struct git_cred *cred) { git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred; @@ -184,3 +185,4 @@ int git_cred_ssh_publickey_new( *cred = &c->parent; return 0; } +#endif diff --git a/src/transports/ssh.c b/src/transports/ssh.c index a1df6c492..167df03db 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -5,6 +5,8 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#ifdef GIT_SSH + #include "git2.h" #include "buffer.h" #include "netops.h" @@ -520,3 +522,5 @@ int git_smart_subtransport_ssh(git_smart_subtransport **out, git_transport *owne *out = (git_smart_subtransport *) t; return 0; } + +#endif From 7369b3c3bf396e466d065f9921415fe2b9d69a7a Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Tue, 7 May 2013 14:26:33 -0400 Subject: [PATCH 190/384] Added libssh2 cmake module --- .gitignore | 2 +- CMakeLists.txt | 13 ++++++++++ cmake/Modules/FindLibSSH2.cmake | 44 +++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 cmake/Modules/FindLibSSH2.cmake diff --git a/.gitignore b/.gitignore index 949baec98..bba9d5d5c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,8 +24,8 @@ msvc/Release/ *.sdf *.opensdf *.aps -CMake* *.cmake +!cmake/Modules/*.cmake .DS_Store *~ tags diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bd25aacc..20d63fecb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,8 @@ PROJECT(libgit2 C) CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") + # Build options # OPTION( SONAME "Set the (SO)VERSION of the target" ON ) @@ -138,6 +140,15 @@ ELSE() FILE(GLOB SRC_ZLIB deps/zlib/*.c) ENDIF() +IF(NOT LIBSSH2_LIBRARY) + FIND_PACKAGE(LIBSSH2 QUIET) +ENDIF() +IF (LIBSSH2_FOUND) + ADD_DEFINITIONS(-DGIT_SSH) + INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIR}) + SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES}) +ENDIF() + # Platform specific compilation flags IF (MSVC) @@ -280,6 +291,7 @@ FILE(GLOB SRC_GIT2 src/*.c src/transports/*.c src/xdiff/*.c) # Compile and link libgit2 ADD_LIBRARY(git2 ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC}) TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES}) +TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES}) TARGET_OS_LIBRARIES(git2) # Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240) @@ -340,6 +352,7 @@ IF (BUILD_CLAR) ADD_EXECUTABLE(libgit2_clar ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1}) TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES}) + TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES}) TARGET_OS_LIBRARIES(libgit2_clar) MSVC_SPLIT_SOURCES(libgit2_clar) diff --git a/cmake/Modules/FindLibSSH2.cmake b/cmake/Modules/FindLibSSH2.cmake new file mode 100644 index 000000000..6347d60ea --- /dev/null +++ b/cmake/Modules/FindLibSSH2.cmake @@ -0,0 +1,44 @@ +if (LIBSSH2_LIBRARIES AND LIBSSH2_INCLUDE_DIRS) + set(LIBSSH2_FOUND TRUE) +else (LIBSSH2_LIBRARIES AND LIBSSH2_INCLUDE_DIRS) + find_path(LIBSSH2_INCLUDE_DIR + NAMES + libssh2.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include + ) + + find_library(LIBSSH2_LIBRARY + NAMES + ssh2 + libssh2 + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + if (LIBSSH2_INCLUDE_DIR AND LIBSSH2_LIBRARY) + set(LIBSSH2_FOUND TRUE) + endif (LIBSSH2_INCLUDE_DIR AND LIBSSH2_LIBRARY) + + if (LIBSSH2_FOUND) + set(LIBSSH2_INCLUDE_DIRS + ${LIBSSH2_INCLUDE_DIR} + ) + + set(LIBSSH2_LIBRARIES + ${LIBSSH2_LIBRARIES} + ${LIBSSH2_LIBRARY} + ) + endif (LIBSSH2_FOUND) +endif (LIBSSH2_LIBRARIES AND LIBSSH2_INCLUDE_DIRS) + From 4ca3d6d28f959793f412a6eb02211020a9204050 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Tue, 7 May 2013 14:37:15 -0400 Subject: [PATCH 191/384] Added ifdef --- include/git2/transport.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/git2/transport.h b/include/git2/transport.h index c455e9ee0..b3d43ebf6 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -31,8 +31,10 @@ GIT_BEGIN_DECL typedef enum { /* git_cred_userpass_plaintext */ GIT_CREDTYPE_USERPASS_PLAINTEXT = 1, +#ifdef GIT_SSH GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE = 2, GIT_CREDTYPE_SSH_PUBLICKEY = 3, +#endif } git_credtype_t; /* The base structure for all credential types */ From a4b75dcf56bc8e6d295cae89e2f5871c4707af21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 6 May 2013 21:51:25 +0200 Subject: [PATCH 192/384] repo: unconditionally create a global config backend When a repository is initialised, we need to probe to see if there is a global config to load. If this is not the case, the user isn't able to write to the global config without creating the backend and adding it themselves, which is inconvenient and overly complex. Unconditionally create and add a backend for the global config file regardless of whether it exists as a convenience for users. To enable this, we allow creating backends to files that do not exist yet, changing the semantics somewhat, and making some tests invalid. --- src/config.c | 41 +++++++++++++++++++--- src/config.h | 3 ++ src/repository.c | 4 +++ tests-clar/config/read.c | 7 ---- tests-clar/repo/config.c | 75 ++++++++++++++++++++++++++++++++++++++++ tests-clar/repo/open.c | 2 +- 6 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 tests-clar/repo/config.c diff --git a/src/config.c b/src/config.c index 3f475ea63..99aa00f50 100644 --- a/src/config.c +++ b/src/config.c @@ -91,13 +91,15 @@ int git_config_add_file_ondisk( int force) { git_config_backend *file = NULL; + struct stat st; int res; assert(cfg && path); - if (!git_path_isfile(path)) { - giterr_set(GITERR_CONFIG, "Cannot find config file '%s'", path); - return GIT_ENOTFOUND; + res = p_stat(path, &st); + if (res < 0 && errno != ENOENT) { + giterr_set(GITERR_CONFIG, "Error stat'ing config file '%s'", path); + return -1; } if (git_config_file__ondisk(&file, path) < 0) @@ -381,7 +383,6 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) internal = git_vector_get(&cfg->files, 0); if (!internal) - /* Should we auto-vivify .git/config? Tricky from this location */ return config_error_nofiles(name); file = internal->file; @@ -598,6 +599,33 @@ int git_config_find_system(char *system_config_path, size_t length) system_config_path, length, git_config_find_system_r); } +int git_config__global_location(git_buf *buf) +{ + const git_buf *paths; + const char *sep, *start; + size_t len; + + if (git_futils_dirs_get(&paths, GIT_FUTILS_DIR_GLOBAL) < 0) + return -1; + + /* no paths, so give up */ + if (git_buf_len(paths) == 0) + return -1; + + start = git_buf_cstr(paths); + sep = strchr(start, GIT_PATH_LIST_SEPARATOR); + + if (sep) + len = sep - start; + else + len = paths->size; + + if (git_buf_set(buf, start, len) < 0) + return -1; + + return git_buf_joinpath(buf, buf->ptr, GIT_CONFIG_FILENAME_GLOBAL); +} + int git_config_open_default(git_config **out) { int error; @@ -606,9 +634,12 @@ int git_config_open_default(git_config **out) error = git_config_new(&cfg); - if (!error && !git_config_find_global_r(&buf)) + if (!error && (!git_config_find_global_r(&buf) || + !git_config__global_location(&buf))) { error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_GLOBAL, 0); + } else { + } if (!error && !git_config_find_xdg_r(&buf)) error = git_config_add_file_ondisk(cfg, buf.ptr, diff --git a/src/config.h b/src/config.h index c43e47e82..c5c11ae14 100644 --- a/src/config.h +++ b/src/config.h @@ -28,6 +28,9 @@ extern int git_config_find_global_r(git_buf *global_config_path); extern int git_config_find_xdg_r(git_buf *system_config_path); extern int git_config_find_system_r(git_buf *system_config_path); + +extern int git_config__global_location(git_buf *buf); + extern int git_config_rename_section( git_repository *repo, const char *old_section_name, /* eg "branch.dummy" */ diff --git a/src/repository.c b/src/repository.c index 44e7ca3c4..e2cedc0f7 100644 --- a/src/repository.c +++ b/src/repository.c @@ -594,6 +594,10 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) git_config_find_xdg_r(&xdg_buf); git_config_find_system_r(&system_buf); + /* If there is no global file, open a backend for it anyway */ + if (git_buf_len(&global_buf) == 0) + git_config__global_location(&global_buf); + error = load_config( &config, repo, path_unless_empty(&global_buf), diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c index c85826886..9f943d0f6 100644 --- a/tests-clar/config/read.c +++ b/tests-clar/config/read.c @@ -449,10 +449,3 @@ void test_config_read__can_load_and_parse_an_empty_config_file(void) git_config_free(cfg); } - -void test_config_read__cannot_load_a_non_existing_config_file(void) -{ - git_config *cfg; - - cl_assert_equal_i(GIT_ENOTFOUND, git_config_open_ondisk(&cfg, "./no.config")); -} diff --git a/tests-clar/repo/config.c b/tests-clar/repo/config.c new file mode 100644 index 000000000..086fb5e4f --- /dev/null +++ b/tests-clar/repo/config.c @@ -0,0 +1,75 @@ +#include "clar_libgit2.h" +#include "fileops.h" +#include + +git_buf path = GIT_BUF_INIT; + +void test_repo_config__initialize(void) +{ + cl_fixture_sandbox("empty_standard_repo"); + cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git")); + + git_buf_clear(&path); + + cl_must_pass(p_mkdir("alternate", 0777)); + cl_git_pass(git_path_prettify(&path, "alternate", NULL)); + +} + +void test_repo_config__cleanup(void) +{ + cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)); + + git_buf_free(&path); + cl_fixture_cleanup("empty_standard_repo"); +} + +void test_repo_config__open_missing_global(void) +{ + git_repository *repo; + git_config *config, *global; + + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL)); + + cl_git_pass(git_config_set_string(global, "test.set", "42")); + + git_config_free(global); + git_config_free(config); + git_repository_free(repo); +} + +void test_repo_config__open_missing_global_with_separators(void) +{ + git_repository *repo; + git_config *config, *global; + + cl_git_pass(git_buf_printf(&path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy")); + + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); + + git_buf_free(&path); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL)); + + cl_git_pass(git_config_set_string(global, "test.set", "42")); + + git_config_free(global); + git_config_free(config); + git_repository_free(repo); +} diff --git a/tests-clar/repo/open.c b/tests-clar/repo/open.c index 6b5253797..840858586 100644 --- a/tests-clar/repo/open.c +++ b/tests-clar/repo/open.c @@ -309,7 +309,7 @@ void test_repo_open__no_config(void) cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); cl_git_pass(git_repository_config(&config, repo)); - cl_git_fail(git_config_set_string(config, "test.set", "42")); + cl_git_pass(git_config_set_string(config, "test.set", "42")); git_config_free(config); git_repository_free(repo); From 5d8318875fc7234d00140ce1f18b67bc4703e5f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 7 May 2013 00:10:02 +0200 Subject: [PATCH 193/384] config: convenience function to open global/xdg The rules for which one to open is a bit silly, so let's make it easier for our users. --- include/git2/config.h | 15 +++++++++ src/config.c | 8 +++++ tests-clar/config/global.c | 67 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 tests-clar/config/global.c diff --git a/include/git2/config.h b/include/git2/config.h index 5a2f956fd..724788ae0 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -194,6 +194,21 @@ GIT_EXTERN(int) git_config_open_level( const git_config *parent, unsigned int level); +/** + * Open the global/XDG configuration file according to git's rules + * + * Git allows you to store your global configuration at + * `$HOME/.config` or `$XDG_CONFIG_HOME/git/config`. For backwards + * compatability, the XDG file shouldn't be used unless the use has + * created it explicitly. With this function you'll open the correct + * one to write to. + * + * @param out pointer in which to store the config object + * @param config the config object in which to look + */ +GIT_EXTERN(int) git_config_open_global(git_config **out, git_config *config); + + /** * Reload changed config files * diff --git a/src/config.c b/src/config.c index 99aa00f50..bd629f7c4 100644 --- a/src/config.c +++ b/src/config.c @@ -227,6 +227,14 @@ static int git_config__add_internal( return 0; } +int git_config_open_global(git_config **cfg_out, git_config *cfg) +{ + if (!git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_XDG)) + return 0; + + return git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_GLOBAL); +} + int git_config_open_level( git_config **cfg_out, const git_config *cfg_parent, diff --git a/tests-clar/config/global.c b/tests-clar/config/global.c new file mode 100644 index 000000000..2ecdf97d8 --- /dev/null +++ b/tests-clar/config/global.c @@ -0,0 +1,67 @@ +#include "clar_libgit2.h" +#include "buffer.h" +#include "fileops.h" + +void test_config_global__initialize(void) +{ + git_buf path = GIT_BUF_INIT; + + cl_must_pass(p_mkdir("home", 0777)); + cl_git_pass(git_path_prettify(&path, "home", NULL)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + + cl_must_pass(p_mkdir("xdg", 0777)); + cl_git_pass(git_path_prettify(&path, "xdg", NULL)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, NULL)); + + git_buf_free(&path); +} + +void test_config_global__cleanup(void) +{ + cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_config_global__open_global(void) +{ + git_config *cfg, *global, *selected, *dummy; + + cl_git_pass(git_config_open_default(&cfg)); + cl_git_pass(git_config_open_level(&global, cfg, GIT_CONFIG_LEVEL_GLOBAL)); + cl_git_fail(git_config_open_level(&dummy, cfg, GIT_CONFIG_LEVEL_XDG)); + cl_git_pass(git_config_open_global(&selected, cfg)); + + git_config_free(selected); + git_config_free(global); + git_config_free(cfg); +} + +void test_config_global__open_xdg(void) +{ + git_config *cfg, *xdg, *selected; + const char *val, *str = "teststring"; + const char *key = "this.variable"; + + p_setenv("XDG_CONFIG_HOME", "xdg", 1); + + cl_must_pass(p_mkdir("xdg/git/", 0777)); + cl_git_mkfile("xdg/git/config", ""); + + cl_git_pass(git_config_open_default(&cfg)); + cl_git_pass(git_config_open_level(&xdg, cfg, GIT_CONFIG_LEVEL_XDG)); + cl_git_pass(git_config_open_global(&selected, cfg)); + + cl_git_pass(git_config_set_string(xdg, key, str)); + cl_git_pass(git_config_get_string(&val, selected, key)); + cl_assert_equal_s(str, val); + + git_config_free(selected); + git_config_free(xdg); + git_config_free(cfg); +} From 98d633cccf0a214b83ebfe9c7cb8ca449a1f0637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 7 May 2013 23:28:21 +0200 Subject: [PATCH 194/384] Expose git_reference_dwim Extract this function out of the rev-parse code to be able to DWIM a reference instead of its target. --- include/git2/refs.h | 13 +++++++++ src/refs.c | 61 +++++++++++++++++++++++++++++++++++++++ src/revparse.c | 69 +++------------------------------------------ 3 files changed, 78 insertions(+), 65 deletions(-) diff --git a/include/git2/refs.h b/include/git2/refs.h index e1d425352..e78873408 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -54,6 +54,19 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, GIT_EXTERN(int) git_reference_name_to_id( git_oid *out, git_repository *repo, const char *name); +/** + * Lookup a reference by DWIMing its short name + * + * Apply the git precendence rules to the given shorthand to determine + * which reference the user is refering to. + * + * @param out pointer in which to store the reference + * @param repo the repository in which to look + * @param shrothand the short name for the reference + * @return 0 or an error code + */ +GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, const char *shorthand); + /** * Create a new symbolic reference. * diff --git a/src/refs.c b/src/refs.c index 8bba3941e..684c54cd1 100644 --- a/src/refs.c +++ b/src/refs.c @@ -290,6 +290,67 @@ int git_reference_lookup_resolved( return 0; } +int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname) +{ + int error = 0, i; + bool fallbackmode = true, foundvalid = false; + git_reference *ref; + git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT; + + static const char* formatters[] = { + "%s", + GIT_REFS_DIR "%s", + GIT_REFS_TAGS_DIR "%s", + GIT_REFS_HEADS_DIR "%s", + GIT_REFS_REMOTES_DIR "%s", + GIT_REFS_REMOTES_DIR "%s/" GIT_HEAD_FILE, + NULL + }; + + if (*refname) + git_buf_puts(&name, refname); + else { + git_buf_puts(&name, GIT_HEAD_FILE); + fallbackmode = false; + } + + for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) { + + git_buf_clear(&refnamebuf); + + if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0) + goto cleanup; + + if (!git_reference_is_valid_name(git_buf_cstr(&refnamebuf))) { + error = GIT_EINVALIDSPEC; + continue; + } + foundvalid = true; + + error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1); + + if (!error) { + *out = ref; + error = 0; + goto cleanup; + } + + if (error != GIT_ENOTFOUND) + goto cleanup; + } + +cleanup: + if (error && !foundvalid) { + /* never found a valid reference name */ + giterr_set(GITERR_REFERENCE, + "Could not use '%s' as valid reference name", git_buf_cstr(&name)); + } + + git_buf_free(&name); + git_buf_free(&refnamebuf); + return error; +} + /** * Getters */ diff --git a/src/revparse.c b/src/revparse.c index e8cc32aff..05231e3fc 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -14,67 +14,6 @@ #include "git2.h" -static int disambiguate_refname(git_reference **out, git_repository *repo, const char *refname) -{ - int error = 0, i; - bool fallbackmode = true, foundvalid = false; - git_reference *ref; - git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT; - - static const char* formatters[] = { - "%s", - GIT_REFS_DIR "%s", - GIT_REFS_TAGS_DIR "%s", - GIT_REFS_HEADS_DIR "%s", - GIT_REFS_REMOTES_DIR "%s", - GIT_REFS_REMOTES_DIR "%s/" GIT_HEAD_FILE, - NULL - }; - - if (*refname) - git_buf_puts(&name, refname); - else { - git_buf_puts(&name, GIT_HEAD_FILE); - fallbackmode = false; - } - - for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) { - - git_buf_clear(&refnamebuf); - - if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0) - goto cleanup; - - if (!git_reference_is_valid_name(git_buf_cstr(&refnamebuf))) { - error = GIT_EINVALIDSPEC; - continue; - } - foundvalid = true; - - error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1); - - if (!error) { - *out = ref; - error = 0; - goto cleanup; - } - - if (error != GIT_ENOTFOUND) - goto cleanup; - } - -cleanup: - if (error && !foundvalid) { - /* never found a valid reference name */ - giterr_set(GITERR_REFERENCE, - "Could not use '%s' as valid reference name", git_buf_cstr(&name)); - } - - git_buf_free(&name); - git_buf_free(&refnamebuf); - return error; -} - static int maybe_sha_or_abbrev(git_object** out, git_repository *repo, const char *spec, size_t speclen) { git_oid oid; @@ -157,7 +96,7 @@ static int revparse_lookup_object(git_object **out, git_repository *repo, const if (error < 0 && error != GIT_ENOTFOUND) return error; - error = disambiguate_refname(&ref, repo, spec); + error = git_reference_dwim(&ref, repo, spec); if (!error) { error = git_object_lookup(out, repo, git_reference_target(ref), GIT_OBJ_ANY); git_reference_free(ref); @@ -242,7 +181,7 @@ static int retrieve_previously_checked_out_branch_or_revision(git_object **out, git_buf_put(&buf, msg+regexmatches[1].rm_so, regexmatches[1].rm_eo - regexmatches[1].rm_so); - if ((error = disambiguate_refname(base_ref, repo, git_buf_cstr(&buf))) == 0) + if ((error = git_reference_dwim(base_ref, repo, git_buf_cstr(&buf))) == 0) goto cleanup; if (error < 0 && error != GIT_ENOTFOUND) @@ -323,7 +262,7 @@ static int retrieve_revobject_from_reflog(git_object **out, git_reference **base int error = -1; if (*base_ref == NULL) { - if ((error = disambiguate_refname(&ref, repo, identifier)) < 0) + if ((error = git_reference_dwim(&ref, repo, identifier)) < 0) return error; } else { ref = *base_ref; @@ -351,7 +290,7 @@ static int retrieve_remote_tracking_reference(git_reference **base_ref, const ch int error = -1; if (*base_ref == NULL) { - if ((error = disambiguate_refname(&ref, repo, identifier)) < 0) + if ((error = git_reference_dwim(&ref, repo, identifier)) < 0) return error; } else { ref = *base_ref; From 4f2eb2b7f4cf6b2b6594887edd8948cb149c8052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 8 May 2013 02:28:47 +0200 Subject: [PATCH 195/384] Introduce git_reference_shorthand Generate a shorthand name out of the full refname. --- include/git2/refs.h | 15 +++++++++++++++ src/refs.c | 17 +++++++++++++++++ tests-clar/refs/shorthand.c | 27 +++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 tests-clar/refs/shorthand.c diff --git a/include/git2/refs.h b/include/git2/refs.h index e1d425352..bb3d21b06 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -493,6 +493,21 @@ GIT_EXTERN(int) git_reference_peel( */ GIT_EXTERN(int) git_reference_is_valid_name(const char *refname); +/** + * Get the reference's short name + * + * This will transform the reference name into a name "human-readable" + * version. If no shortname is appropriate, it will return the full + * name. + * + * The memory is owned by the reference and must not be freed. + * + * @param ref a reference + * @return the human-readable version of the name + */ +GIT_EXTERN(const char *) git_reference_shorthand(git_reference *ref); + + /** @} */ GIT_END_DECL #endif diff --git a/src/refs.c b/src/refs.c index 8bba3941e..5ed2883d9 100644 --- a/src/refs.c +++ b/src/refs.c @@ -1016,3 +1016,20 @@ int git_reference_is_valid_name( refname, GIT_REF_FORMAT_ALLOW_ONELEVEL); } + +const char *git_reference_shorthand(git_reference *ref) +{ + const char *name = ref->name; + + if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR)) + return name + strlen(GIT_REFS_HEADS_DIR); + else if (!git__prefixcmp(name, GIT_REFS_TAGS_DIR)) + return name + strlen(GIT_REFS_TAGS_DIR); + else if (!git__prefixcmp(name, GIT_REFS_REMOTES_DIR)) + return name + strlen(GIT_REFS_REMOTES_DIR); + else if (!git__prefixcmp(name, GIT_REFS_DIR)) + return name + strlen(GIT_REFS_DIR); + + /* No shorthands are avaiable, so just return the name */ + return name; +} diff --git a/tests-clar/refs/shorthand.c b/tests-clar/refs/shorthand.c new file mode 100644 index 000000000..f995d26ca --- /dev/null +++ b/tests-clar/refs/shorthand.c @@ -0,0 +1,27 @@ +#include "clar_libgit2.h" + +#include "repository.h" + +void assert_shorthand(git_repository *repo, const char *refname, const char *shorthand) +{ + git_reference *ref; + + cl_git_pass(git_reference_lookup(&ref, repo, refname)); + cl_assert_equal_s(git_reference_shorthand(ref), shorthand); + git_reference_free(ref); +} + +void test_refs_shorthand__0(void) +{ + git_repository *repo; + + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + + + assert_shorthand(repo, "refs/heads/master", "master"); + assert_shorthand(repo, "refs/tags/test", "test"); + assert_shorthand(repo, "refs/remotes/test/master", "test/master"); + assert_shorthand(repo, "refs/notes/fanout", "notes/fanout"); + + git_repository_free(repo); +} From eb05b114e1c8042225086393a39b712fe3f31e71 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 7 May 2013 22:45:01 -0700 Subject: [PATCH 196/384] Fix dumb type in time comparison --- src/diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index e0dff9c95..f466546bb 100644 --- a/src/diff.c +++ b/src/diff.c @@ -535,7 +535,7 @@ cleanup: static bool diff_time_eq( const git_index_time *a, const git_index_time *b, bool use_nanos) { - return a->seconds == a->seconds && + return a->seconds == b->seconds && (!use_nanos || a->nanoseconds == b->nanoseconds); } From 3d1c9f612d78c6c235a3f38af226300fb6dd7dd6 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 9 May 2013 06:45:06 -0700 Subject: [PATCH 197/384] Fix git_repository_message docs This clarifies the docs for git_repository_message and also adds to the tests to explicitly check NUL termination of data when the output buffer is smaller than the message size. There is a minor behavior change so that a non-NULL output buffer will always be NUL terminated (at length zero) if an error occurs. --- include/git2/repository.h | 13 +++++++++++-- src/repository.c | 3 +++ tests-clar/repo/message.c | 7 ++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/include/git2/repository.h b/include/git2/repository.h index cd238e17c..e0464c63f 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -460,10 +460,19 @@ GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo); * Use this function to get the contents of this file. Don't forget to * remove the file after you create the commit. * + * If the repository message exists and there are no errors reading it, this + * returns the bytes needed to store the message in memory (i.e. message + * file size plus one terminating NUL byte). That value is returned even if + * `out` is NULL or `len` is shorter than the necessary size. + * + * The `out` buffer will *always* be NUL terminated, even if truncation + * occurs. + * * @param out Buffer to write data into or NULL to just read required size - * @param len Length of buffer in bytes + * @param len Length of `out` buffer in bytes * @param repo Repository to read prepared message from - * @return Bytes written to buffer, GIT_ENOTFOUND if no message, or -1 on error + * @return GIT_ENOUTFOUND if no message exists, other value < 0 for other + * errors, or total bytes in message (may be > `len`) on success */ GIT_EXTERN(int) git_repository_message(char *out, size_t len, git_repository *repo); diff --git a/src/repository.c b/src/repository.c index e2cedc0f7..0b8fc395b 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1596,6 +1596,9 @@ int git_repository_message(char *buffer, size_t len, git_repository *repo) struct stat st; int error; + if (buffer != NULL) + *buffer = '\0'; + if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) return -1; diff --git a/tests-clar/repo/message.c b/tests-clar/repo/message.c index 59487d51b..629d40c12 100644 --- a/tests-clar/repo/message.c +++ b/tests-clar/repo/message.c @@ -35,13 +35,18 @@ void test_repo_message__message(void) len = git_repository_message(NULL, 0, _repo); cl_assert(len > 0); + _actual = git__malloc(len + 1); cl_assert(_actual != NULL); + /* Test non truncation */ cl_assert(git_repository_message(_actual, len, _repo) > 0); - _actual[len] = '\0'; cl_assert_equal_s(expected, _actual); + /* Test truncation and that trailing NUL is inserted */ + cl_assert(git_repository_message(_actual, 6, _repo) > 0); + cl_assert_equal_s("Test\n", _actual); + cl_git_pass(p_unlink(git_buf_cstr(&_path))); cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(NULL, 0, _repo)); } From 617bb17556ae97a3e21b502c7fed1acfe9e1370a Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 9 May 2013 17:44:44 +0200 Subject: [PATCH 198/384] calloc refs instead of malloc'ing them --- src/refdb_fs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index c0a32bae7..bb2cd0a73 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -106,7 +106,7 @@ static int packed_parse_oid( refname_len = refname_end - refname_begin; - ref = git__malloc(sizeof(struct packref) + refname_len + 1); + ref = git__calloc(1, sizeof(struct packref) + refname_len + 1); GITERR_CHECK_ALLOC(ref); memcpy(ref->name, refname_begin, refname_len); @@ -316,7 +316,7 @@ static int loose_lookup_to_packfile( git_buf_rtrim(&ref_file); name_len = strlen(name); - ref = git__malloc(sizeof(struct packref) + name_len + 1); + ref = git__calloc(1, sizeof(struct packref) + name_len + 1); GITERR_CHECK_ALLOC(ref); memcpy(ref->name, name, name_len); From 05f581311b1a3d8003823a57e87482847827ae7d Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Thu, 9 May 2013 17:36:27 -0400 Subject: [PATCH 199/384] Renaming --- src/transports/ssh.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 167df03db..31625cb3d 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -404,7 +404,7 @@ on_error: return -1; } -static int _git_uploadpack_ls( +static int ssh_uploadpack_ls( ssh_subtransport *t, const char *url, git_smart_subtransport_stream **stream) @@ -415,7 +415,7 @@ static int _git_uploadpack_ls( return 0; } -static int _git_uploadpack( +static int ssh_uploadpack( ssh_subtransport *t, const char *url, git_smart_subtransport_stream **stream) @@ -431,7 +431,7 @@ static int _git_uploadpack( return -1; } -static int _git_receivepack_ls( +static int ssh_receivepack_ls( ssh_subtransport *t, const char *url, git_smart_subtransport_stream **stream) @@ -442,7 +442,7 @@ static int _git_receivepack_ls( return 0; } -static int _git_receivepack( +static int ssh_receivepack( ssh_subtransport *t, const char *url, git_smart_subtransport_stream **stream) @@ -468,16 +468,16 @@ static int _ssh_action( switch (action) { case GIT_SERVICE_UPLOADPACK_LS: - return _git_uploadpack_ls(t, url, stream); + return ssh_uploadpack_ls(t, url, stream); case GIT_SERVICE_UPLOADPACK: - return _git_uploadpack(t, url, stream); + return ssh_uploadpack(t, url, stream); case GIT_SERVICE_RECEIVEPACK_LS: - return _git_receivepack_ls(t, url, stream); + return ssh_receivepack_ls(t, url, stream); case GIT_SERVICE_RECEIVEPACK: - return _git_receivepack(t, url, stream); + return ssh_receivepack(t, url, stream); } *stream = NULL; From ce6d50b99420a4e0994cf9be285a92eb8c4bac0e Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Thu, 9 May 2013 17:37:42 -0400 Subject: [PATCH 200/384] Changed to use libssh2_channel_exec --- src/transports/ssh.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 31625cb3d..f11b5fb67 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -79,12 +79,9 @@ static int send_command(ssh_stream *s) if (error < 0) goto cleanup; - error = libssh2_channel_process_startup( - s->channel, - "exec", - (uint32_t)sizeof("exec") - 1, - request.ptr, - request.size + error = libssh2_channel_exec( + s->channel, + request.ptr ); if (0 != error) From b4d81a00bf97260eff52345047a14c93de6fddc0 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Thu, 9 May 2013 17:40:21 -0400 Subject: [PATCH 201/384] Moved libssh2 sign callback into typedef --- include/git2/transport.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/git2/transport.h b/include/git2/transport.h index b3d43ebf6..48a35b549 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -52,6 +52,8 @@ typedef struct git_cred_userpass_plaintext { } git_cred_userpass_plaintext; #ifdef GIT_SSH +typedef LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*git_cred_sign_callback)); + /* A ssh key file and passphrase */ typedef struct git_cred_ssh_keyfile_passphrase { git_cred parent; @@ -116,7 +118,7 @@ GIT_EXTERN(int) git_cred_ssh_publickey_new( git_cred **out, const char *publickey, size_t publickey_len, - LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)), + git_cred_sign_callback, void *sign_data); #endif From ae59321fb86d080d0fd17e8c9a57c855914bcd32 Mon Sep 17 00:00:00 2001 From: Michael Schubert Date: Fri, 10 May 2013 14:31:58 +0200 Subject: [PATCH 202/384] clone: fix -Wmaybe-uninitialized warning --- src/clone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clone.c b/src/clone.c index 38c0d409e..2ff119ee3 100644 --- a/src/clone.c +++ b/src/clone.c @@ -355,7 +355,7 @@ static int setup_remotes_and_fetch( const git_clone_options *options) { int retcode = GIT_ERROR; - git_remote *origin; + git_remote *origin = NULL; /* Construct an origin remote */ if ((retcode = create_and_configure_origin(&origin, repo, url, options)) < 0) From 89ea0e51814bc3161fd8fc62045cdabbedf41a2c Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Fri, 10 May 2013 09:40:14 -0400 Subject: [PATCH 203/384] Removed ifdef --- include/git2/transport.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/git2/transport.h b/include/git2/transport.h index 48a35b549..81bb3abe1 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -31,10 +31,8 @@ GIT_BEGIN_DECL typedef enum { /* git_cred_userpass_plaintext */ GIT_CREDTYPE_USERPASS_PLAINTEXT = 1, -#ifdef GIT_SSH GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE = 2, GIT_CREDTYPE_SSH_PUBLICKEY = 3, -#endif } git_credtype_t; /* The base structure for all credential types */ From 1f9e41ee865ac3a9c60d39c7db212478c04bf86d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 10 May 2013 07:50:53 -0700 Subject: [PATCH 204/384] Improve ignore handling in git_status_file The git_status_file API was doing a hack to deal with files that are inside ignored directories. The status scan was not reporting any file in this case, so git_status_file would attempt a final "stat()" call, and return IGNORED if the file actually existed. On case-insensitive filesystems where core.ignorecase is set incorrectly, this magic check can "succeed" and report a file as ignored when it should actually return ENOTFOUND. Now that we have the GIT_STATUS_OPT_RECURSE_IGNORED_DIRS, we can use that flag to make sure that git_status_file() will look into ignored directories and eliminate the hack completely, so we give the correct error. --- src/status.c | 20 ++++---------------- tests-clar/clar_libgit2.c | 12 ++++++++++++ tests-clar/clar_libgit2.h | 1 + tests-clar/status/worktree.c | 23 +++++++++++++++++++++++ 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/status.c b/src/status.c index 73472ab14..89f3eedb5 100644 --- a/src/status.c +++ b/src/status.c @@ -266,6 +266,7 @@ int git_status_file( opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED | + GIT_STATUS_OPT_RECURSE_IGNORED_DIRS | GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS | GIT_STATUS_OPT_INCLUDE_UNMODIFIED; @@ -281,22 +282,9 @@ int git_status_file( } if (!error && !sfi.count) { - git_buf full = GIT_BUF_INIT; - - /* if the file actually exists and we still did not get a callback - * for it, then it must be contained inside an ignored directory, so - * mark it as such instead of generating an error. - */ - if (!git_buf_joinpath(&full, git_repository_workdir(repo), path) && - git_path_exists(full.ptr)) - sfi.status = GIT_STATUS_IGNORED; - else { - giterr_set(GITERR_INVALID, - "Attempt to get status of nonexistent file '%s'", path); - error = GIT_ENOTFOUND; - } - - git_buf_free(&full); + giterr_set(GITERR_INVALID, + "Attempt to get status of nonexistent file '%s'", path); + error = GIT_ENOTFOUND; } *status_flags = sfi.status; diff --git a/tests-clar/clar_libgit2.c b/tests-clar/clar_libgit2.c index 68d17162b..de0e41bf7 100644 --- a/tests-clar/clar_libgit2.c +++ b/tests-clar/clar_libgit2.c @@ -190,6 +190,18 @@ git_repository *cl_git_sandbox_init(const char *sandbox) return _cl_repo; } +git_repository *cl_git_sandbox_reopen(void) +{ + if (_cl_repo) { + git_repository_free(_cl_repo); + _cl_repo = NULL; + + cl_git_pass(git_repository_open(&_cl_repo, _cl_sandbox)); + } + + return _cl_repo; +} + void cl_git_sandbox_cleanup(void) { if (_cl_repo) { diff --git a/tests-clar/clar_libgit2.h b/tests-clar/clar_libgit2.h index 93909d8a5..3fcf45a37 100644 --- a/tests-clar/clar_libgit2.h +++ b/tests-clar/clar_libgit2.h @@ -60,6 +60,7 @@ int cl_rename(const char *source, const char *dest); git_repository *cl_git_sandbox_init(const char *sandbox); void cl_git_sandbox_cleanup(void); +git_repository *cl_git_sandbox_reopen(void); /* Local-repo url helpers */ const char* cl_git_fixture_url(const char *fixturename); diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index 0138b1712..062a09aeb 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -672,3 +672,26 @@ void test_status_worktree__file_status_honors_core_ignorecase_false(void) { assert_ignore_case(false, GIT_STATUS_WT_DELETED, GIT_STATUS_WT_NEW); } + +void test_status_worktree__file_status_honors_case_ignorecase_regarding_untracked_files(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + unsigned int status; + git_index *index; + + cl_repo_set_bool(repo, "core.ignorecase", false); + + repo = cl_git_sandbox_reopen(); + + /* Actually returns GIT_STATUS_IGNORED on Windows */ + cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND); + + cl_git_pass(git_repository_index(&index, repo)); + + cl_git_pass(git_index_add_bypath(index, "new_file")); + cl_git_pass(git_index_write(index)); + git_index_free(index); + + /* Actually returns GIT_STATUS_IGNORED on Windows */ + cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND); +} From e9ba61f399594b16b8168f87264b7794eebc6b3a Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 10 May 2013 09:32:42 -0700 Subject: [PATCH 205/384] Fix diff output for renames and copies If you use rename detection, the renamed and copied files would not show any text diffs because the function that decides if data should be loaded didn't know which sides of the diff to load for those cases. This adds a test that looks at the patch generated for diff entries that are COPIED or RENAMED. --- src/diff_output.c | 2 ++ tests-clar/diff/rename.c | 55 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/diff_output.c b/src/diff_output.c index 2214ae1b5..07fcf47a7 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -593,6 +593,8 @@ static int diff_patch_load( delta->new_file.flags |= GIT_DIFF_FLAG__NO_DATA; break; case GIT_DELTA_MODIFIED: + case GIT_DELTA_COPIED: + case GIT_DELTA_RENAMED: break; case GIT_DELTA_UNTRACKED: delta->old_file.flags |= GIT_DIFF_FLAG__NO_DATA; diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 5a8af93bb..8134cb6b7 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -364,7 +364,7 @@ void test_diff_rename__handles_small_files(void) cl_git_pass(git_repository_index(&index, g_repo)); tree = resolve_commit_oid_to_tree(g_repo, tree_sha); - + cl_git_rewritefile("renames/songof7cities.txt", "single line\n"); cl_git_pass(git_index_add_bypath(index, "songof7cities.txt")); @@ -391,3 +391,56 @@ void test_diff_rename__working_directory_changes(void) /* and with / without CRLF changes */ } + +void test_diff_rename__patch(void) +{ + const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a"; + const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084"; + git_tree *old_tree, *new_tree; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + git_diff_patch *patch; + const git_diff_delta *delta; + char *text; + const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n"; + + old_tree = resolve_commit_oid_to_tree(g_repo, sha0); + new_tree = resolve_commit_oid_to_tree(g_repo, sha1); + + diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, old_tree, new_tree, &diffopts)); + + opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + /* == Changes ===================================================== + * sixserving.txt -> ikeepsix.txt (copy, add title, >80% match) + * sevencities.txt (no change) + * sixserving.txt -> sixserving.txt (indentation change) + * songofseven.txt -> songofseven.txt (major rewrite, <20% match - split) + */ + + cl_assert_equal_i(4, (int)git_diff_num_deltas(diff)); + + cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0)); + cl_assert_equal_i(GIT_DELTA_COPIED, (int)delta->status); + + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected, text); + git__free(text); + + git_diff_patch_free(patch); + + cl_git_pass(git_diff_get_patch(NULL, &delta, diff, 1)); + cl_assert_equal_i(GIT_DELTA_UNMODIFIED, (int)delta->status); + + cl_git_pass(git_diff_get_patch(NULL, &delta, diff, 2)); + cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status); + + cl_git_pass(git_diff_get_patch(NULL, &delta, diff, 3)); + cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status); + + git_diff_list_free(diff); +} From 249888948f73667695fcde8088249db2e1a217ba Mon Sep 17 00:00:00 2001 From: nulltoken Date: Fri, 10 May 2013 12:02:17 +0200 Subject: [PATCH 206/384] Fix some memory leaks --- tests-clar/clone/nonetwork.c | 2 ++ tests-clar/refs/branches/upstream.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c index 8b17fd998..8aae1fb52 100644 --- a/tests-clar/clone/nonetwork.c +++ b/tests-clar/clone/nonetwork.c @@ -185,6 +185,7 @@ void test_clone_nonetwork__custom_autotag(void) cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_NONE, origin->download_tags); git_strarray_free(&tags); + git_remote_free(origin); } void test_clone_nonetwork__custom_autotag_tags_all(void) @@ -199,6 +200,7 @@ void test_clone_nonetwork__custom_autotag_tags_all(void) cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_ALL, origin->download_tags); git_strarray_free(&tags); + git_remote_free(origin); } void test_clone_nonetwork__cope_with_already_existing_directory(void) diff --git a/tests-clar/refs/branches/upstream.c b/tests-clar/refs/branches/upstream.c index 648acb44d..69e55a0c5 100644 --- a/tests-clar/refs/branches/upstream.c +++ b/tests-clar/refs/branches/upstream.c @@ -113,11 +113,12 @@ void test_refs_branches_upstream__set_unset_upstream(void) cl_git_pass(git_config_get_string(&value, config, "branch.test.merge")); cl_assert_equal_s(value, "refs/heads/master"); + git_reference_free(branch); + /* local */ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test")); cl_git_pass(git_branch_set_upstream(branch, "master")); - cl_git_pass(git_repository_config(&config, repository)); cl_git_pass(git_config_get_string(&value, config, "branch.test.remote")); cl_assert_equal_s(value, "."); cl_git_pass(git_config_get_string(&value, config, "branch.test.merge")); From 4def7035cac133607256fd91352ce54ac4548a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 2 Mar 2013 19:31:03 +0100 Subject: [PATCH 207/384] refs: introduce an iterator This allows us to get a list of reference names in a loop instead of callbacks. --- include/git2/refs.h | 25 ++++++ include/git2/sys/refdb_backend.h | 35 +++++++++ include/git2/types.h | 4 + src/refdb.c | 24 ++++++ src/refdb.h | 4 + src/refdb_fs.c | 126 +++++++++++++++++++++++++++++++ src/refs.c | 20 +++++ tests-clar/refs/iterator.c | 76 +++++++++++++++++++ 8 files changed, 314 insertions(+) create mode 100644 tests-clar/refs/iterator.c diff --git a/include/git2/refs.h b/include/git2/refs.h index e1d425352..48ecc96e0 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -346,6 +346,31 @@ GIT_EXTERN(void) git_reference_free(git_reference *ref); */ GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2); +/** + * Create an iterator for the repo's references + * + * @param out pointer in which to store the iterator + * @param repo the repository + * @return 0 or an error code + */ +GIT_EXTERN(int) git_reference_iterator_new(git_reference_iterator **out, git_repository *repo); + +/** + * Get the next reference name + * + * @param out pointer in which to store the string + * @param iter the iterator + * @param 0, GIT_ITEROVER if there are no more; or an error code + */ +GIT_EXTERN(int) git_reference_next(const char **out, git_reference_iterator *iter); + +/** + * Free the iterator and its associated resources + * + * @param iter the iterator to free + */ +GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter); + /** * Perform a callback on each reference in the repository whose name * matches the given pattern. diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index d5f599fec..4c882b5cd 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -20,6 +20,22 @@ */ GIT_BEGIN_DECL + +/** + * Every backend's iterator must have a pointer to itself as the first + * element, so the API can talk to it. You'd define your iterator as + * + * struct my_iterator { + * git_reference_iterator parent; + * ... + * } + * + * and assing `iter->parent.backend` to your `git_refdb_backend`. + */ +struct git_reference_iterator { + git_refdb_backend *backend; +}; + /** An instance for a custom backend */ struct git_refdb_backend { unsigned int version; @@ -66,6 +82,25 @@ struct git_refdb_backend { void *payload); /** + * Allocate an iterator object for the backend + */ + int (*iterator)( + git_reference_iterator **iter, + struct git_refdb_backend *backend); + + /** + * Return the current value and advance the iterator. + */ + int (*next)( + const char **name, + git_reference_iterator *iter); + + /** + * Free the iterator + */ + void (*iterator_free)( + git_reference_iterator *iter); + /* * Writes the given reference to the refdb. A refdb implementation * must provide this function. */ diff --git a/include/git2/types.h b/include/git2/types.h index aca9ed927..43751d3b0 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -165,6 +165,10 @@ typedef struct git_signature { /** In-memory representation of a reference. */ typedef struct git_reference git_reference; +/** Iterator for references */ +typedef struct git_reference_iterator git_reference_iterator; + + /** Basic type of any Git reference. */ typedef enum { GIT_REF_INVALID = 0, /** Invalid reference */ diff --git a/src/refdb.c b/src/refdb.c index 33a1934d1..73882e807 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -124,6 +124,30 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name) return error; } +int git_refdb_iterator(git_reference_iterator **out, git_refdb *db) +{ + git_reference_iterator *iter; + + /* FIXME: don't segfault when there is no backends */ + if (db->backend->iterator(&iter, db->backend) < 0) { + git__free(iter); + return -1; + } + + *out = iter; + return 0; +} + +int git_refdb_next(const char **out, git_reference_iterator *iter) +{ + return iter->backend->next(out, iter); +} + +void git_refdb_iterator_free(git_reference_iterator *iter) +{ + iter->backend->iterator_free(iter); +} + int git_refdb_foreach( git_refdb *db, unsigned int list_flags, diff --git a/src/refdb.h b/src/refdb.h index 047113ac8..a243f627c 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -39,6 +39,10 @@ int git_refdb_foreach_glob( git_reference_foreach_cb callback, void *payload); +int git_refdb_iterator(git_reference_iterator **out, git_refdb *db); +int git_refdb_next(const char **out, git_reference_iterator *iter); +void git_refdb_iterator_free(git_reference_iterator *iter); + int git_refdb_write(git_refdb *refdb, const git_reference *ref); int git_refdb_delete(git_refdb *refdb, const git_reference *ref); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index c0a32bae7..5c8e59f52 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -13,6 +13,7 @@ #include "reflog.h" #include "refdb.h" #include "refdb_fs.h" +#include "iterator.h" #include #include @@ -652,6 +653,128 @@ static int refdb_fs_backend__foreach( return data.callback_error ? GIT_EUSER : result; } +typedef struct { + git_reference_iterator parent; + unsigned int loose; + /* packed */ + git_strmap *h; + khiter_t k; + /* loose */ + git_iterator *fsiter; + git_buf buf; +} refdb_fs_iter; + +static int refdb_fs_backend__iterator(git_reference_iterator **out, git_refdb_backend *_backend) +{ + refdb_fs_iter *iter; + refdb_fs_backend *backend; + + assert(_backend); + backend = (refdb_fs_backend *)_backend; + + if (packed_load(backend) < 0) + return -1; + + iter = git__calloc(1, sizeof(refdb_fs_iter)); + GITERR_CHECK_ALLOC(iter); + + iter->parent.backend = _backend; + iter->h = backend->refcache.packfile; + iter->k = kh_begin(backend->refcache.packfile); + + *out = (git_reference_iterator *)iter; + + return 0; +} + +static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter) +{ + refdb_fs_iter *iter = (refdb_fs_iter *) _iter; + + git_buf_free(&iter->buf); + git_iterator_free(iter->fsiter); + git__free(iter); +} + +static int iter_packed(const char **out, refdb_fs_iter *iter) +{ + /* Move forward to the next entry */ + while (!kh_exist(iter->h, iter->k)) { + iter->k++; + if (iter->k == kh_end(iter->h)) + return GIT_ITEROVER; + } + + *out = kh_key(iter->h, iter->k); + iter->k++; + + return 0; +} + +static int iter_loose(const char **out, refdb_fs_iter *iter) +{ + const git_index_entry *entry; + int retry; + git_strmap *packfile_refs; + refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend; + + packfile_refs = backend->refcache.packfile; + + do { + khiter_t pos; + if (git_iterator_current(&entry, iter->fsiter) < 0) + return -1; + + git_buf_clear(&iter->buf); + if (!entry) + return GIT_ITEROVER; + + if (git_buf_printf(&iter->buf, "refs/%s", entry->path) < 0) + return -1; + + git_iterator_advance(NULL, iter->fsiter); + + /* Skip this one if we already listed it in packed */ + pos = git_strmap_lookup_index(packfile_refs, git_buf_cstr(&iter->buf)); + retry = 0; + if (git_strmap_valid_index(packfile_refs, pos) || + !git_reference_is_valid_name(git_buf_cstr(&iter->buf))) + retry = 1; + + *out = git_buf_cstr(&iter->buf); + } while (retry); + + return 0; +} + +static int iter_loose_setup(refdb_fs_iter *iter) +{ + refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend; + + git_buf_clear(&iter->buf); + if (git_buf_printf(&iter->buf, "%s/refs", backend->path) < 0) + return -1; + + return git_iterator_for_filesystem(&iter->fsiter, git_buf_cstr(&iter->buf), 0, NULL, NULL); +} + +static int refdb_fs_backend__next(const char **out, git_reference_iterator *_iter) +{ + refdb_fs_iter *iter = (refdb_fs_iter *)_iter; + + /* First round of checks to make sure where we are */ + if (!iter->loose && iter->k == kh_end(iter->h)) { + if (iter_loose_setup(iter) < 0) + return -1; + iter->loose = 1; + } + + if (!iter->loose) + return iter_packed(out, iter); + else + return iter_loose(out, iter); +} + static int loose_write(refdb_fs_backend *backend, const git_reference *ref) { git_filebuf file = GIT_FILEBUF_INIT; @@ -1082,6 +1205,9 @@ int git_refdb_backend_fs( backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; backend->parent.foreach = &refdb_fs_backend__foreach; + backend->parent.iterator = &refdb_fs_backend__iterator; + backend->parent.next = &refdb_fs_backend__next; + backend->parent.iterator_free = &refdb_fs_backend__iterator_free; backend->parent.write = &refdb_fs_backend__write; backend->parent.delete = &refdb_fs_backend__delete; backend->parent.compress = &refdb_fs_backend__compress; diff --git a/src/refs.c b/src/refs.c index b85a2e828..547bd570c 100644 --- a/src/refs.c +++ b/src/refs.c @@ -568,6 +568,26 @@ int git_reference_foreach( return git_refdb_foreach(refdb, list_flags, callback, payload); } +int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo) +{ + git_refdb *refdb; + + if (git_repository_refdb__weakptr(&refdb, repo) < 0) + return -1; + + return git_refdb_iterator(out, refdb); +} + +int git_reference_next(const char **out, git_reference_iterator *iter) +{ + return git_refdb_next(out, iter); +} + +void git_reference_iterator_free(git_reference_iterator *iter) +{ + git_refdb_iterator_free(iter); +} + static int cb__reflist_add(const char *ref, void *data) { return git_vector_insert((git_vector *)data, git__strdup(ref)); diff --git a/tests-clar/refs/iterator.c b/tests-clar/refs/iterator.c new file mode 100644 index 000000000..aef0453c8 --- /dev/null +++ b/tests-clar/refs/iterator.c @@ -0,0 +1,76 @@ +#include "clar_libgit2.h" +#include "refs.h" +#include "vector.h" + +static git_repository *repo; + +void test_refs_iterator__initialize(void) +{ + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); +} + +void test_refs_iterator__cleanup(void) +{ + git_repository_free(repo); +} + +static const char *refnames[] = { + "refs/heads/br2", + "refs/heads/cannot-fetch", + "refs/heads/chomped", + "refs/heads/haacked", + "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", +}; + +void test_refs_iterator__list(void) +{ + git_reference_iterator *iter; + git_vector output; + char *refname; + int error; + size_t i; + + cl_git_pass(git_vector_init(&output, 32, git__strcmp_cb)); + cl_git_pass(git_reference_iterator_new(&iter, repo)); + + do { + const char *name; + error = git_reference_next(&name, iter); + cl_assert(error == 0 || error == GIT_ITEROVER); + if (error != GIT_ITEROVER) { + char *dup = git__strdup(name); + cl_assert(dup != NULL); + cl_git_pass(git_vector_insert(&output, dup)); + } + } while (!error); + + cl_assert_equal_i(output.length, ARRAY_SIZE(refnames)); + + git_vector_sort(&output); + git_vector_foreach(&output, i, refname) { + cl_assert_equal_s(refname, refnames[i]); + } + + git_reference_iterator_free(iter); + + git_vector_foreach(&output, i, refname) { + git__free(refname); + } + git_vector_free(&output); +} From 69a3c766b6c6b9bbe2859bf8572c12b93a9d37c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 15:03:32 +0200 Subject: [PATCH 208/384] refdb_fs: don't crash when the repo doesn't have a path --- src/refdb_fs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 5c8e59f52..62965a66b 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -181,6 +181,9 @@ static int packed_load(refdb_fs_backend *backend) GITERR_CHECK_ALLOC(ref_cache->packfile); } + if (backend->path == NULL) + return 0; + result = reference_read(&packfile, &ref_cache->packfile_time, backend->path, GIT_PACKEDREFS_FILE, &updated); @@ -1150,6 +1153,10 @@ static int setup_namespace(git_buf *path, git_repository *repo) { char *parts, *start, *end; + /* Not all repositories have a path */ + if (repo->path_repository == NULL) + return 0; + /* Load the path to the repo first */ git_buf_puts(path, repo->path_repository); From 51fc5e895d7655611431a0bdb76b9fd8499a9e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 15:16:55 +0200 Subject: [PATCH 209/384] Make sure the ref iterator works in an repo without physical presence --- src/refdb.c | 6 +++++- tests-clar/refs/iterator.c | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/refdb.c b/src/refdb.c index 73882e807..269234bfa 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -128,7 +128,11 @@ int git_refdb_iterator(git_reference_iterator **out, git_refdb *db) { git_reference_iterator *iter; - /* FIXME: don't segfault when there is no backends */ + if (!db->backend || !db->backend->iterator) { + giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators"); + return -1; + } + if (db->backend->iterator(&iter, db->backend) < 0) { git__free(iter); return -1; diff --git a/tests-clar/refs/iterator.c b/tests-clar/refs/iterator.c index aef0453c8..d5555c657 100644 --- a/tests-clar/refs/iterator.c +++ b/tests-clar/refs/iterator.c @@ -74,3 +74,21 @@ void test_refs_iterator__list(void) } git_vector_free(&output); } + +void test_refs_iterator__empty(void) +{ + git_reference_iterator *iter; + git_odb *odb; + const char *name; + git_repository *empty; + + cl_git_pass(git_odb_new(&odb)); + cl_git_pass(git_repository_wrap_odb(&empty, odb)); + + cl_git_pass(git_reference_iterator_new(&iter, empty)); + cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&name, iter)); + + git_reference_iterator_free(iter); + git_odb_free(odb); + git_repository_free(empty); +} From fb592a96ebac46f0e176fbbe849cf9b52b9f4727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 15:54:57 +0200 Subject: [PATCH 210/384] Remove outdated test Selecting wether to list loose or packed references is not something we want to support anymore, so remove a test for this. --- tests-clar/refs/list.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests-clar/refs/list.c b/tests-clar/refs/list.c index 3948b2b7a..8a386fdfd 100644 --- a/tests-clar/refs/list.c +++ b/tests-clar/refs/list.c @@ -41,17 +41,6 @@ void test_refs_list__all(void) git_strarray_free(&ref_list); } -void test_refs_list__symbolic_only(void) -{ - // try to list only the symbolic references - git_strarray ref_list; - - cl_git_pass(git_reference_list(&ref_list, g_repo, GIT_REF_SYMBOLIC)); - cl_assert(ref_list.count == 0); /* no symrefs in the test repo */ - - git_strarray_free(&ref_list); -} - void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_extension(void) { git_strarray ref_list; From 932af0e9eb35e37b2cf993c0806d6ea46476cf39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 15:57:44 +0200 Subject: [PATCH 211/384] Add iterator support to the testdb backend --- tests-clar/refdb/testdb.c | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index 627254e44..eee60ac90 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -112,6 +112,49 @@ static int refdb_test_backend__lookup( return GIT_ENOTFOUND; } +typedef struct { + git_reference_iterator parent; + size_t i; +} refdb_test_iter; + +static int refdb_test_backend__iterator(git_reference_iterator **out, git_refdb_backend *_backend) +{ + refdb_test_iter *iter; + + GIT_UNUSED(_backend); + + iter = git__malloc(sizeof(refdb_test_iter)); + GITERR_CHECK_ALLOC(iter); + + iter->parent.backend = _backend; + iter->i = 0; + + *out = (git_reference_iterator *) iter; + + return 0; +} + +static int refdb_test_backend__next(const char **name, git_reference_iterator *_iter) +{ + refdb_test_entry *entry; + refdb_test_backend *backend = (refdb_test_backend *) _iter->backend; + refdb_test_iter *iter = (refdb_test_iter *) _iter; + + entry = git_vector_get(&backend->refs, iter->i); + if (!entry) + return GIT_ITEROVER; + + *name = entry->name; + iter->i++; + + return 0; +} + +static void refdb_test_backend__iterator_free(git_reference_iterator *iter) +{ + git__free(iter); +} + static int refdb_test_backend__foreach( git_refdb_backend *_backend, unsigned int list_flags, @@ -200,6 +243,9 @@ int refdb_backend_test( backend->parent.exists = &refdb_test_backend__exists; backend->parent.lookup = &refdb_test_backend__lookup; + backend->parent.iterator = &refdb_test_backend__iterator; + backend->parent.next = &refdb_test_backend__next; + backend->parent.iterator_free = &refdb_test_backend__iterator_free; backend->parent.foreach = &refdb_test_backend__foreach; backend->parent.write = &refdb_test_backend__write; backend->parent.delete = &refdb_test_backend__delete; From 95727245fd4f08aa77d1077901976ab3bd7472e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 16:03:17 +0200 Subject: [PATCH 212/384] refs: implement _foreach with the iterator --- src/refs.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/refs.c b/src/refs.c index 547bd570c..4ee6235cf 100644 --- a/src/refs.c +++ b/src/refs.c @@ -562,10 +562,28 @@ int git_reference_foreach( git_reference_foreach_cb callback, void *payload) { - git_refdb *refdb; - git_repository_refdb__weakptr(&refdb, repo); + git_reference_iterator *iter; + const char *name; + int error; - return git_refdb_foreach(refdb, list_flags, callback, payload); + GIT_UNUSED(list_flags); + + if (git_reference_iterator_new(&iter, repo) < 0) + return -1; + + while ((error = git_reference_next(&name, iter)) == 0) { + if (callback(name, payload)) { + error = GIT_EUSER; + goto out; + } + } + + if (error == GIT_ITEROVER) + error = 0; + +out: + git_reference_iterator_free(iter); + return error; } int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo) From 2b562c3a1edf0f521bdb7adf23e524d5a8389b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 16:32:58 +0200 Subject: [PATCH 213/384] refs: remove the OID/SYMBOLIC filtering Nobody should ever be using anything other than ALL at this level, so remove the option altogether. As part of this, git_reference_foreach_glob is now implemented in the frontend using an iterator. Backends will later regain the ability of doing the glob filtering in the backend. --- examples/general.c | 2 +- include/git2/refs.h | 19 +------ include/git2/sys/refdb_backend.h | 31 +++-------- src/branch.c | 2 +- src/clone.c | 1 - src/refdb.c | 48 ---------------- src/refdb.h | 13 ----- src/refdb_fs.c | 95 -------------------------------- src/refs.c | 39 ++++++++----- src/remote.c | 1 - src/repository.c | 2 +- src/revwalk.c | 2 +- src/tag.c | 2 +- src/transports/local.c | 2 +- src/transports/smart_protocol.c | 2 +- tests-clar/network/fetchlocal.c | 6 +- tests-clar/refdb/inmemory.c | 4 +- tests-clar/refdb/testdb.c | 28 ---------- tests-clar/refs/foreachglob.c | 14 ++--- tests-clar/refs/list.c | 4 +- tests-clar/refs/listall.c | 4 +- 21 files changed, 55 insertions(+), 266 deletions(-) diff --git a/examples/general.c b/examples/general.c index adc7ed8d2..d7a58479c 100644 --- a/examples/general.c +++ b/examples/general.c @@ -453,7 +453,7 @@ int main (int argc, char** argv) // Here we will implement something like `git for-each-ref` simply listing // out all available references and the object SHA they resolve to. git_strarray ref_list; - git_reference_list(&ref_list, repo, GIT_REF_LISTALL); + git_reference_list(&ref_list, repo); const char *refname; git_reference *ref; diff --git a/include/git2/refs.h b/include/git2/refs.h index 48ecc96e0..9bb78ccd6 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -284,12 +284,6 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref); /** * Fill a list with all the references that can be found in a repository. * - * Using the `list_flags` parameter, the listed references may be filtered - * by type (`GIT_REF_OID` or `GIT_REF_SYMBOLIC`) or using a bitwise OR of - * `git_ref_t` values. To include packed refs, include `GIT_REF_PACKED`. - * For convenience, use the value `GIT_REF_LISTALL` to obtain all - * references, including packed ones. - * * The string array will be filled with the names of all references; these * values are owned by the user and should be free'd manually when no * longer needed, using `git_strarray_free()`. @@ -297,36 +291,27 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref); * @param array Pointer to a git_strarray structure where * the reference names will be stored * @param repo Repository where to find the refs - * @param list_flags Filtering flags for the reference listing * @return 0 or an error code */ -GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo, unsigned int list_flags); +GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo); typedef int (*git_reference_foreach_cb)(const char *refname, void *payload); /** * Perform a callback on each reference in the repository. * - * Using the `list_flags` parameter, the references may be filtered by - * type (`GIT_REF_OID` or `GIT_REF_SYMBOLIC`) or using a bitwise OR of - * `git_ref_t` values. To include packed refs, include `GIT_REF_PACKED`. - * For convenience, use the value `GIT_REF_LISTALL` to obtain all - * references, including packed ones. - * * The `callback` function will be called for each reference in the * repository, receiving the name of the reference and the `payload` value * passed to this method. Returning a non-zero value from the callback * will terminate the iteration. * * @param repo Repository where to find the refs - * @param list_flags Filtering flags for the reference listing. * @param callback Function which will be called for every listed ref * @param payload Additional data to pass to the callback * @return 0 on success, GIT_EUSER on non-zero callback, or error code */ GIT_EXTERN(int) git_reference_foreach( git_repository *repo, - unsigned int list_flags, git_reference_foreach_cb callback, void *payload); @@ -385,7 +370,6 @@ GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter); * * @param repo Repository where to find the refs * @param glob Pattern to match (fnmatch-style) against reference name. - * @param list_flags Filtering flags for the reference listing. * @param callback Function which will be called for every listed ref * @param payload Additional data to pass to the callback * @return 0 on success, GIT_EUSER on non-zero callback, or error code @@ -393,7 +377,6 @@ GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter); GIT_EXTERN(int) git_reference_foreach_glob( git_repository *repo, const char *glob, - unsigned int list_flags, git_reference_foreach_cb callback, void *payload); diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 4c882b5cd..f5eacd105 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -59,30 +59,9 @@ struct git_refdb_backend { const char *ref_name); /** - * Enumerates each reference in the refdb. A refdb implementation must - * provide this function. - */ - int (*foreach)( - git_refdb_backend *backend, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload); - - /** - * Enumerates each reference in the refdb that matches the given - * glob string. A refdb implementation may provide this function; - * if it is not provided, foreach will be used and the results filtered - * against the glob. - */ - int (*foreach_glob)( - git_refdb_backend *backend, - const char *glob, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload); - - /** - * Allocate an iterator object for the backend + * Allocate an iterator object for the backend. + * + * A refdb implementation must provide this function. */ int (*iterator)( git_reference_iterator **iter, @@ -90,6 +69,8 @@ struct git_refdb_backend { /** * Return the current value and advance the iterator. + * + * A refdb implementation must provide this function. */ int (*next)( const char **name, @@ -97,6 +78,8 @@ struct git_refdb_backend { /** * Free the iterator + * + * A refdb implementation must provide this function. */ void (*iterator_free)( git_reference_iterator *iter); diff --git a/src/branch.c b/src/branch.c index 88f052529..74e8d843e 100644 --- a/src/branch.c +++ b/src/branch.c @@ -157,7 +157,7 @@ int git_branch_foreach( filter.branch_type = list_flags; filter.callback_payload = payload; - return git_reference_foreach(repo, GIT_REF_LISTALL, &branch_foreach_cb, (void *)&filter); + return git_reference_foreach(repo, &branch_foreach_cb, (void *)&filter); } int git_branch_move( diff --git a/src/clone.c b/src/clone.c index aeb7bbf5c..49af9a1e2 100644 --- a/src/clone.c +++ b/src/clone.c @@ -243,7 +243,6 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote) /* Not master. Check all the other refs. */ if (git_reference_foreach( repo, - GIT_REF_LISTALL, reference_matches_remote_head, &head_info) < 0) goto cleanup; diff --git a/src/refdb.c b/src/refdb.c index 269234bfa..5e33c2e38 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -152,60 +152,12 @@ void git_refdb_iterator_free(git_reference_iterator *iter) iter->backend->iterator_free(iter); } -int git_refdb_foreach( - git_refdb *db, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload) -{ - assert(db && db->backend); - - return db->backend->foreach(db->backend, list_flags, callback, payload); -} - struct glob_cb_data { const char *glob; git_reference_foreach_cb callback; void *payload; }; -static int fromglob_cb(const char *reference_name, void *payload) -{ - struct glob_cb_data *data = (struct glob_cb_data *)payload; - - if (!p_fnmatch(data->glob, reference_name, 0)) - return data->callback(reference_name, data->payload); - - return 0; -} - -int git_refdb_foreach_glob( - git_refdb *db, - const char *glob, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload) -{ - int error; - struct glob_cb_data data; - - assert(db && db->backend && glob && callback); - - if(db->backend->foreach_glob != NULL) - error = db->backend->foreach_glob(db->backend, - glob, list_flags, callback, payload); - else { - data.glob = glob; - data.callback = callback; - data.payload = payload; - - error = db->backend->foreach(db->backend, - list_flags, fromglob_cb, &data); - } - - return error; -} - int git_refdb_write(git_refdb *db, const git_reference *ref) { assert(db && db->backend); diff --git a/src/refdb.h b/src/refdb.h index a243f627c..e88dead7a 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -26,19 +26,6 @@ int git_refdb_lookup( git_refdb *refdb, const char *ref_name); -int git_refdb_foreach( - git_refdb *refdb, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload); - -int git_refdb_foreach_glob( - git_refdb *refdb, - const char *glob, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload); - int git_refdb_iterator(git_reference_iterator **out, git_refdb *db); int git_refdb_next(const char **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 62965a66b..ab04e453e 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -562,100 +562,6 @@ struct dirent_list_data { int callback_error; }; -static git_ref_t loose_guess_rtype(const git_buf *full_path) -{ - git_buf ref_file = GIT_BUF_INIT; - git_ref_t type; - - type = GIT_REF_INVALID; - - if (git_futils_readbuffer(&ref_file, full_path->ptr) == 0) { - if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) - type = GIT_REF_SYMBOLIC; - else - type = GIT_REF_OID; - } - - git_buf_free(&ref_file); - return type; -} - -static int _dirent_loose_listall(void *_data, git_buf *full_path) -{ - struct dirent_list_data *data = (struct dirent_list_data *)_data; - const char *file_path = full_path->ptr + data->repo_path_len; - - if (git_path_isdir(full_path->ptr) == true) - return git_path_direach(full_path, _dirent_loose_listall, _data); - - /* do not add twice a reference that exists already in the packfile */ - if (git_strmap_exists(data->backend->refcache.packfile, file_path)) - return 0; - - if (data->list_type != GIT_REF_LISTALL) { - if ((data->list_type & loose_guess_rtype(full_path)) == 0) - return 0; /* we are filtering out this reference */ - } - - /* Locked references aren't returned */ - if (!git__suffixcmp(file_path, GIT_FILELOCK_EXTENSION)) - return 0; - - if (data->callback(file_path, data->callback_payload)) - data->callback_error = GIT_EUSER; - - return data->callback_error; -} - -static int refdb_fs_backend__foreach( - git_refdb_backend *_backend, - unsigned int list_type, - git_reference_foreach_cb callback, - void *payload) -{ - refdb_fs_backend *backend; - int result; - struct dirent_list_data data; - git_buf refs_path = GIT_BUF_INIT; - const char *ref_name; - void *ref = NULL; - - GIT_UNUSED(ref); - - assert(_backend); - backend = (refdb_fs_backend *)_backend; - - if (packed_load(backend) < 0) - return -1; - - /* list all the packed references first */ - if (list_type & GIT_REF_OID) { - git_strmap_foreach(backend->refcache.packfile, ref_name, ref, { - if (callback(ref_name, payload)) - return GIT_EUSER; - }); - } - - /* now list the loose references, trying not to - * duplicate the ref names already in the packed-refs file */ - - data.repo_path_len = strlen(backend->path); - data.list_type = list_type; - data.backend = backend; - data.callback = callback; - data.callback_payload = payload; - data.callback_error = 0; - - if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0) - return -1; - - result = git_path_direach(&refs_path, _dirent_loose_listall, &data); - - git_buf_free(&refs_path); - - return data.callback_error ? GIT_EUSER : result; -} - typedef struct { git_reference_iterator parent; unsigned int loose; @@ -1211,7 +1117,6 @@ int git_refdb_backend_fs( backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; - backend->parent.foreach = &refdb_fs_backend__foreach; backend->parent.iterator = &refdb_fs_backend__iterator; backend->parent.next = &refdb_fs_backend__next; backend->parent.iterator_free = &refdb_fs_backend__iterator_free; diff --git a/src/refs.c b/src/refs.c index 4ee6235cf..a7be117ad 100644 --- a/src/refs.c +++ b/src/refs.c @@ -139,7 +139,7 @@ static int reference_path_available( data.available = 1; error = git_reference_foreach( - repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&data); + repo, _reference_available_cb, (void *)&data); if (error < 0) return error; @@ -558,7 +558,6 @@ int git_reference_resolve(git_reference **ref_out, const git_reference *ref) int git_reference_foreach( git_repository *repo, - unsigned int list_flags, git_reference_foreach_cb callback, void *payload) { @@ -566,8 +565,6 @@ int git_reference_foreach( const char *name; int error; - GIT_UNUSED(list_flags); - if (git_reference_iterator_new(&iter, repo) < 0) return -1; @@ -613,8 +610,7 @@ static int cb__reflist_add(const char *ref, void *data) int git_reference_list( git_strarray *array, - git_repository *repo, - unsigned int list_flags) + git_repository *repo) { git_vector ref_list; @@ -627,7 +623,7 @@ int git_reference_list( return -1; if (git_reference_foreach( - repo, list_flags, &cb__reflist_add, (void *)&ref_list) < 0) { + repo, &cb__reflist_add, (void *)&ref_list) < 0) { git_vector_free(&ref_list); return -1; } @@ -925,19 +921,32 @@ int git_reference__update_terminal( int git_reference_foreach_glob( git_repository *repo, const char *glob, - unsigned int list_flags, - int (*callback)( - const char *reference_name, - void *payload), + git_reference_foreach_cb callback, void *payload) { - git_refdb *refdb; + git_reference_iterator *iter; + const char *name; + int error; - assert(repo && glob && callback); + if (git_reference_iterator_new(&iter, repo) < 0) + return -1; - git_repository_refdb__weakptr(&refdb, repo); + while ((error = git_reference_next(&name, iter)) == 0) { + if (p_fnmatch(glob, name, 0)) + continue; - return git_refdb_foreach_glob(refdb, glob, list_flags, callback, payload); + if (callback(name, payload)) { + error = GIT_EUSER; + goto out; + } + } + + if (error == GIT_ITEROVER) + error = 0; + +out: + git_reference_iterator_free(iter); + return error; } int git_reference_has_log( diff --git a/src/remote.c b/src/remote.c index 6eaaf8b49..10bffec44 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1297,7 +1297,6 @@ static int rename_remote_references( if (git_reference_foreach( repo, - GIT_REF_LISTALL, rename_cb, &refnames) < 0) goto cleanup; diff --git a/src/repository.c b/src/repository.c index e6eaf753c..80118a41f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1469,7 +1469,7 @@ static int at_least_one_cb(const char *refname, void *payload) static int repo_contains_no_reference(git_repository *repo) { - int error = git_reference_foreach(repo, GIT_REF_LISTALL, at_least_one_cb, NULL); + int error = git_reference_foreach(repo, at_least_one_cb, NULL); if (error == GIT_EUSER) return 0; diff --git a/src/revwalk.c b/src/revwalk.c index 16f06624d..528d02b20 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -186,7 +186,7 @@ static int push_glob(git_revwalk *walk, const char *glob, int hide) data.hide = hide; if (git_reference_foreach_glob( - walk->repo, git_buf_cstr(&buf), GIT_REF_LISTALL, push_glob_cb, &data) < 0) + walk->repo, git_buf_cstr(&buf), push_glob_cb, &data) < 0) goto on_error; regfree(&preg); diff --git a/src/tag.c b/src/tag.c index a4f2e2581..f81956de7 100644 --- a/src/tag.c +++ b/src/tag.c @@ -427,7 +427,7 @@ int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) data.cb_data = cb_data; data.repo = repo; - return git_reference_foreach(repo, GIT_REF_OID, &tags_cb, &data); + return git_reference_foreach(repo, &tags_cb, &data); } typedef struct { diff --git a/src/transports/local.c b/src/transports/local.c index 8b4d50c14..bd3bf93bf 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -124,7 +124,7 @@ static int store_refs(transport_local *t) assert(t); - if (git_reference_list(&ref_names, t->repo, GIT_REF_LISTALL) < 0 || + if (git_reference_list(&ref_names, t->repo) < 0 || git_vector_init(&t->refs, ref_names.count, NULL) < 0) goto on_error; diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 765b914b7..09bef1ae3 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -193,7 +193,7 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo) unsigned int i; git_reference *ref; - if (git_reference_list(&refs, repo, GIT_REF_LISTALL) < 0) + if (git_reference_list(&refs, repo) < 0) return -1; if (git_revwalk_new(&walk, repo) < 0) diff --git a/tests-clar/network/fetchlocal.c b/tests-clar/network/fetchlocal.c index bcf298cde..09335b3df 100644 --- a/tests-clar/network/fetchlocal.c +++ b/tests-clar/network/fetchlocal.c @@ -34,7 +34,7 @@ void test_network_fetchlocal__complete(void) cl_git_pass(git_remote_download(origin, transfer_cb, &callcount)); cl_git_pass(git_remote_update_tips(origin)); - cl_git_pass(git_reference_list(&refnames, repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&refnames, repo)); cl_assert_equal_i(19, (int)refnames.count); cl_assert(callcount > 0); @@ -58,7 +58,7 @@ void test_network_fetchlocal__partial(void) const char *url; cl_set_cleanup(&cleanup_sandbox, NULL); - cl_git_pass(git_reference_list(&refnames, repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&refnames, repo)); cl_assert_equal_i(1, (int)refnames.count); url = cl_git_fixture_url("testrepo.git"); @@ -69,7 +69,7 @@ void test_network_fetchlocal__partial(void) git_strarray_free(&refnames); - cl_git_pass(git_reference_list(&refnames, repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&refnames, repo)); cl_assert_equal_i(20, (int)refnames.count); /* 18 remote + 1 local */ cl_assert(callcount > 0); diff --git a/tests-clar/refdb/inmemory.c b/tests-clar/refdb/inmemory.c index 243b5bb37..d2594cd6d 100644 --- a/tests-clar/refdb/inmemory.c +++ b/tests-clar/refdb/inmemory.c @@ -163,7 +163,7 @@ void test_refdb_inmemory__foreach(void) cl_git_pass(git_oid_fromstr(&oid3, "763d71aadf09a7951596c9746c024e7eece7c7af")); cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0)); - cl_git_pass(git_reference_foreach(repo, GIT_REF_LISTALL, foreach_test, &i)); + cl_git_pass(git_reference_foreach(repo,foreach_test, &i)); cl_assert_equal_i(3, (int)i); git_reference_free(write1); @@ -210,7 +210,7 @@ void test_refdb_inmemory__delete(void) git_reference_delete(write3); git_reference_free(write3); - cl_git_pass(git_reference_foreach(repo, GIT_REF_LISTALL, delete_test, &i)); + cl_git_pass(git_reference_foreach(repo, delete_test, &i)); cl_assert_equal_i(1, (int)i); git_reference_free(write2); diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index eee60ac90..4d118562a 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -155,33 +155,6 @@ static void refdb_test_backend__iterator_free(git_reference_iterator *iter) git__free(iter); } -static int refdb_test_backend__foreach( - git_refdb_backend *_backend, - unsigned int list_flags, - git_reference_foreach_cb callback, - void *payload) -{ - refdb_test_backend *backend; - refdb_test_entry *entry; - size_t i; - - assert(_backend); - backend = (refdb_test_backend *)_backend; - - git_vector_foreach(&backend->refs, i, entry) { - if (entry->type == GIT_REF_OID && (list_flags & GIT_REF_OID) == 0) - continue; - - if (entry->type == GIT_REF_SYMBOLIC && (list_flags & GIT_REF_SYMBOLIC) == 0) - continue; - - if (callback(entry->name, payload) != 0) - return GIT_EUSER; - } - - return 0; -} - static void refdb_test_entry_free(refdb_test_entry *entry) { if (entry->type == GIT_REF_SYMBOLIC) @@ -246,7 +219,6 @@ int refdb_backend_test( backend->parent.iterator = &refdb_test_backend__iterator; backend->parent.next = &refdb_test_backend__next; backend->parent.iterator_free = &refdb_test_backend__iterator_free; - backend->parent.foreach = &refdb_test_backend__foreach; backend->parent.write = &refdb_test_backend__write; backend->parent.delete = &refdb_test_backend__delete; backend->parent.free = &refdb_test_backend__free; diff --git a/tests-clar/refs/foreachglob.c b/tests-clar/refs/foreachglob.c index 4da1a15dd..2c458082f 100644 --- a/tests-clar/refs/foreachglob.c +++ b/tests-clar/refs/foreachglob.c @@ -37,11 +37,11 @@ static int count_cb(const char *reference_name, void *payload) return 0; } -static void assert_retrieval(const char *glob, unsigned int flags, int expected_count) +static void assert_retrieval(const char *glob, int expected_count) { int count = 0; - cl_git_pass(git_reference_foreach_glob(repo, glob, flags, count_cb, &count)); + cl_git_pass(git_reference_foreach_glob(repo, glob, count_cb, &count)); cl_assert_equal_i(expected_count, count); } @@ -49,17 +49,17 @@ static void assert_retrieval(const char *glob, unsigned int flags, int expected_ void test_refs_foreachglob__retrieve_all_refs(void) { /* 12 heads (including one packed head) + 1 note + 2 remotes + 7 tags */ - assert_retrieval("*", GIT_REF_LISTALL, 22); + assert_retrieval("*", 22); } void test_refs_foreachglob__retrieve_remote_branches(void) { - assert_retrieval("refs/remotes/*", GIT_REF_LISTALL, 2); + assert_retrieval("refs/remotes/*", 2); } void test_refs_foreachglob__retrieve_local_branches(void) { - assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 12); + assert_retrieval("refs/heads/*", 12); } void test_refs_foreachglob__retrieve_partially_named_references(void) @@ -69,7 +69,7 @@ void test_refs_foreachglob__retrieve_partially_named_references(void) * refs/remotes/test/master, refs/tags/test */ - assert_retrieval("*test*", GIT_REF_LISTALL, 4); + assert_retrieval("*test*", 4); } @@ -89,7 +89,7 @@ void test_refs_foreachglob__can_cancel(void) int count = 0; cl_assert_equal_i(GIT_EUSER, git_reference_foreach_glob( - repo, "*", GIT_REF_LISTALL, interrupt_cb, &count) ); + repo, "*", interrupt_cb, &count) ); cl_assert_equal_i(11, count); } diff --git a/tests-clar/refs/list.c b/tests-clar/refs/list.c index 8a386fdfd..c9c2af4a7 100644 --- a/tests-clar/refs/list.c +++ b/tests-clar/refs/list.c @@ -25,7 +25,7 @@ void test_refs_list__all(void) // try to list all the references in our test repo git_strarray ref_list; - cl_git_pass(git_reference_list(&ref_list, g_repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&ref_list, g_repo)); /*{ unsigned short i; @@ -50,7 +50,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten "./testrepo/.git/refs/heads/hanwen.lock", "144344043ba4d4a405da03de3844aa829ae8be0e\n"); - cl_git_pass(git_reference_list(&ref_list, g_repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&ref_list, g_repo)); cl_assert_equal_i((int)ref_list.count, 13); git_strarray_free(&ref_list); diff --git a/tests-clar/refs/listall.c b/tests-clar/refs/listall.c index 8f4c3746b..c696fbb2e 100644 --- a/tests-clar/refs/listall.c +++ b/tests-clar/refs/listall.c @@ -9,7 +9,7 @@ static void ensure_no_refname_starts_with_a_forward_slash(const char *path) size_t i; cl_git_pass(git_repository_open(&repo, path)); - cl_git_pass(git_reference_list(&ref_list, repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&ref_list, repo)); cl_assert(ref_list.count > 0); @@ -38,7 +38,7 @@ void test_refs_listall__from_repository_opened_through_gitdir_path(void) void test_refs_listall__from_repository_with_no_trailing_newline(void) { cl_git_pass(git_repository_open(&repo, cl_fixture("bad_tag.git"))); - cl_git_pass(git_reference_list(&ref_list, repo, GIT_REF_LISTALL)); + cl_git_pass(git_reference_list(&ref_list, repo)); cl_assert(ref_list.count > 0); From 9bd89d962265b399b537e41058d40a6aeab00e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 16:49:39 +0200 Subject: [PATCH 214/384] Move a couple more functions to use iterators --- src/branch.c | 57 +++++++++++++++++++++++++++------------------------- src/remote.c | 32 +++++++++++++++++------------ 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/branch.c b/src/branch.c index 74e8d843e..5f4e338ba 100644 --- a/src/branch.c +++ b/src/branch.c @@ -124,40 +124,43 @@ on_error: return error; } -typedef struct { - git_branch_foreach_cb branch_cb; - void *callback_payload; - unsigned int branch_type; -} branch_foreach_filter; - -static int branch_foreach_cb(const char *branch_name, void *payload) -{ - branch_foreach_filter *filter = (branch_foreach_filter *)payload; - - if (filter->branch_type & GIT_BRANCH_LOCAL && - git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0) - return filter->branch_cb(branch_name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, filter->callback_payload); - - if (filter->branch_type & GIT_BRANCH_REMOTE && - git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0) - return filter->branch_cb(branch_name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, filter->callback_payload); - - return 0; -} - int git_branch_foreach( git_repository *repo, unsigned int list_flags, - git_branch_foreach_cb branch_cb, + git_branch_foreach_cb callback, void *payload) { - branch_foreach_filter filter; + git_reference_iterator *iter; + const char *name; + int error; - filter.branch_cb = branch_cb; - filter.branch_type = list_flags; - filter.callback_payload = payload; + if (git_reference_iterator_new(&iter, repo) < 0) + return -1; + + while ((error = git_reference_next(&name, iter)) == 0) { + if (list_flags & GIT_BRANCH_LOCAL && + git__prefixcmp(name, GIT_REFS_HEADS_DIR) == 0) { + if (callback(name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, payload)) { + error = GIT_EUSER; + break; + } + } + + if (list_flags & GIT_BRANCH_REMOTE && + git__prefixcmp(name, GIT_REFS_REMOTES_DIR) == 0) { + if (callback(name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, payload)) { + error = GIT_EUSER; + break; + } + } + } + + if (error == GIT_ITEROVER) + error = 0; + + git_reference_iterator_free(iter); + return error; - return git_reference_foreach(repo, &branch_foreach_cb, (void *)&filter); } int git_branch_move( diff --git a/src/remote.c b/src/remote.c index 10bffec44..2f25c0f4a 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1244,14 +1244,6 @@ static int update_branch_remote_config_entry( update_config_entries_cb, &data); } -static int rename_cb(const char *ref, void *data) -{ - if (git__prefixcmp(ref, GIT_REFS_REMOTES_DIR)) - return 0; - - return git_vector_insert((git_vector *)data, git__strdup(ref)); -} - static int rename_one_remote_reference( git_repository *repo, const char *reference_name, @@ -1291,15 +1283,29 @@ static int rename_remote_references( int error = -1; unsigned int i; char *name; + const char *refname; + git_reference_iterator *iter; if (git_vector_init(&refnames, 8, NULL) < 0) + return -1; + + if (git_reference_iterator_new(&iter, repo) < 0) goto cleanup; - if (git_reference_foreach( - repo, - rename_cb, - &refnames) < 0) - goto cleanup; + while ((error = git_reference_next(&refname, iter)) == 0) { + if (git__prefixcmp(refname, GIT_REFS_REMOTES_DIR)) + continue; + + if ((error = git_vector_insert(&refnames, git__strdup(refname))) < 0) + break; + + } + + git_reference_iterator_free(iter); + if (error == GIT_ITEROVER) + error = 0; + else + goto cleanup; git_vector_foreach(&refnames, i, name) { if ((error = rename_one_remote_reference(repo, name, old_name, new_name)) < 0) From c58cac12c24fbb127cf1928bec20decb007a75e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 4 May 2013 18:06:14 +0200 Subject: [PATCH 215/384] Introduce a glob-filtering iterator If the backend doesn't provide support for it, the matching is done in refdb on top of a normal iterator. --- include/git2/sys/refdb_backend.h | 14 ++++++++++- src/refdb.c | 42 +++++++++++++++++++++++++++----- src/refdb.h | 1 + src/refs.c | 15 +++++++++--- tests-clar/refdb/testdb.c | 2 +- 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index f5eacd105..8dbf38ca9 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -30,10 +30,11 @@ GIT_BEGIN_DECL * ... * } * - * and assing `iter->parent.backend` to your `git_refdb_backend`. + * and assign `iter->parent.backend` to your `git_refdb_backend`. */ struct git_reference_iterator { git_refdb_backend *backend; + char *glob; }; /** An instance for a custom backend */ @@ -67,6 +68,17 @@ struct git_refdb_backend { git_reference_iterator **iter, struct git_refdb_backend *backend); + /** + * Allocate a glob-filtering iterator object for the backend. + * + * A refdb implementation may provide this function. If it's + * not available, the glob matching will be done by the frontend. + */ + int (*iterator_glob)( + git_reference_iterator **iter, + struct git_refdb_backend *backend, + const char *glob); + /** * Return the current value and advance the iterator. * diff --git a/src/refdb.c b/src/refdb.c index 5e33c2e38..9f9037ce7 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -126,29 +126,59 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name) int git_refdb_iterator(git_reference_iterator **out, git_refdb *db) { - git_reference_iterator *iter; - if (!db->backend || !db->backend->iterator) { giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators"); return -1; } - if (db->backend->iterator(&iter, db->backend) < 0) { - git__free(iter); + if (db->backend->iterator(out, db->backend) < 0) + return -1; + + return 0; +} + +int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob) +{ + if (!db->backend) { + giterr_set(GITERR_REFERENCE, "There are no backends loaded"); + return -1; + } + + if (db->backend->iterator_glob) + return db->backend->iterator_glob(out, db->backend, glob); + + /* If the backend doesn't support glob-filtering themselves, we have to do it */ + if (db->backend->iterator(out, db->backend) < 0) + return -1; + + (*out)->glob = git__strdup(glob); + if (!(*out)->glob) { + db->backend->iterator_free(*out); return -1; } - *out = iter; return 0; } int git_refdb_next(const char **out, git_reference_iterator *iter) { - return iter->backend->next(out, iter); + int error; + + if (!iter->glob) + return iter->backend->next(out, iter); + + /* If the iterator has a glob, we need to filter */ + while ((error = iter->backend->next(out, iter)) == 0) { + if (!p_fnmatch(iter->glob, *out, 0)) + break; + } + + return error; } void git_refdb_iterator_free(git_reference_iterator *iter) { + git__free(iter->glob); iter->backend->iterator_free(iter); } diff --git a/src/refdb.h b/src/refdb.h index e88dead7a..2edd05d18 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -27,6 +27,7 @@ int git_refdb_lookup( const char *ref_name); int git_refdb_iterator(git_reference_iterator **out, git_refdb *db); +int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob); int git_refdb_next(const char **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); diff --git a/src/refs.c b/src/refs.c index a7be117ad..fc6652fec 100644 --- a/src/refs.c +++ b/src/refs.c @@ -593,6 +593,16 @@ int git_reference_iterator_new(git_reference_iterator **out, git_repository *rep return git_refdb_iterator(out, refdb); } +int git_reference_iterator_glob_new(git_reference_iterator **out, git_repository *repo, const char *glob) +{ + git_refdb *refdb; + + if (git_repository_refdb__weakptr(&refdb, repo) < 0) + return -1; + + return git_refdb_iterator_glob(out, refdb, glob); +} + int git_reference_next(const char **out, git_reference_iterator *iter) { return git_refdb_next(out, iter); @@ -928,13 +938,10 @@ int git_reference_foreach_glob( const char *name; int error; - if (git_reference_iterator_new(&iter, repo) < 0) + if (git_reference_iterator_glob_new(&iter, repo, glob) < 0) return -1; while ((error = git_reference_next(&name, iter)) == 0) { - if (p_fnmatch(glob, name, 0)) - continue; - if (callback(name, payload)) { error = GIT_EUSER; goto out; diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index 4d118562a..961e18d44 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -123,7 +123,7 @@ static int refdb_test_backend__iterator(git_reference_iterator **out, git_refdb_ GIT_UNUSED(_backend); - iter = git__malloc(sizeof(refdb_test_iter)); + iter = git__calloc(1, sizeof(refdb_test_iter)); GITERR_CHECK_ALLOC(iter); iter->parent.backend = _backend; From e583334c00e80274866c87495e6ed2a40ec0a6f6 Mon Sep 17 00:00:00 2001 From: Linquize Date: Fri, 10 May 2013 21:42:22 +0800 Subject: [PATCH 216/384] Fix broken build when MSVC SDL checks is enabled --- src/commit.c | 2 +- src/config_file.c | 2 +- src/object.c | 2 +- src/push.c | 2 +- src/transports/smart_protocol.c | 10 +++++----- src/transports/winhttp.c | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/commit.c b/src/commit.c index 3dc647c9b..be6e32c76 100644 --- a/src/commit.c +++ b/src/commit.c @@ -292,7 +292,7 @@ int git_commit_nth_gen_ancestor( const git_commit *commit, unsigned int n) { - git_commit *current, *parent; + git_commit *current, *parent = NULL; int error; assert(ancestor && commit); diff --git a/src/config_file.c b/src/config_file.c index a9a40c366..e57cd1e53 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -296,7 +296,7 @@ cleanup: static int config_set(git_config_backend *cfg, const char *name, const char *value) { - cvar_t *var = NULL, *old_var; + cvar_t *var = NULL, *old_var = NULL; diskfile_backend *b = (diskfile_backend *)cfg; char *key, *esc_value = NULL; khiter_t pos; diff --git a/src/object.c b/src/object.c index a6807f26b..9b8ccdd3e 100644 --- a/src/object.c +++ b/src/object.c @@ -117,7 +117,7 @@ int git_object_lookup_prefix( { git_object *object = NULL; git_odb *odb = NULL; - git_odb_object *odb_obj; + git_odb_object *odb_obj = NULL; int error = 0; assert(repo && object_out && id); diff --git a/src/push.c b/src/push.c index 0499d8648..452d71789 100644 --- a/src/push.c +++ b/src/push.c @@ -180,7 +180,7 @@ int git_push_update_tips(git_push *push) git_buf remote_ref_name = GIT_BUF_INIT; size_t i, j; git_refspec *fetch_spec; - push_spec *push_spec; + push_spec *push_spec = NULL; git_reference *remote_ref; push_status *status; int error = 0; diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 09bef1ae3..67d309523 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -21,8 +21,8 @@ int git_smart__store_refs(transport_smart *t, int flushes) gitno_buffer *buf = &t->buffer; git_vector *refs = &t->refs; int error, flush = 0, recvd; - const char *line_end; - git_pkt *pkt; + const char *line_end = NULL; + git_pkt *pkt = NULL; git_pkt_ref *ref; size_t i; @@ -135,7 +135,7 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps) static int recv_pkt(git_pkt **out, gitno_buffer *buf) { const char *ptr = buf->data, *line_end = ptr; - git_pkt *pkt; + git_pkt *pkt = NULL; int pkt_type, error = 0, ret; do { @@ -640,8 +640,8 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt) static int parse_report(gitno_buffer *buf, git_push *push) { - git_pkt *pkt; - const char *line_end; + git_pkt *pkt = NULL; + const char *line_end = NULL; int error, recvd; for (;;) { diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index e502001cb..d803c812c 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -893,7 +893,7 @@ static int winhttp_connect( wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")"; wchar_t host[GIT_WIN_PATH]; int32_t port; - const char *default_port; + const char *default_port = "80"; int ret; if (!git__prefixcmp(url, prefix_http)) { From 99d32707b963e6fea16817cafd5c3f579578a262 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Sat, 11 May 2013 06:42:25 -0700 Subject: [PATCH 217/384] Fix refdb iteration early termination bug There was a problem found in the Rugged test suite where the refdb_fs_backend__next function could exit too early in some very specific hashing patterns for packed refs. This ports the Rugged test to libgit2 and then fixes the bug. --- src/refdb_fs.c | 21 ++++++----- tests-clar/refs/branches/foreach.c | 34 +++++++++++++----- tests-clar/resources/testrepo2/.gitted/HEAD | 1 + tests-clar/resources/testrepo2/.gitted/config | 14 ++++++++ .../resources/testrepo2/.gitted/description | 1 + tests-clar/resources/testrepo2/.gitted/index | Bin 0 -> 512 bytes .../resources/testrepo2/.gitted/info/exclude | 6 ++++ .../resources/testrepo2/.gitted/logs/HEAD | 1 + .../testrepo2/.gitted/logs/refs/heads/master | 1 + .../.gitted/logs/refs/remotes/origin/HEAD | 1 + .../0c/37a5391bbff43c37f0d0371823a5509eed5b1d | Bin 0 -> 134 bytes .../13/85f264afb75a56a5bec74243be9b367ba4ca08 | Bin 0 -> 19 bytes .../18/1037049a54a1eb5fab404658a3a250b44335d7 | Bin 0 -> 51 bytes .../18/10dff58d8a660512d4832e740f692884338ccd | Bin 0 -> 119 bytes .../2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 | Bin 0 -> 125 bytes .../36/060c58702ed4c2a40832c51758d5344201d89a | 2 ++ .../45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 | Bin 0 -> 18 bytes .../4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 | 2 ++ .../5b/5b025afb0b4c913b4c338a42934a3863bf3644 | 2 ++ .../61/9f9935957e010c419cb9d15621916ddfcc0b96 | Bin 0 -> 116 bytes .../75/057dd4114e74cca1d750d0aee1647c903cb60a | Bin 0 -> 119 bytes .../7f/043268ea43ce18e3540acaabf9e090c91965b0 | Bin 0 -> 55 bytes .../81/4889a078c031f61ed08ab5fa863aea9314344d | Bin 0 -> 82 bytes .../84/96071c1b46c854b31185ea97743be6a8774479 | Bin 0 -> 126 bytes .../9f/d738e8f7967c078dceed8190330fc8648ee56a | 3 ++ .../a4/a7dce85cf63874e984719f4fdd239f5145052f | 2 ++ .../a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd | Bin 0 -> 28 bytes .../a8/233120f6ad708f843d861ce2b7228ec4e3dec6 | Bin 0 -> 26 bytes .../be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 | 3 ++ .../c4/7800c7266a2be04c571c04d5a6614691ea99bd | 3 ++ .../c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b | Bin 0 -> 116 bytes .../e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 | Bin 0 -> 15 bytes .../f6/0079018b664e4e79329a7ef9559c8d9e0378d1 | Bin 0 -> 82 bytes .../fa/49b077972391ad58037050f2a75f74e3671e92 | Bin 0 -> 24 bytes .../fd/093bff70906175335656e6ce6ae05783708765 | Bin 0 -> 82 bytes ...c6adf9f61318f041845b01440d09aa7a91e1b5.idx | Bin 0 -> 1240 bytes ...6adf9f61318f041845b01440d09aa7a91e1b5.pack | Bin 0 -> 491 bytes .../resources/testrepo2/.gitted/packed-refs | 6 ++++ .../testrepo2/.gitted/refs/heads/master | 1 + .../.gitted/refs/remotes/origin/HEAD | 1 + tests-clar/resources/testrepo2/README | 1 + tests-clar/resources/testrepo2/new.txt | 1 + tests-clar/resources/testrepo2/subdir/README | 1 + tests-clar/resources/testrepo2/subdir/new.txt | 1 + .../resources/testrepo2/subdir/subdir2/README | 1 + .../testrepo2/subdir/subdir2/new.txt | 1 + 46 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 tests-clar/resources/testrepo2/.gitted/HEAD create mode 100644 tests-clar/resources/testrepo2/.gitted/config create mode 100644 tests-clar/resources/testrepo2/.gitted/description create mode 100644 tests-clar/resources/testrepo2/.gitted/index create mode 100644 tests-clar/resources/testrepo2/.gitted/info/exclude create mode 100644 tests-clar/resources/testrepo2/.gitted/logs/HEAD create mode 100644 tests-clar/resources/testrepo2/.gitted/logs/refs/heads/master create mode 100644 tests-clar/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx create mode 100644 tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack create mode 100644 tests-clar/resources/testrepo2/.gitted/packed-refs create mode 100644 tests-clar/resources/testrepo2/.gitted/refs/heads/master create mode 100644 tests-clar/resources/testrepo2/.gitted/refs/remotes/origin/HEAD create mode 100644 tests-clar/resources/testrepo2/README create mode 100644 tests-clar/resources/testrepo2/new.txt create mode 100644 tests-clar/resources/testrepo2/subdir/README create mode 100644 tests-clar/resources/testrepo2/subdir/new.txt create mode 100644 tests-clar/resources/testrepo2/subdir/subdir2/README create mode 100644 tests-clar/resources/testrepo2/subdir/subdir2/new.txt diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 9d0af579e..c462a4251 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -671,17 +671,20 @@ static int refdb_fs_backend__next(const char **out, git_reference_iterator *_ite { refdb_fs_iter *iter = (refdb_fs_iter *)_iter; - /* First round of checks to make sure where we are */ - if (!iter->loose && iter->k == kh_end(iter->h)) { - if (iter_loose_setup(iter) < 0) - return -1; - iter->loose = 1; + if (iter->loose) + return iter_loose(out, iter); + + if (iter->k != kh_end(iter->h)) { + int error = iter_packed(out, iter); + if (error != GIT_ITEROVER) + return error; } - if (!iter->loose) - return iter_packed(out, iter); - else - return iter_loose(out, iter); + if (iter_loose_setup(iter) < 0) + return -1; + iter->loose = 1; + + return iter_loose(out, iter); } static int loose_write(refdb_fs_backend *backend, const git_reference *ref) diff --git a/tests-clar/refs/branches/foreach.c b/tests-clar/refs/branches/foreach.c index 96a5bc2b9..57f9c0e39 100644 --- a/tests-clar/refs/branches/foreach.c +++ b/tests-clar/refs/branches/foreach.c @@ -24,6 +24,8 @@ void test_refs_branches_foreach__cleanup(void) repo = NULL; cl_fixture_cleanup("testrepo.git"); + + cl_git_sandbox_cleanup(); } static int count_branch_list_cb(const char *branch_name, git_branch_t branch_type, void *payload) @@ -72,14 +74,11 @@ static void assert_branch_has_been_found(struct expectations *findings, const ch { int pos = 0; - while (findings[pos].branch_name) - { + for (pos = 0; findings[pos].branch_name; ++pos) { if (strcmp(expected_branch_name, findings[pos].branch_name) == 0) { cl_assert_equal_i(1, findings[pos].encounters); return; } - - pos++; } cl_fail("expected branch not found in list."); @@ -94,12 +93,9 @@ static int contains_branch_list_cb(const char *branch_name, git_branch_t branch_ exp = (struct expectations *)payload; - while (exp[pos].branch_name) - { + for (pos = 0; exp[pos].branch_name; ++pos) { if (strcmp(branch_name, exp[pos].branch_name) == 0) exp[pos].encounters++; - - pos++; } return 0; @@ -153,3 +149,25 @@ void test_refs_branches_foreach__can_cancel(void) cl_assert_equal_i(5, count); } + +void test_refs_branches_foreach__mix_of_packed_and_loose(void) +{ + struct expectations exp[] = { + { "master", 0 }, + { "origin/HEAD", 0 }, + { "origin/master", 0 }, + { "origin/packed", 0 }, + { NULL, 0 } + }; + git_repository *r2; + + r2 = cl_git_sandbox_init("testrepo2"); + + cl_git_pass(git_branch_foreach(r2, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, + contains_branch_list_cb, &exp)); + + assert_branch_has_been_found(exp, "master"); + assert_branch_has_been_found(exp, "origin/HEAD"); + assert_branch_has_been_found(exp, "origin/master"); + assert_branch_has_been_found(exp, "origin/packed"); +} diff --git a/tests-clar/resources/testrepo2/.gitted/HEAD b/tests-clar/resources/testrepo2/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests-clar/resources/testrepo2/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests-clar/resources/testrepo2/.gitted/config b/tests-clar/resources/testrepo2/.gitted/config new file mode 100644 index 000000000..fc2433caf --- /dev/null +++ b/tests-clar/resources/testrepo2/.gitted/config @@ -0,0 +1,14 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = false +[remote "origin"] + url = https://github.com/libgit2/false.git + fetch = +refs/heads/*:refs/remotes/origin/* +[branch "master"] + remote = origin + merge = refs/heads/master + rebase = true diff --git a/tests-clar/resources/testrepo2/.gitted/description b/tests-clar/resources/testrepo2/.gitted/description new file mode 100644 index 000000000..498b267a8 --- /dev/null +++ b/tests-clar/resources/testrepo2/.gitted/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/tests-clar/resources/testrepo2/.gitted/index b/tests-clar/resources/testrepo2/.gitted/index new file mode 100644 index 0000000000000000000000000000000000000000..b614d0727d53a80dbfcfa54e3ac60c59cb7857a6 GIT binary patch literal 512 zcmZ?q402{*U|<4bw!l7@FF=|BM)NT+urTv_i7+rUE@5C`{0fu;vKd%}TR)|&-yRjV zbl-6&=Y6xys+XMNU|#xza+4U? z^HR(8N-9c_+<|6}IGQ=w-NRd4nv{}Rq>peG++S$sNuZgB-CcZ8^AOI%Wu6q8dDz`2 t3N;Tx84=-5X*6@OyHgC!TqO4 1368278260 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/../../../rugged/test/fixtures/testrepo.git diff --git a/tests-clar/resources/testrepo2/.gitted/logs/refs/heads/master b/tests-clar/resources/testrepo2/.gitted/logs/refs/heads/master new file mode 100644 index 000000000..4e80c69fa --- /dev/null +++ b/tests-clar/resources/testrepo2/.gitted/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 36060c58702ed4c2a40832c51758d5344201d89a Russell Belfer 1368278260 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/../../../rugged/test/fixtures/testrepo.git diff --git a/tests-clar/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD b/tests-clar/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD new file mode 100644 index 000000000..4e80c69fa --- /dev/null +++ b/tests-clar/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 36060c58702ed4c2a40832c51758d5344201d89a Russell Belfer 1368278260 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/../../../rugged/test/fixtures/testrepo.git diff --git a/tests-clar/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d b/tests-clar/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d new file mode 100644 index 0000000000000000000000000000000000000000..bfe146a5a809d2163d2a521b57be55a6459054f6 GIT binary patch literal 134 zcmV;10D1p-0UeA%3c@fD06pgw`vFUm-If$Y1fSpwc0(E~#uT#%{@&EfFqawS436kf z<&mPoD5F?E@*-9!&JH^f1CyVT7{i3J;6!BfyCsOVlvR!P$HxtJmz>^ki&Yh_caHT1 orxyQm$%WVF(33MbAA)m)0pV!9wZ>>_3j3#|);K5g1ApKxugrQtFaQ7m literal 0 HcmV?d00001 diff --git a/tests-clar/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests-clar/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 new file mode 100644 index 0000000000000000000000000000000000000000..cedb2a22e6914c3bbbed90bbedf8fd2095bf5a7d GIT binary patch literal 19 acmb-^#7G-5C`FfcPQQ3!H%bn$g%SfOmF@NI2De~WFK%%kl}eMcVO zI|fyeRFs&PoDrXvnUktlQc=R-y0dwo*>)TDjyrSqY|q)<@TYo1I8Fm*0&IVk`D literal 0 HcmV?d00001 diff --git a/tests-clar/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 b/tests-clar/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 new file mode 100644 index 0000000000000000000000000000000000000000..3cd240db52b878954cf28186a859d716910768e0 GIT binary patch literal 125 zcmV-@0D}K`0hNtg3c@fD0R7G>_5_l2HxEHX=n*`@Ca!J4G|;Sg{T6TF-^|A_rPaD9 zxPT|!M8G9yF +F- \ No newline at end of file diff --git a/tests-clar/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 b/tests-clar/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 new file mode 100644 index 0000000000000000000000000000000000000000..1fd79b47794e916f9873ed60f1cb92815d3295b3 GIT binary patch literal 116 zcmV-)0E_>40V^p=O;s>7FlI0`FfcPQQ3!H%bn$g%5N`dHvVMD1*wTH+ot*d0HmhE8 ziUX=5FST5+q@sl3m*B7G-5C`FfcPQQ3!H%bn$g%5N`dHvVMD1*wTH+ot*d0HmhE8 ziUX=5sVFfoIU_zTGbdHAq@skub!YQFv+XwQ9e3vJ*`Bkz;ZOC3aH!I})N-(rU!EJv Zrz=lf8^K%<@M(E`$>VgnNdSzWFYprfIFkSX literal 0 HcmV?d00001 diff --git a/tests-clar/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 b/tests-clar/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 new file mode 100644 index 0000000000000000000000000000000000000000..3d1016daae43a7a3c163b6385ad15078cdbbcea1 GIT binary patch literal 55 zcmV-70LcG%0V^p=O;s?qU@$Z=Ff%bx2y%6F@paWJsVHF(ZvB+9etT5d(tXFBocGN( Nt6p-70{~G25c)?67YP6W literal 0 HcmV?d00001 diff --git a/tests-clar/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests-clar/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d new file mode 100644 index 0000000000000000000000000000000000000000..2f9b6b6e3d9250ba09360734aa47973a993b59d1 GIT binary patch literal 82 zcmV-Y0ImOc0V^p=O;s?nWH2-^Ff%bx2y%6F@pWZbp=_w|ZEZn+i*1|CqwPw4M;_lh o233)lTCP`8QNplXwC&*i7t3XG_U8!Ackl^w`fs=w02$OB|A$m1)Bpeg literal 0 HcmV?d00001 diff --git a/tests-clar/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests-clar/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 new file mode 100644 index 0000000000000000000000000000000000000000..5df58dda56789631c78aeed62708e1b694440195 GIT binary patch literal 126 zcmV-^0D=E_0iBK82?8+?0R2uC+kmpkZXSY&U

    RiSaIAE^xQ@?_ml44FkiJ(R)*{ z(H(TH6>PFd5&0~h#n$X!k{LPpBqYvbW+w8_Xyl{wSm9BID%@u&V}Z+7esG(*wD+lu geg*3yQ9w!oju;WmZug_se_Eq;)3!|J3!n-%%(!(uEdT%j literal 0 HcmV?d00001 diff --git a/tests-clar/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests-clar/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a new file mode 100644 index 000000000..a79612435 --- /dev/null +++ b/tests-clar/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a @@ -0,0 +1,3 @@ +x[ +0E*fդ "W0-Ft݁pS[Yx^ +Db CLhut}8X*4ZsYUA X3RM) s6輢Mរ&Jm;}<\@ޏpĀv?jۺL?H \ No newline at end of file diff --git a/tests-clar/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests-clar/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f new file mode 100644 index 000000000..f8588696b --- /dev/null +++ b/tests-clar/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f @@ -0,0 +1,2 @@ +x;j1Dmdǎ|M3`V{ >QvL0I?!4Z=!צ8F!rsQy9]$D&l6A>jFWҵ IKNiZ%S + U~̽>' w [ DGڡQ-M>dO}\8g_ШoYr \ No newline at end of file diff --git a/tests-clar/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests-clar/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd new file mode 100644 index 0000000000000000000000000000000000000000..d0d7e736e536a41bcb885005f8bf258c61cad682 GIT binary patch literal 28 kcmbBQQQ6W+Sv9;eTEK4oHX{LN+y0Ic;3tpET3 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests-clar/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 new file mode 100644 index 0000000000000000000000000000000000000000..18a7f61c29ea8c5c9a48e3b30bead7f058d06293 GIT binary patch literal 26 icmb40V^p=O;s>7Fk&z?FfcPQQ3!H%bn$g%5N`dHvVMD1*wTH+ot*d0HmhE8 ziUX=5FST5+q@sl3m*B=Z5m>$`jW{Fc$=TS{`5WI9+ZM01*fsda7_Ea{vGU literal 0 HcmV?d00001 diff --git a/tests-clar/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests-clar/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 new file mode 100644 index 0000000000000000000000000000000000000000..112998d425717bb922ce74e8f6f0f831d8dc4510 GIT binary patch literal 24 gcmbYw4{Y+*Z5}2Csyees*{;KHB55dV&?dF#`kRGN5~TfOsX4T@J*P yfLQIszKM4h+|MXn`snRCm0er%Quq9yFn5~doV&qSCs?ySKaj$-^Vag-Df$4qg=3fi literal 0 HcmV?d00001 diff --git a/tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack new file mode 100644 index 0000000000000000000000000000000000000000..74c7fe4f3a657d606a4a004c87336749079c0edf GIT binary patch literal 491 zcmWG=boORoU|<4bwrPA7bLRHNavd@dXt`h9wYOo@&ulXVr3x1R33=PLWk|2oj=R6e z{6ngQ-HiT=p)71gtxNdtSc@#my7+iQ>e4qWip{2_EIvQCa!ROJYez<`n&YvUtTcI* z9TTeiTBZe0nUf^pW%6`sa`eMPzM|)nA8tGF@Z|lXUFTAFz2AA(eS_Z5b4eUxWitfM zx;^&{{dY4@|EHR09p8m=V|lCdF3HolfsX5m=3+ABVfbI&731*Idp5t|LFI}jHQzg~ zyQiAoi@zxS!<^^L4J{_ighMw%nza;7mv(7p=6M|PE%R{fdiLnm>17)awYE#nS$-B0AgjUCm@D?V9#TFX)~ z$JoTcz}PU*66+=E@hWo`*7*&jWecg0;xw=p8Q#< znGm{LIG-cz>zd?K^@r5dt1_-_DeboX%y8$7^v#Wo6?0bm=)z13@;H6Q^C=U9)d4m= z>xwzr=RI@+K|))fGcic~WZt)&iFo@NUWM`6pV&9?&Vu_Hg-aj3U8k~ZOJ3@p{}TZ5 CmFm|3 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/testrepo2/.gitted/packed-refs b/tests-clar/resources/testrepo2/.gitted/packed-refs new file mode 100644 index 000000000..97ea6a848 --- /dev/null +++ b/tests-clar/resources/testrepo2/.gitted/packed-refs @@ -0,0 +1,6 @@ +# pack-refs with: peeled fully-peeled +36060c58702ed4c2a40832c51758d5344201d89a refs/remotes/origin/master +41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/remotes/origin/packed +5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/tags/v0.9 +0c37a5391bbff43c37f0d0371823a5509eed5b1d refs/tags/v1.0 +^5b5b025afb0b4c913b4c338a42934a3863bf3644 diff --git a/tests-clar/resources/testrepo2/.gitted/refs/heads/master b/tests-clar/resources/testrepo2/.gitted/refs/heads/master new file mode 100644 index 000000000..a7eafce3c --- /dev/null +++ b/tests-clar/resources/testrepo2/.gitted/refs/heads/master @@ -0,0 +1 @@ +36060c58702ed4c2a40832c51758d5344201d89a diff --git a/tests-clar/resources/testrepo2/.gitted/refs/remotes/origin/HEAD b/tests-clar/resources/testrepo2/.gitted/refs/remotes/origin/HEAD new file mode 100644 index 000000000..6efe28fff --- /dev/null +++ b/tests-clar/resources/testrepo2/.gitted/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +ref: refs/remotes/origin/master diff --git a/tests-clar/resources/testrepo2/README b/tests-clar/resources/testrepo2/README new file mode 100644 index 000000000..1385f264a --- /dev/null +++ b/tests-clar/resources/testrepo2/README @@ -0,0 +1 @@ +hey diff --git a/tests-clar/resources/testrepo2/new.txt b/tests-clar/resources/testrepo2/new.txt new file mode 100644 index 000000000..fa49b0779 --- /dev/null +++ b/tests-clar/resources/testrepo2/new.txt @@ -0,0 +1 @@ +new file diff --git a/tests-clar/resources/testrepo2/subdir/README b/tests-clar/resources/testrepo2/subdir/README new file mode 100644 index 000000000..1385f264a --- /dev/null +++ b/tests-clar/resources/testrepo2/subdir/README @@ -0,0 +1 @@ +hey diff --git a/tests-clar/resources/testrepo2/subdir/new.txt b/tests-clar/resources/testrepo2/subdir/new.txt new file mode 100644 index 000000000..fa49b0779 --- /dev/null +++ b/tests-clar/resources/testrepo2/subdir/new.txt @@ -0,0 +1 @@ +new file diff --git a/tests-clar/resources/testrepo2/subdir/subdir2/README b/tests-clar/resources/testrepo2/subdir/subdir2/README new file mode 100644 index 000000000..1385f264a --- /dev/null +++ b/tests-clar/resources/testrepo2/subdir/subdir2/README @@ -0,0 +1 @@ +hey diff --git a/tests-clar/resources/testrepo2/subdir/subdir2/new.txt b/tests-clar/resources/testrepo2/subdir/subdir2/new.txt new file mode 100644 index 000000000..fa49b0779 --- /dev/null +++ b/tests-clar/resources/testrepo2/subdir/subdir2/new.txt @@ -0,0 +1 @@ +new file From 000e68961cb44b04435a733e151f8b1357547bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 12 May 2013 15:35:02 +0200 Subject: [PATCH 218/384] CMake: don't try to use bundled zlib when the system's path is in the cache The code surrounding zlib bundling did not take into consideration that ZLIB_LIBRARY gets cached, and assumed that FIND(ZLIB) would always set ZLIB_FOUND, which does not hold true, as this variable signifies that we have found the package and had to look at the system, as its location was not cached. Only use the bundled sources if the external zlib is neither newly-found nor cached. --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a228e342..402ff2226 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,7 +131,9 @@ ENDIF() IF (ZLIB_FOUND) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) LINK_LIBRARIES(${ZLIB_LIBRARIES}) -ELSE() + # Fake the message CMake would have shown + MESSAGE("-- Found zlib: ${ZLIB_LIBRARY}") +ELSEIF (NOT ZLIB_LIBRARY) MESSAGE( "zlib was not found; using bundled 3rd-party sources." ) INCLUDE_DIRECTORIES(deps/zlib) ADD_DEFINITIONS(-DNO_VIZ -DSTDC -DNO_GZIP) From b4d33e46c4b00d36783f5b75c1316b7a74c4da20 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Tue, 14 May 2013 21:54:26 +0200 Subject: [PATCH 219/384] Fix some memory leaks --- tests-clar/diff/rename.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 8134cb6b7..1349d4013 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -443,4 +443,6 @@ void test_diff_rename__patch(void) cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status); git_diff_list_free(diff); + git_tree_free(old_tree); + git_tree_free(new_tree); } From fbcab44b208ae726450289794ecf8e69af3f7e58 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Tue, 14 May 2013 16:03:09 -0400 Subject: [PATCH 220/384] Create directory for symlink before creating symlink --- src/checkout.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index e9ec2bdab..6835db073 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -764,11 +764,14 @@ cleanup: } static int blob_content_to_link( - struct stat *st, git_blob *blob, const char *path, int can_symlink) + struct stat *st, git_blob *blob, const char *path, mode_t dir_mode, int can_symlink) { git_buf linktarget = GIT_BUF_INIT; int error; + if ((error = git_futils_mkpath2file(path, dir_mode)) < 0) + return error; + if ((error = git_blob__getbuf(&linktarget, blob)) < 0) return error; @@ -914,7 +917,7 @@ static int checkout_blob( if (S_ISLNK(file->mode)) error = blob_content_to_link( - &st, blob, git_buf_cstr(&data->path), data->can_symlink); + &st, blob, git_buf_cstr(&data->path), data->opts.dir_mode, data->can_symlink); else error = blob_content_to_file( &st, blob, git_buf_cstr(&data->path), file->mode, &data->opts); From 3bbc87d697b24725beb8dcd41fa83ca604d3a9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 14 May 2013 23:09:26 +0200 Subject: [PATCH 221/384] CMake: allow appending a string to the library filename This helps us install multiple versions of the library side-by-side. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 402ff2226..1ffe1ddd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ OPTION( BUILD_EXAMPLES "Build library usage example apps" OFF ) OPTION( TAGS "Generate tags" OFF ) OPTION( PROFILE "Generate profiling information" OFF ) OPTION( ENABLE_TRACE "Enables tracing support" OFF ) +OPTION( SONAME_APPEND "Append the given string to the library's filename" OFF ) IF(MSVC) # This option is only availalbe when building with MSVC. By default, # libgit2 is build using the stdcall calling convention, as that's what @@ -304,6 +305,9 @@ MSVC_SPLIT_SOURCES(git2) IF (SONAME) SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING}) SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_VERSION_MAJOR}) + IF (SONAME_APPEND) + SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME "git2-${SONAME_APPEND}") + ENDIF() ENDIF() CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY) From bc2020d64869aa19a88b71aee33a24b54c178dab Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 14 May 2013 16:39:19 -0700 Subject: [PATCH 222/384] Extend valgrind suppressions on Mac --- tests-clar/valgrind-supp-mac.txt | 72 ++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests-clar/valgrind-supp-mac.txt b/tests-clar/valgrind-supp-mac.txt index 03e60dcd7..297b11e62 100644 --- a/tests-clar/valgrind-supp-mac.txt +++ b/tests-clar/valgrind-supp-mac.txt @@ -80,3 +80,75 @@ ... fun:puts } +{ + mac-ssl-uninitialized-1 + Memcheck:Cond + obj:/usr/lib/libcrypto.0.9.8.dylib + ... + fun:ssl23_connect +} +{ + mac-ssl-uninitialized-2 + Memcheck:Cond + ... + obj:/usr/lib/libssl.0.9.8.dylib + ... + fun:ssl23_connect +} +{ + mac-ssl-uninitialized-3 + Memcheck:Value8 + obj:/usr/lib/libcrypto.0.9.8.dylib + ... + fun:ssl23_connect +} +{ + mac-ssl-uninitialized-4 + Memcheck:Param + ... + obj:/usr/lib/libcrypto.0.9.8.dylib + ... + fun:ssl23_connect +} +{ + mac-ssl-leak-1 + Memcheck:Leak + fun:malloc + fun:CRYPTO_malloc + ... + fun:ERR_load_strings +} +{ + mac-ssl-leak-2 + Memcheck:Leak + fun:malloc + fun:CRYPTO_malloc + ... + fun:SSL_library_init +} +{ + mac-ssl-leak-3 + Memcheck:Leak + fun:malloc + fun:strdup + ... + fun:si_module_with_name + fun:getaddrinfo +} +{ + mac-ssl-leak-4 + Memcheck:Leak + fun:malloc + fun:CRYPTO_malloc + ... + fun:ssl3_get_server_certificate +} +{ + clar-printf-buf + Memcheck:Leak + fun:malloc + fun:__smakebuf + ... + fun:printf + fun:clar_print_init +} From 0cb16fe924fb5c4e58866c28b06ace876e2dcbd3 Mon Sep 17 00:00:00 2001 From: Linquize Date: Wed, 15 May 2013 20:26:55 +0800 Subject: [PATCH 223/384] Unify whitespaces to tabs --- include/git2/attr.h | 2 +- include/git2/config.h | 16 ++--- include/git2/inttypes.h | 18 +++--- include/git2/merge.h | 2 +- include/git2/repository.h | 10 +-- include/git2/reset.h | 6 +- include/git2/strarray.h | 4 +- include/git2/sys/refdb_backend.h | 2 +- src/attr.c | 8 +-- src/attr_file.h | 6 +- src/checkout.c | 2 +- src/config.c | 6 +- src/date.c | 6 +- src/diff_output.c | 30 ++++----- src/hash/hash_generic.h | 6 +- src/hash/hash_win32.h | 8 +-- src/merge.h | 28 ++++---- src/notes.c | 38 +++++------ src/refdb_fs.c | 4 +- src/repository.c | 10 +-- src/transports/local.c | 2 +- src/util.h | 8 +-- src/win32/posix_w32.c | 106 +++++++++++++++---------------- 23 files changed, 164 insertions(+), 164 deletions(-) diff --git a/include/git2/attr.h b/include/git2/attr.h index f099245b0..0d8a910f2 100644 --- a/include/git2/attr.h +++ b/include/git2/attr.h @@ -141,7 +141,7 @@ GIT_EXTERN(git_attr_t) git_attr_value(const char *attr); */ GIT_EXTERN(int) git_attr_get( const char **value_out, - git_repository *repo, + git_repository *repo, uint32_t flags, const char *path, const char *name); diff --git a/include/git2/config.h b/include/git2/config.h index 724788ae0..8d1a1a5a6 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -190,9 +190,9 @@ GIT_EXTERN(int) git_config_open_ondisk(git_config **out, const char *path); * multi-level parent config, or an error code */ GIT_EXTERN(int) git_config_open_level( - git_config **out, - const git_config *parent, - unsigned int level); + git_config **out, + const git_config *parent, + unsigned int level); /** * Open the global/XDG configuration file according to git's rules @@ -459,11 +459,11 @@ GIT_EXTERN(int) git_config_foreach_match( * @return 0 on success, error code otherwise */ GIT_EXTERN(int) git_config_get_mapped( - int *out, - const git_config *cfg, - const char *name, - const git_cvar_map *maps, - size_t map_n); + int *out, + const git_config *cfg, + const char *name, + const git_cvar_map *maps, + size_t map_n); /** * Maps a string value to an integer constant diff --git a/include/git2/inttypes.h b/include/git2/inttypes.h index 716084219..17364c7f8 100644 --- a/include/git2/inttypes.h +++ b/include/git2/inttypes.h @@ -283,18 +283,18 @@ _inline #endif // STATIC_IMAXDIV ] imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) { - imaxdiv_t result; + imaxdiv_t result; - result.quot = numer / denom; - result.rem = numer % denom; + result.quot = numer / denom; + result.rem = numer % denom; - if (numer < 0 && result.rem > 0) { - // did division wrong; must fix up - ++result.quot; - result.rem -= denom; - } + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } - return result; + return result; } // 7.8.2.3 The strtoimax and strtoumax functions diff --git a/include/git2/merge.h b/include/git2/merge.h index 8ca90b95f..738d8e028 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -54,7 +54,7 @@ typedef struct { */ unsigned int target_limit; - /** Pluggable similarity metric; pass NULL to use internal metric */ + /** Pluggable similarity metric; pass NULL to use internal metric */ git_diff_similarity_metric *metric; /** Flags for automerging content. */ diff --git a/include/git2/repository.h b/include/git2/repository.h index e0464c63f..bb2b3db83 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -545,11 +545,11 @@ GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo, * applied when calculating the hash. */ GIT_EXTERN(int) git_repository_hashfile( - git_oid *out, - git_repository *repo, - const char *path, - git_otype type, - const char *as_path); + git_oid *out, + git_repository *repo, + const char *path, + git_otype type, + const char *as_path); /** * Make the repository HEAD point to the specified reference. diff --git a/include/git2/reset.h b/include/git2/reset.h index c7c951942..c36781722 100644 --- a/include/git2/reset.h +++ b/include/git2/reset.h @@ -72,9 +72,9 @@ GIT_EXTERN(int) git_reset( * @return 0 on success or an error code < 0 */ GIT_EXTERN(int) git_reset_default( - git_repository *repo, - git_object *target, - git_strarray* pathspecs); + git_repository *repo, + git_object *target, + git_strarray* pathspecs); /** @} */ GIT_END_DECL diff --git a/include/git2/strarray.h b/include/git2/strarray.h index d338eb7ad..86fa25f3f 100644 --- a/include/git2/strarray.h +++ b/include/git2/strarray.h @@ -20,8 +20,8 @@ GIT_BEGIN_DECL /** Array of strings */ typedef struct git_strarray { - char **strings; - size_t count; + char **strings; + size_t count; } git_strarray; /** diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 8dbf38ca9..548597fbc 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -39,7 +39,7 @@ struct git_reference_iterator { /** An instance for a custom backend */ struct git_refdb_backend { - unsigned int version; + unsigned int version; /** * Queries the refdb backend to determine if the given ref_name diff --git a/src/attr.c b/src/attr.c index 6dd2c7e2f..9fe4471f6 100644 --- a/src/attr.c +++ b/src/attr.c @@ -36,7 +36,7 @@ static int collect_attr_files( int git_attr_get( const char **value, - git_repository *repo, + git_repository *repo, uint32_t flags, const char *pathname, const char *name) @@ -88,10 +88,10 @@ typedef struct { int git_attr_get_many( const char **values, - git_repository *repo, + git_repository *repo, uint32_t flags, const char *pathname, - size_t num_attr, + size_t num_attr, const char **names) { int error; @@ -151,7 +151,7 @@ cleanup: int git_attr_foreach( - git_repository *repo, + git_repository *repo, uint32_t flags, const char *pathname, int (*callback)(const char *name, const char *value, void *payload), diff --git a/src/attr_file.h b/src/attr_file.h index d8abcda58..15bba1c6a 100644 --- a/src/attr_file.h +++ b/src/attr_file.h @@ -47,14 +47,14 @@ typedef struct { typedef struct { git_refcount unused; const char *name; - uint32_t name_hash; + uint32_t name_hash; } git_attr_name; typedef struct { git_refcount rc; /* for macros */ char *name; - uint32_t name_hash; - const char *value; + uint32_t name_hash; + const char *value; } git_attr_assignment; typedef struct { diff --git a/src/checkout.c b/src/checkout.c index 6835db073..68028dfef 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -770,7 +770,7 @@ static int blob_content_to_link( int error; if ((error = git_futils_mkpath2file(path, dir_mode)) < 0) - return error; + return error; if ((error = git_blob__getbuf(&linktarget, blob)) < 0) return error; diff --git a/src/config.c b/src/config.c index bd629f7c4..dc8c7024e 100644 --- a/src/config.c +++ b/src/config.c @@ -236,9 +236,9 @@ int git_config_open_global(git_config **cfg_out, git_config *cfg) } int git_config_open_level( - git_config **cfg_out, - const git_config *cfg_parent, - unsigned int level) + git_config **cfg_out, + const git_config *cfg_parent, + unsigned int level) { git_config *cfg; file_internal *internal; diff --git a/src/date.c b/src/date.c index ce1721a0b..48841e4f9 100644 --- a/src/date.c +++ b/src/date.c @@ -823,8 +823,8 @@ static void pending_number(struct tm *tm, int *num) } static git_time_t approxidate_str(const char *date, - const struct timeval *tv, - int *error_ret) + const struct timeval *tv, + int *error_ret) { int number = 0; int touched = 0; @@ -866,7 +866,7 @@ int git__date_parse(git_time_t *out, const char *date) int offset, error_ret=0; if (!parse_date_basic(date, ×tamp, &offset)) { - *out = timestamp; + *out = timestamp; return 0; } diff --git a/src/diff_output.c b/src/diff_output.c index 07fcf47a7..be1ff56e7 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -1712,12 +1712,12 @@ notfound: } static int print_to_buffer_cb( - const git_diff_delta *delta, - const git_diff_range *range, - char line_origin, - const char *content, - size_t content_len, - void *payload) + const git_diff_delta *delta, + const git_diff_range *range, + char line_origin, + const char *content, + size_t content_len, + void *payload) { git_buf *output = payload; GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin); @@ -1797,16 +1797,16 @@ int git_diff__paired_foreach( i_max = idx2head ? idx2head->deltas.length : 0; j_max = wd2idx ? wd2idx->deltas.length : 0; - /* Get appropriate strcmp function */ - strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL; + /* Get appropriate strcmp function */ + strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL; - /* Assert both iterators use matching ignore-case. If this function ever - * supports merging diffs that are not sorted by the same function, then - * it will need to spool and sort on one of the results before merging - */ - if (idx2head && wd2idx) { - assert(idx2head->strcomp == wd2idx->strcomp); - } + /* Assert both iterators use matching ignore-case. If this function ever + * supports merging diffs that are not sorted by the same function, then + * it will need to spool and sort on one of the results before merging + */ + if (idx2head && wd2idx) { + assert(idx2head->strcomp == wd2idx->strcomp); + } for (i = 0, j = 0; i < i_max || j < j_max; ) { i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL; diff --git a/src/hash/hash_generic.h b/src/hash/hash_generic.h index b731de8b3..6b60c98c4 100644 --- a/src/hash/hash_generic.h +++ b/src/hash/hash_generic.h @@ -11,9 +11,9 @@ #include "hash.h" struct git_hash_ctx { - unsigned long long size; - unsigned int H[5]; - unsigned int W[16]; + unsigned long long size; + unsigned int H[5]; + unsigned int W[16]; }; #define git_hash_global_init() 0 diff --git a/src/hash/hash_win32.h b/src/hash/hash_win32.h index daa769b59..2eee5ca79 100644 --- a/src/hash/hash_win32.h +++ b/src/hash/hash_win32.h @@ -48,10 +48,10 @@ struct hash_cryptoapi_prov { /* Function declarations for CNG */ typedef NTSTATUS (WINAPI *hash_win32_cng_open_algorithm_provider_fn)( - HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm, - LPCWSTR pszAlgId, - LPCWSTR pszImplementation, - DWORD dwFlags); + HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm, + LPCWSTR pszAlgId, + LPCWSTR pszImplementation, + DWORD dwFlags); typedef NTSTATUS (WINAPI *hash_win32_cng_get_property_fn)( HANDLE /* BCRYPT_HANDLE */ hObject, diff --git a/src/merge.h b/src/merge.h index 6307d1569..da1fdf472 100644 --- a/src/merge.h +++ b/src/merge.h @@ -70,41 +70,41 @@ typedef enum { typedef struct { - git_repository *repo; - git_pool pool; + git_repository *repo; + git_pool pool; - /* Vector of git_index_entry that represent the merged items that + /* Vector of git_index_entry that represent the merged items that * have been staged, either because only one side changed, or because * the two changes were non-conflicting and mergeable. These items * will be written as staged entries in the main index. */ - git_vector staged; + git_vector staged; - /* Vector of git_merge_diff entries that represent the conflicts that + /* Vector of git_merge_diff entries that represent the conflicts that * have not been automerged. These items will be written to high-stage * entries in the main index. */ - git_vector conflicts; + git_vector conflicts; - /* Vector of git_merge_diff that have been automerged. These items + /* Vector of git_merge_diff that have been automerged. These items * will be written to the REUC when the index is produced. */ - git_vector resolved; + git_vector resolved; } git_merge_diff_list; /** * Description of changes to one file across three trees. */ typedef struct { - git_merge_diff_type_t type; + git_merge_diff_type_t type; - git_index_entry ancestor_entry; + git_index_entry ancestor_entry; - git_index_entry our_entry; - git_delta_t our_status; + git_index_entry our_entry; + git_delta_t our_status; - git_index_entry their_entry; - git_delta_t their_status; + git_index_entry their_entry; + git_delta_t their_status; } git_merge_diff; int git_merge__bases_many( diff --git a/src/notes.c b/src/notes.c index 4ca2aaa63..3e3db58db 100644 --- a/src/notes.c +++ b/src/notes.c @@ -579,30 +579,30 @@ cleanup: } int git_note_foreach( - git_repository *repo, - const char *notes_ref, - git_note_foreach_cb note_cb, - void *payload) + git_repository *repo, + const char *notes_ref, + git_note_foreach_cb note_cb, + void *payload) { - int error; - git_note_iterator *iter = NULL; - git_oid note_id, annotated_id; + int error; + git_note_iterator *iter = NULL; + git_oid note_id, annotated_id; - if ((error = git_note_iterator_new(&iter, repo, notes_ref)) < 0) - return error; + if ((error = git_note_iterator_new(&iter, repo, notes_ref)) < 0) + return error; - while (!(error = git_note_next(¬e_id, &annotated_id, iter))) { - if (note_cb(¬e_id, &annotated_id, payload)) { - error = GIT_EUSER; - break; - } - } + while (!(error = git_note_next(¬e_id, &annotated_id, iter))) { + if (note_cb(¬e_id, &annotated_id, payload)) { + error = GIT_EUSER; + break; + } + } - if (error == GIT_ITEROVER) - error = 0; + if (error == GIT_ITEROVER) + error = 0; - git_note_iterator_free(iter); - return error; + git_note_iterator_free(iter); + return error; } diff --git a/src/refdb_fs.c b/src/refdb_fs.c index c462a4251..b9df83f81 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -257,8 +257,8 @@ static int packed_load(refdb_fs_backend *backend) if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0) goto parse_failed; } else if (backend->peeling_mode == PEELING_FULL || - (backend->peeling_mode == PEELING_STANDARD && - git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0)) { + (backend->peeling_mode == PEELING_STANDARD && + git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0)) { ref->flags |= PACKREF_CANNOT_PEEL; } diff --git a/src/repository.c b/src/repository.c index 4ab3921bb..9957f32b7 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1636,11 +1636,11 @@ int git_repository_message_remove(git_repository *repo) } int git_repository_hashfile( - git_oid *out, - git_repository *repo, - const char *path, - git_otype type, - const char *as_path) + git_oid *out, + git_repository *repo, + const char *path, + git_otype type, + const char *as_path) { int error; git_vector filters = GIT_VECTOR_INIT; diff --git a/src/transports/local.c b/src/transports/local.c index bd3bf93bf..2530a847f 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -495,7 +495,7 @@ static int local_download_pack( /* Tag or some other wanted object. Add it on its own */ error = git_packbuilder_insert(pack, &rhead->oid, rhead->name); } - git_object_free(obj); + git_object_free(obj); } /* Walk the objects, building a packfile */ diff --git a/src/util.h b/src/util.h index 687afe084..6f876d012 100644 --- a/src/util.h +++ b/src/util.h @@ -262,22 +262,22 @@ GIT_INLINE(size_t) git__size_t_powerof2(size_t v) GIT_INLINE(bool) git__isupper(int c) { - return (c >= 'A' && c <= 'Z'); + return (c >= 'A' && c <= 'Z'); } GIT_INLINE(bool) git__isalpha(int c) { - return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); + return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); } GIT_INLINE(bool) git__isdigit(int c) { - return (c >= '0' && c <= '9'); + return (c >= '0' && c <= '9'); } GIT_INLINE(bool) git__isspace(int c) { - return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */); + return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */); } GIT_INLINE(bool) git__iswildcard(int c) diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 52eb4638a..b9115836e 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -479,29 +479,29 @@ int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags) * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that */ -struct tm * -p_localtime_r (const time_t *timer, struct tm *result) -{ - struct tm *local_result; - local_result = localtime (timer); +struct tm * +p_localtime_r (const time_t *timer, struct tm *result) +{ + struct tm *local_result; + local_result = localtime (timer); - if (local_result == NULL || result == NULL) - return NULL; + if (local_result == NULL || result == NULL) + return NULL; - memcpy (result, local_result, sizeof (struct tm)); - return result; -} -struct tm * -p_gmtime_r (const time_t *timer, struct tm *result) -{ - struct tm *local_result; - local_result = gmtime (timer); + memcpy (result, local_result, sizeof (struct tm)); + return result; +} +struct tm * +p_gmtime_r (const time_t *timer, struct tm *result) +{ + struct tm *local_result; + local_result = gmtime (timer); - if (local_result == NULL || result == NULL) - return NULL; + if (local_result == NULL || result == NULL) + return NULL; - memcpy (result, local_result, sizeof (struct tm)); - return result; + memcpy (result, local_result, sizeof (struct tm)); + return result; } #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) @@ -514,44 +514,44 @@ p_gmtime_r (const time_t *timer, struct tm *result) #define _TIMEZONE_DEFINED struct timezone { - int tz_minuteswest; /* minutes W of Greenwich */ - int tz_dsttime; /* type of dst correction */ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ }; #endif - + int p_gettimeofday(struct timeval *tv, struct timezone *tz) { - FILETIME ft; - unsigned __int64 tmpres = 0; - static int tzflag; - - if (NULL != tv) - { - GetSystemTimeAsFileTime(&ft); - - tmpres |= ft.dwHighDateTime; - tmpres <<= 32; - tmpres |= ft.dwLowDateTime; - - /*converting file time to unix epoch*/ - tmpres /= 10; /*convert into microseconds*/ - tmpres -= DELTA_EPOCH_IN_MICROSECS; - tv->tv_sec = (long)(tmpres / 1000000UL); - tv->tv_usec = (long)(tmpres % 1000000UL); - } - - if (NULL != tz) - { - if (!tzflag) - { - _tzset(); - tzflag++; - } - tz->tz_minuteswest = _timezone / 60; - tz->tz_dsttime = _daylight; - } - - return 0; + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + + if (NULL != tv) + { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch*/ + tmpres /= 10; /*convert into microseconds*/ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) + { + if (!tzflag) + { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; } int p_inet_pton(int af, const char* src, void* dst) From f0ab73720a4e7a9b37c901a27519ea65eafeb8a6 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 15 May 2013 17:51:57 +0200 Subject: [PATCH 224/384] signature: Lenient when dupping, strict when creating --- src/signature.c | 19 ++++++++++++++----- tests-clar/commit/signature.c | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/signature.c b/src/signature.c index 649dbcd3d..48bdd81ab 100644 --- a/src/signature.c +++ b/src/signature.c @@ -66,10 +66,12 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema p->name = extract_trimmed(name, strlen(name)); p->email = extract_trimmed(email, strlen(email)); - if (p->name == NULL || p->email == NULL || - p->name[0] == '\0' || p->email[0] == '\0') { + if (p->name == NULL || p->email == NULL) + return -1; /* oom */ + + if (p->name[0] == '\0') { git_signature_free(p); - return signature_error("Empty name or email"); + return signature_error("Signature cannot have an empty name"); } p->when.time = time; @@ -81,9 +83,16 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema git_signature *git_signature_dup(const git_signature *sig) { - git_signature *new; - if (git_signature_new(&new, sig->name, sig->email, sig->when.time, sig->when.offset) < 0) + git_signature *new = git__calloc(1, sizeof(git_signature)); + + if (new == NULL) return NULL; + + new->name = git__strdup(sig->name); + new->email = git__strdup(sig->email); + new->when.time = sig->when.time; + new->when.offset = sig->when.offset; + return new; } diff --git a/tests-clar/commit/signature.c b/tests-clar/commit/signature.c index 9364efb10..aef72a80d 100644 --- a/tests-clar/commit/signature.c +++ b/tests-clar/commit/signature.c @@ -54,8 +54,8 @@ void test_commit_signature__create_empties(void) cl_git_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60)); cl_git_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60)); - cl_git_fail(try_build_signature("nulltoken", "", 1234567890, 60)); - cl_git_fail(try_build_signature("nulltoken", " ", 1234567890, 60)); + cl_git_pass(try_build_signature("nulltoken", "", 1234567890, 60)); + cl_git_pass(try_build_signature("nulltoken", " ", 1234567890, 60)); } void test_commit_signature__create_one_char(void) From 22011b33daf1a722ae1f522b32e188dccb21ac42 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Wed, 15 May 2013 12:38:40 -0400 Subject: [PATCH 225/384] Cleanup --- src/transports/ssh.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index f11b5fb67..f04adf582 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -375,12 +375,8 @@ static int _git_ssh_setup_conn( t->current_stream = s; git__free(host); git__free(port); - if (user) { - git__free(user); - } - if (pass) { - git__free(pass); - } + git__free(user); + git__free(pass); return 0; @@ -389,12 +385,10 @@ on_error: ssh_stream_free(*stream); git__free(host); - if (port) - git__free(port); - if (user) - git__free(user); - if (pass) - git__free(pass); + git__free(port); + git__free(user); + git__free(pass); + if (session) libssh2_session_free(session), session = NULL; From b54ed3efe88801df17ba7ceff40887dfabed0338 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Wed, 15 May 2013 12:41:16 -0400 Subject: [PATCH 226/384] Added error check --- src/transports/ssh.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index f04adf582..b88b84817 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -342,7 +342,8 @@ static int _git_ssh_setup_conn( goto on_error; if (user && pass) { - git_cred_userpass_plaintext_new(&t->cred, user, pass); + if (git_cred_userpass_plaintext_new(&t->cred, user, pass) < 0) + goto on_error; } else { if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, From e057e41122cbcfef60b7ab0283cbcda44df538c7 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Wed, 15 May 2013 12:44:51 -0400 Subject: [PATCH 227/384] Reworked git_cred_ssh_keyfile_passphrase_new method --- src/transports/cred.c | 44 +++++++++++-------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/src/transports/cred.c b/src/transports/cred.c index e4d205ea2..bc37d84ae 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -90,48 +90,26 @@ int git_cred_ssh_keyfile_passphrase_new( { git_cred_ssh_keyfile_passphrase *c; - if (!cred) - return -1; + assert(cred && privatekey); - c = git__malloc(sizeof(git_cred_ssh_keyfile_passphrase)); + c = git__calloc(1, sizeof(git_cred_ssh_keyfile_passphrase)); GITERR_CHECK_ALLOC(c); c->parent.credtype = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE; c->parent.free = ssh_keyfile_passphrase_free; c->privatekey = git__strdup(privatekey); - - if (!c->privatekey) { - git__free(c); - return -1; - } + GITERR_CHECK_ALLOC(c->privatekey); if (publickey) { - c->publickey = git__strdup(publickey); - - if (!c->publickey) { - git__free(c->privatekey); - git__free(c); - return -1; - } - } else { - c->publickey = NULL; - } - - if (passphrase) { - c->passphrase = git__strdup(passphrase); - - if (!c->passphrase) { - git__free(c->privatekey); - if (c->publickey) { - git__free(c->publickey); - } - git__free(c); - return -1; - } - } else { - c->passphrase = NULL; - } + c->publickey = git__strdup(publickey); + GITERR_CHECK_ALLOC(c->publickey); + } + + if (passphrase) { + c->passphrase = git__strdup(passphrase); + GITERR_CHECK_ALLOC(c->passphrase); + } *cred = &c->parent; return 0; From ccaee222a10226266550ddcdb364c7a300ebb740 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Wed, 15 May 2013 12:46:33 -0400 Subject: [PATCH 228/384] Added GITERR_CHECK_ALLOC --- src/transports/cred.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/transports/cred.c b/src/transports/cred.c index bc37d84ae..4916c6e18 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -148,15 +148,11 @@ int git_cred_ssh_publickey_new( c->parent.free = ssh_publickey_free; c->publickey = git__malloc(publickey_len); + GITERR_CHECK_ALLOC(c->publickey); + memcpy(c->publickey, publickey, publickey_len); - if (!c->publickey) { - git__free(c); - return -1; - } - c->publickey_len = publickey_len; - c->sign_callback = sign_callback; c->sign_data = sign_data; From 6cbbd739356f20c681d541438fdbad6027e0eef5 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Wed, 15 May 2013 12:48:43 -0400 Subject: [PATCH 229/384] Renamed FindLibSSH2.cmake --- cmake/Modules/{FindLibSSH2.cmake => FindLibSSH2.cmake.tmp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmake/Modules/{FindLibSSH2.cmake => FindLibSSH2.cmake.tmp} (100%) diff --git a/cmake/Modules/FindLibSSH2.cmake b/cmake/Modules/FindLibSSH2.cmake.tmp similarity index 100% rename from cmake/Modules/FindLibSSH2.cmake rename to cmake/Modules/FindLibSSH2.cmake.tmp From a6b79b9bdcbfc2c53931f49e9be5cf4d44f61d37 Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Wed, 15 May 2013 12:49:15 -0400 Subject: [PATCH 230/384] Changed case of FindLibSSH2.cmake to FindLIBSSH2.cmake --- cmake/Modules/{FindLibSSH2.cmake.tmp => FindLIBSSH2.cmake} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmake/Modules/{FindLibSSH2.cmake.tmp => FindLIBSSH2.cmake} (100%) diff --git a/cmake/Modules/FindLibSSH2.cmake.tmp b/cmake/Modules/FindLIBSSH2.cmake similarity index 100% rename from cmake/Modules/FindLibSSH2.cmake.tmp rename to cmake/Modules/FindLIBSSH2.cmake From 84ac625ddd1135755030dfb01b2be965e7cacb2f Mon Sep 17 00:00:00 2001 From: Brad Morgan Date: Wed, 15 May 2013 12:51:40 -0400 Subject: [PATCH 231/384] Added GITERR_CHECK_ALLOC --- src/transports/ssh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index b88b84817..a312c8d08 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -336,6 +336,7 @@ static int _git_ssh_setup_conn( if (git_ssh_extract_url_parts(&host, &user, url) < 0) goto on_error; port = git__strdup(default_port); + GITERR_CHECK_ALLOC(port); } if (gitno_connect(&s->socket, host, port, 0) < 0) From 6fe02c11a6709adb52c2a48cd6470b6e964e6c99 Mon Sep 17 00:00:00 2001 From: Jameson Miller Date: Wed, 15 May 2013 14:44:35 -0400 Subject: [PATCH 232/384] Fetch should not fail when remote HEAD reference is not present locally --- src/remote.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/remote.c b/src/remote.c index e5a7df75e..7a64622a5 100644 --- a/src/remote.c +++ b/src/remote.c @@ -870,19 +870,6 @@ static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vecto if (git_vector_init(&update_heads, 16, NULL) < 0) return -1; - /* Let's go find HEAD, if it exists. Check only the first ref in the vector. */ - if (refs->length > 0) { - head = git_vector_get(refs, 0); - - if (!strcmp(head->name, GIT_HEAD_FILE)) { - if (git_reference_create(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0) - goto on_error; - - i = 1; - git_reference_free(ref); - } - } - for (; i < refs->length; ++i) { head = git_vector_get(refs, i); autotag = 0; From 1fed6b07f0722c8b4349ff3709a49df3d3c9ae61 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Mon, 13 May 2013 21:57:37 +0200 Subject: [PATCH 233/384] Fix trailing whitespaces --- examples/rev-list.c | 1 - include/git2/merge.h | 8 +- include/git2/pack.h | 6 +- include/git2/sys/index.h | 1 - include/git2/trace.h | 3 +- src/checkout.c | 2 +- src/clone.c | 2 +- src/diff_tform.c | 6 +- src/hashsig.c | 1 - src/index.c | 38 +-- src/merge.c | 327 +++++++++++++------------- src/merge.h | 36 +-- src/merge_file.c | 47 ++-- src/merge_file.h | 6 +- src/pathspec.c | 2 +- src/refdb_fs.c | 6 +- src/reset.c | 2 +- src/revparse.c | 1 - src/signature.c | 2 +- src/trace.c | 3 +- src/trace.h | 6 +- src/transports/local.c | 2 +- src/win32/findfile.c | 1 - src/win32/posix_w32.c | 2 +- tests-clar/checkout/tree.c | 2 +- tests-clar/clone/empty.c | 4 +- tests-clar/commit/write.c | 2 +- tests-clar/index/names.c | 22 +- tests-clar/index/reuc.c | 34 +-- tests-clar/merge/merge_helpers.c | 100 ++++---- tests-clar/merge/trees/automerge.c | 28 +-- tests-clar/merge/trees/modeconflict.c | 4 +- tests-clar/merge/trees/renames.c | 25 +- tests-clar/merge/trees/treediff.c | 105 ++++----- tests-clar/merge/trees/trivial.c | 38 +-- tests-clar/refs/branches/move.c | 22 +- tests-clar/refs/ref_helpers.c | 6 +- tests-clar/refs/revparse.c | 21 +- tests-clar/refs/setter.c | 16 +- tests-clar/reset/hard.c | 30 +-- tests-clar/status/submodules.c | 1 - tests-clar/trace/trace.c | 1 - 42 files changed, 479 insertions(+), 493 deletions(-) diff --git a/examples/rev-list.c b/examples/rev-list.c index d9ec15f76..1fb7ebf9f 100644 --- a/examples/rev-list.c +++ b/examples/rev-list.c @@ -117,4 +117,3 @@ int main (int argc, char **argv) return 0; } - diff --git a/include/git2/merge.h b/include/git2/merge.h index 738d8e028..955840569 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -33,7 +33,7 @@ typedef enum { /** * Automerge options for `git_merge_trees_opts`. - */ + */ typedef enum { GIT_MERGE_AUTOMERGE_NORMAL = 0, GIT_MERGE_AUTOMERGE_NONE = 1, @@ -45,10 +45,10 @@ typedef enum { typedef struct { unsigned int version; git_merge_tree_flags flags; - + /** Similarity to consider a file renamed (default 50) */ unsigned int rename_threshold; - + /** Maximum similarity sources to examine (overrides the * `merge.renameLimit` config) (default 200) */ @@ -56,7 +56,7 @@ typedef struct { /** Pluggable similarity metric; pass NULL to use internal metric */ git_diff_similarity_metric *metric; - + /** Flags for automerging content. */ git_merge_automerge_flags automerge_flags; } git_merge_tree_opts; diff --git a/include/git2/pack.h b/include/git2/pack.h index 118b8d554..5e431e62d 100644 --- a/include/git2/pack.h +++ b/include/git2/pack.h @@ -96,12 +96,12 @@ GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid * /** * Insert a commit object - * + * * This will add a commit as well as the completed referenced tree. - * + * * @param pb The packbuilder * @param id The oid of the commit - * + * * @return 0 or an error code */ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *id); diff --git a/include/git2/sys/index.h b/include/git2/sys/index.h index f74637f84..a32e07036 100644 --- a/include/git2/sys/index.h +++ b/include/git2/sys/index.h @@ -177,4 +177,3 @@ GIT_EXTERN(void) git_index_reuc_clear(git_index *index); /** @} */ GIT_END_DECL #endif - diff --git a/include/git2/trace.h b/include/git2/trace.h index 7409b032d..f9b4d6ff6 100644 --- a/include/git2/trace.h +++ b/include/git2/trace.h @@ -32,7 +32,7 @@ typedef enum { /** Errors that do not impact the program's execution */ GIT_TRACE_ERROR = 2, - + /** Warnings that suggest abnormal data */ GIT_TRACE_WARN = 3, @@ -65,4 +65,3 @@ GIT_EXTERN(int) git_trace_set(git_trace_level_t level, git_trace_callback cb); /** @} */ GIT_END_DECL #endif - diff --git a/src/checkout.c b/src/checkout.c index 68028dfef..8f5575779 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -771,7 +771,7 @@ static int blob_content_to_link( if ((error = git_futils_mkpath2file(path, dir_mode)) < 0) return error; - + if ((error = git_blob__getbuf(&linktarget, blob)) < 0) return error; diff --git a/src/clone.c b/src/clone.c index 499195dcc..7ebdb5765 100644 --- a/src/clone.c +++ b/src/clone.c @@ -274,7 +274,7 @@ static int update_head_to_branch( int retcode; git_buf remote_branch_name = GIT_BUF_INIT; git_reference* remote_ref = NULL; - + assert(options->checkout_branch); if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s", diff --git a/src/diff_tform.c b/src/diff_tform.c index 201a0e896..e88525654 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -178,7 +178,7 @@ int git_diff_find_similar__hashsig_for_file( GIT_UNUSED(f); error = git_hashsig_create_fromfile((git_hashsig **)out, path, opt); - + if (error == GIT_EBUFS) { error = 0; giterr_clear(); @@ -195,7 +195,7 @@ int git_diff_find_similar__hashsig_for_buf( GIT_UNUSED(f); error = git_hashsig_create((git_hashsig **)out, buf, len, opt); - + if (error == GIT_EBUFS) { error = 0; giterr_clear(); @@ -437,7 +437,7 @@ static int similarity_measure( return -1; if (!cache[b_idx] && similarity_calc(diff, opts, b_idx, cache) < 0) return -1; - + /* some metrics may not wish to process this file (too big / too small) */ if (!cache[a_idx] || !cache[b_idx]) return 0; diff --git a/src/hashsig.c b/src/hashsig.c index 3a75aaaed..ab8d8b3f0 100644 --- a/src/hashsig.c +++ b/src/hashsig.c @@ -365,4 +365,3 @@ int git_hashsig_compare(const git_hashsig *a, const git_hashsig *b) return (hashsig_heap_compare(&a->mins, &b->mins) + hashsig_heap_compare(&a->maxs, &b->maxs)) / 2; } - diff --git a/src/index.c b/src/index.c index 53edb4874..4b3c2bb4b 100644 --- a/src/index.c +++ b/src/index.c @@ -381,7 +381,7 @@ void git_index_clear(git_index *index) git_index_reuc_clear(index); git_index_name_clear(index); - + git_futils_filestamp_set(&index->stamp, NULL); git_tree_cache_free(index->tree); @@ -1108,7 +1108,7 @@ const git_index_name_entry *git_index_name_get_byindex( git_index *index, size_t n) { assert(index); - + git_vector_sort(&index->names); return git_vector_get(&index->names, n); } @@ -1122,7 +1122,7 @@ int git_index_name_add(git_index *index, conflict_name = git__calloc(1, sizeof(git_index_name_entry)); GITERR_CHECK_ALLOC(conflict_name); - + if (ancestor) { conflict_name->ancestor = git__strdup(ancestor); GITERR_CHECK_ALLOC(conflict_name->ancestor); @@ -1137,7 +1137,7 @@ int git_index_name_add(git_index *index, conflict_name->theirs = git__strdup(theirs); GITERR_CHECK_ALLOC(conflict_name->theirs); } - + return git_vector_insert(&index->names, conflict_name); } @@ -1147,7 +1147,7 @@ void git_index_name_clear(git_index *index) git_index_name_entry *conflict_name; assert(index); - + git_vector_foreach(&index->names, i, conflict_name) { if (conflict_name->ancestor) git__free(conflict_name->ancestor); @@ -1160,7 +1160,7 @@ void git_index_name_clear(git_index *index) git__free(conflict_name); } - + git_vector_clear(&index->names); } @@ -1354,7 +1354,7 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) static int read_conflict_names(git_index *index, const char *buffer, size_t size) { size_t len; - + /* This gets called multiple times, the vector might already be initialized */ if (index->names._alloc_size == 0 && git_vector_init(&index->names, 16, conflict_name_cmp) < 0) @@ -1375,7 +1375,7 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size \ buffer += len; \ size -= len; - + while (size) { git_index_name_entry *conflict_name = git__calloc(1, sizeof(git_index_name_entry)); GITERR_CHECK_ALLOC(conflict_name); @@ -1383,17 +1383,17 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size read_conflict_name(conflict_name->ancestor); read_conflict_name(conflict_name->ours); read_conflict_name(conflict_name->theirs); - + if (git_vector_insert(&index->names, conflict_name) < 0) return -1; } #undef read_conflict_name - + /* entries are guaranteed to be sorted on-disk */ index->names.sorted = 1; - - return 0; + + return 0; } static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size) @@ -1724,7 +1724,7 @@ static int create_name_extension_data(git_buf *name_buf, git_index_name_entry *c error = git_buf_put(name_buf, "\0", 1); else error = git_buf_put(name_buf, conflict_name->ancestor, strlen(conflict_name->ancestor) + 1); - + if (error != 0) goto on_error; @@ -1753,20 +1753,20 @@ static int write_name_extension(git_index *index, git_filebuf *file) struct index_extension extension; size_t i; int error = 0; - + git_vector_foreach(out, i, conflict_name) { if ((error = create_name_extension_data(&name_buf, conflict_name)) < 0) goto done; } - + memset(&extension, 0x0, sizeof(struct index_extension)); memcpy(&extension.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4); extension.extension_size = (uint32_t)name_buf.size; - + error = write_extension(file, &extension, &name_buf); - + git_buf_free(&name_buf); - + done: return error; } @@ -1844,7 +1844,7 @@ static int write_index(git_index *index, git_filebuf *file) /* write the rename conflict extension */ if (index->names.length > 0 && write_name_extension(index, file) < 0) return -1; - + /* write the reuc extension */ if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0) return -1; diff --git a/src/merge.c b/src/merge.c index 117af8179..de5d65ac0 100644 --- a/src/merge.c +++ b/src/merge.c @@ -370,14 +370,14 @@ static int merge_conflict_resolve_trivial( if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE || conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED) return 0; - + if (conflict->our_status == GIT_DELTA_RENAMED || conflict->their_status == GIT_DELTA_RENAMED) return 0; ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry); theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry); - + ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED); theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED); ours_theirs_differ = ours_changed && theirs_changed && @@ -433,7 +433,7 @@ static int merge_conflict_resolve_trivial( *resolved = 1; /* Note: trivial resolution does not update the REUC. */ - + return error; } @@ -486,9 +486,9 @@ static int merge_conflict_resolve_one_renamed( int ours_changed, theirs_changed; git_index_entry *merged; int error = 0; - + assert(resolved && diff_list && conflict); - + *resolved = 0; if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) || @@ -497,7 +497,7 @@ static int merge_conflict_resolve_one_renamed( ours_renamed = (conflict->our_status == GIT_DELTA_RENAMED); theirs_renamed = (conflict->their_status == GIT_DELTA_RENAMED); - + if (!ours_renamed && !theirs_renamed) return 0; @@ -509,7 +509,7 @@ static int merge_conflict_resolve_one_renamed( ours_changed = (git_oid__cmp(&conflict->ancestor_entry.oid, &conflict->our_entry.oid) != 0); theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.oid, &conflict->their_entry.oid) != 0); - + /* if both are modified (and not to a common target) require a merge */ if (ours_changed && theirs_changed && git_oid__cmp(&conflict->our_entry.oid, &conflict->their_entry.oid) != 0) @@ -517,7 +517,7 @@ static int merge_conflict_resolve_one_renamed( if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) return -1; - + if (ours_changed) memcpy(merged, &conflict->our_entry, sizeof(git_index_entry)); else @@ -527,12 +527,12 @@ static int merge_conflict_resolve_one_renamed( merged->path = conflict->our_entry.path; else merged->path = conflict->their_entry.path; - + *resolved = 1; - + git_vector_insert(&diff_list->staged, merged); git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict); - + return error; } @@ -550,11 +550,11 @@ static int merge_conflict_resolve_automerge( git_odb *odb = NULL; git_oid automerge_oid; int error = 0; - + assert(resolved && diff_list && conflict); - + *resolved = 0; - + if (automerge_flags == GIT_MERGE_AUTOMERGE_NONE) return 0; @@ -585,7 +585,7 @@ static int merge_conflict_resolve_automerge( !result.automergeable || (error = git_odb_write(&automerge_oid, odb, result.data, result.len, GIT_OBJ_BLOB)) < 0) goto done; - + if ((index_entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) GITERR_CHECK_ALLOC(index_entry); @@ -595,7 +595,7 @@ static int merge_conflict_resolve_automerge( index_entry->file_size = result.len; index_entry->mode = result.mode; git_oid_cpy(&index_entry->oid, &automerge_oid); - + git_vector_insert(&diff_list->staged, index_entry); git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict); @@ -607,7 +607,7 @@ done: git_merge_file_input_free(&theirs); git_merge_file_result_free(&result); git_odb_free(odb); - + return error; } @@ -619,16 +619,16 @@ static int merge_conflict_resolve( { int resolved = 0; int error = 0; - + *out = 0; - + if ((error = merge_conflict_resolve_trivial(&resolved, diff_list, conflict)) < 0) goto done; if (automerge_flags != GIT_MERGE_AUTOMERGE_NONE) { if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0) goto done; - + if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0) goto done; @@ -637,7 +637,7 @@ static int merge_conflict_resolve( } *out = resolved; - + done: return error; } @@ -666,7 +666,7 @@ static int index_entry_similarity_exact( if (git_oid__cmp(&a->oid, &b->oid) == 0) return 100; - + return 0; } @@ -680,28 +680,28 @@ static int index_entry_similarity_calc( git_diff_file diff_file = {{{0}}}; git_off_t blobsize; int error; - + *out = NULL; if ((error = git_blob_lookup(&blob, repo, &entry->oid)) < 0) return error; - + git_oid_cpy(&diff_file.oid, &entry->oid); diff_file.path = entry->path; diff_file.size = entry->file_size; diff_file.mode = entry->mode; diff_file.flags = 0; - + blobsize = git_blob_rawsize(blob); /* file too big for rename processing */ if (!git__is_sizet(blobsize)) return 0; - + error = opts->metric->buffer_signature(out, &diff_file, git_blob_rawcontent(blob), (size_t)blobsize, opts->metric->payload); - + git_blob_free(blob); return error; @@ -718,16 +718,16 @@ static int index_entry_similarity_inexact( { int score = 0; int error = 0; - + if (GIT_MODE_TYPE(a->mode) != GIT_MODE_TYPE(b->mode)) return 0; - + /* update signature cache if needed */ if (!cache[a_idx] && (error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0) return error; if (!cache[b_idx] && (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0) return error; - + /* some metrics may not wish to process this file (too big / too small) */ if (!cache[a_idx] || !cache[b_idx]) return 0; @@ -736,13 +736,13 @@ static int index_entry_similarity_inexact( if (opts->metric->similarity( &score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0) return -1; - + /* clip score */ if (score < 0) score = 0; else if (score > 100) score = 100; - + return score; } @@ -758,7 +758,7 @@ static int merge_diff_mark_similarity( size_t i, j; git_merge_diff *conflict_src, *conflict_tgt; int similarity; - + git_vector_foreach(&diff_list->conflicts, i, conflict_src) { /* Items can be the source of a rename iff they have an item in the * ancestor slot and lack an item in the ours or theirs slot. */ @@ -766,63 +766,63 @@ static int merge_diff_mark_similarity( (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry) && GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry))) continue; - + git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) { size_t our_idx = diff_list->conflicts.length + j; size_t their_idx = (diff_list->conflicts.length * 2) + j; - + if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry)) continue; - + if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) { similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts); - + if (similarity == GIT_EBUFS) - continue; + continue; else if (similarity < 0) return similarity; - + if (similarity > similarity_ours[i].similarity && similarity > similarity_ours[j].similarity) { /* Clear previous best similarity */ if (similarity_ours[i].similarity > 0) similarity_ours[similarity_ours[i].other_idx].similarity = 0; - + if (similarity_ours[j].similarity > 0) similarity_ours[similarity_ours[j].other_idx].similarity = 0; - + similarity_ours[i].similarity = similarity; similarity_ours[i].other_idx = j; - + similarity_ours[j].similarity = similarity; similarity_ours[j].other_idx = i; } } - + if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) { similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts); - + if (similarity > similarity_theirs[i].similarity && similarity > similarity_theirs[j].similarity) { /* Clear previous best similarity */ if (similarity_theirs[i].similarity > 0) similarity_theirs[similarity_theirs[i].other_idx].similarity = 0; - + if (similarity_theirs[j].similarity > 0) similarity_theirs[similarity_theirs[j].other_idx].similarity = 0; - + similarity_theirs[i].similarity = similarity; similarity_theirs[i].other_idx = j; - + similarity_theirs[j].similarity = similarity; similarity_theirs[j].other_idx = i; } } } } - + return 0; } @@ -857,13 +857,13 @@ static void merge_diff_mark_rename_conflict( const git_merge_tree_opts *opts) { git_merge_diff *ours_source = NULL, *theirs_source = NULL; - + if (ours_renamed) ours_source = diff_list->conflicts.contents[ours_source_idx]; - + if (theirs_renamed) theirs_source = diff_list->conflicts.contents[theirs_source_idx]; - + /* Detect 2->1 conflicts */ if (ours_renamed && theirs_renamed) { /* Both renamed to the same target name. */ @@ -877,30 +877,30 @@ static void merge_diff_mark_rename_conflict( /* If our source was also renamed in theirs, this is a 1->2 */ if (similarity_theirs[ours_source_idx].similarity >= opts->rename_threshold) ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2; - + else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry)) { ours_source->type = GIT_MERGE_DIFF_RENAMED_ADDED; target->type = GIT_MERGE_DIFF_RENAMED_ADDED; } - + else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(ours_source->their_entry)) ours_source->type = GIT_MERGE_DIFF_RENAMED_DELETED; - + else if (ours_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED) ours_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED; } else if (theirs_renamed) { /* If their source was also renamed in ours, this is a 1->2 */ if (similarity_ours[theirs_source_idx].similarity >= opts->rename_threshold) theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2; - + else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry)) { theirs_source->type = GIT_MERGE_DIFF_RENAMED_ADDED; target->type = GIT_MERGE_DIFF_RENAMED_ADDED; } - + else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(theirs_source->our_entry)) theirs_source->type = GIT_MERGE_DIFF_RENAMED_DELETED; - + else if (theirs_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED) theirs_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED; } @@ -915,7 +915,7 @@ GIT_INLINE(void) merge_diff_coalesce_rename( /* Coalesce the rename target into the rename source. */ memcpy(source_entry, target_entry, sizeof(git_index_entry)); *source_status = GIT_DELTA_RENAMED; - + memset(target_entry, 0x0, sizeof(git_index_entry)); *target_status = GIT_DELTA_UNMODIFIED; } @@ -930,50 +930,50 @@ static void merge_diff_list_coalesce_renames( bool ours_renamed = 0, theirs_renamed = 0; size_t ours_source_idx = 0, theirs_source_idx = 0; git_merge_diff *ours_source, *theirs_source, *target; - + for (i = 0; i < diff_list->conflicts.length; i++) { target = diff_list->conflicts.contents[i]; - + ours_renamed = 0; theirs_renamed = 0; - + if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry) && similarity_ours[i].similarity >= opts->rename_threshold) { ours_source_idx = similarity_ours[i].other_idx; - + ours_source = diff_list->conflicts.contents[ours_source_idx]; - + merge_diff_coalesce_rename( &ours_source->our_entry, &ours_source->our_status, &target->our_entry, &target->our_status); - + similarity_ours[ours_source_idx].similarity = 0; similarity_ours[i].similarity = 0; - + ours_renamed = 1; } - + /* insufficient to determine direction */ if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry) && similarity_theirs[i].similarity >= opts->rename_threshold) { theirs_source_idx = similarity_theirs[i].other_idx; - + theirs_source = diff_list->conflicts.contents[theirs_source_idx]; - + merge_diff_coalesce_rename( &theirs_source->their_entry, &theirs_source->their_status, &target->their_entry, &target->their_status); - + similarity_theirs[theirs_source_idx].similarity = 0; similarity_theirs[i].similarity = 0; - + theirs_renamed = 1; } - + merge_diff_mark_rename_conflict(diff_list, similarity_ours, ours_renamed, ours_source_idx, similarity_theirs, theirs_renamed, theirs_source_idx, @@ -984,7 +984,7 @@ static void merge_diff_list_coalesce_renames( static int merge_diff_empty(const git_vector *conflicts, size_t idx) { git_merge_diff *conflict = conflicts->contents[idx]; - + return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)); @@ -1000,7 +1000,7 @@ static void merge_diff_list_count_candidates( *src_count = 0; *tgt_count = 0; - + git_vector_foreach(&diff_list->conflicts, i, entry) { if (GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry) && (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->our_entry) || @@ -1021,20 +1021,20 @@ int git_merge_diff_list__find_renames( size_t cache_size = 0; size_t src_count, tgt_count, i; int error = 0; - + assert(diff_list && opts); - + if ((opts->flags & GIT_MERGE_TREE_FIND_RENAMES) == 0) return 0; - + similarity_ours = git__calloc(diff_list->conflicts.length, sizeof(struct merge_diff_similarity)); GITERR_CHECK_ALLOC(similarity_ours); - + similarity_theirs = git__calloc(diff_list->conflicts.length, sizeof(struct merge_diff_similarity)); GITERR_CHECK_ALLOC(similarity_theirs); - + /* Calculate similarity between items that were deleted from the ancestor * and added in the other branch. */ @@ -1048,7 +1048,7 @@ int git_merge_diff_list__find_renames( GITERR_CHECK_ALLOC(cache); merge_diff_list_count_candidates(diff_list, &src_count, &tgt_count); - + if (src_count > opts->target_limit || tgt_count > opts->target_limit) { /* TODO: report! */ } else { @@ -1063,7 +1063,7 @@ int git_merge_diff_list__find_renames( * into the old name. */ merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts); - + /* And remove any entries that were merged and are now empty. */ git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty); @@ -1094,7 +1094,7 @@ GIT_INLINE(const char *) merge_diff_path( return conflict->our_entry.path; else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)) return conflict->their_entry.path; - + return NULL; } @@ -1106,7 +1106,7 @@ GIT_INLINE(bool) merge_diff_any_side_added_or_modified( conflict->their_status == GIT_DELTA_ADDED || conflict->their_status == GIT_DELTA_MODIFIED) return true; - + return false; } @@ -1114,11 +1114,11 @@ GIT_INLINE(bool) path_is_prefixed(const char *parent, const char *child) { size_t child_len = strlen(child); size_t parent_len = strlen(parent); - + if (child_len < parent_len || strncmp(parent, child, parent_len) != 0) return 0; - + return (child[parent_len] == '/'); } @@ -1127,7 +1127,7 @@ GIT_INLINE(int) merge_diff_detect_df_conflict( git_merge_diff *conflict) { const char *cur_path = merge_diff_path(conflict); - + /* Determine if this is a D/F conflict or the child of one */ if (df_data->df_path && path_is_prefixed(df_data->df_path, cur_path)) @@ -1139,14 +1139,14 @@ GIT_INLINE(int) merge_diff_detect_df_conflict( merge_diff_any_side_added_or_modified(conflict) && path_is_prefixed(df_data->prev_path, cur_path)) { conflict->type = GIT_MERGE_DIFF_DF_CHILD; - + df_data->prev_conflict->type = GIT_MERGE_DIFF_DIRECTORY_FILE; df_data->df_path = df_data->prev_path; } - + df_data->prev_path = cur_path; df_data->prev_conflict = conflict; - + return 0; } @@ -1172,7 +1172,7 @@ GIT_INLINE(int) merge_diff_detect_type( conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED; else conflict->type = GIT_MERGE_DIFF_NONE; - + return 0; } @@ -1183,11 +1183,11 @@ GIT_INLINE(int) index_entry_dup( { if (src != NULL) { memcpy(out, src, sizeof(git_index_entry)); - + if ((out->path = git_pool_strdup(pool, src->path)) == NULL) return -1; } - + return 0; } @@ -1208,7 +1208,7 @@ GIT_INLINE(int) merge_delta_type_from_index_entries( else if (git_oid__cmp(&ancestor->oid, &other->oid) || ancestor->mode != other->mode) return GIT_DELTA_MODIFIED; - + return GIT_DELTA_UNMODIFIED; } @@ -1218,20 +1218,20 @@ static git_merge_diff *merge_diff_from_index_entries( { git_merge_diff *conflict; git_pool *pool = &diff_list->pool; - + if ((conflict = git_pool_malloc(pool, sizeof(git_merge_diff))) == NULL) return NULL; - + if (index_entry_dup(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 || index_entry_dup(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 || index_entry_dup(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0) return NULL; - + conflict->our_status = merge_delta_type_from_index_entries( entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_OURS]); conflict->their_status = merge_delta_type_from_index_entries( entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_THEIRS]); - + return conflict; } @@ -1243,13 +1243,13 @@ static int merge_index_insert_conflict( const git_index_entry *tree_items[3]) { git_merge_diff *conflict; - + if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL || merge_diff_detect_type(conflict) < 0 || merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 || git_vector_insert(&diff_list->conflicts, conflict) < 0) return -1; - + return 0; } @@ -1259,13 +1259,13 @@ static int merge_index_insert_unmodified( { int error = 0; git_index_entry *entry; - + entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry)); GITERR_CHECK_ALLOC(entry); - + if ((error = index_entry_dup(entry, &diff_list->pool, tree_items[0])) >= 0) error = git_vector_insert(&diff_list->staged, entry); - + return error; } @@ -1282,40 +1282,40 @@ int git_merge_diff_list__find_differences( int cur_item_modified; size_t i, j; int error = 0; - + assert(diff_list && our_tree && their_tree); if ((error = git_iterator_for_tree(&iterators[TREE_IDX_ANCESTOR], (git_tree *)ancestor_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || (error = git_iterator_for_tree(&iterators[TREE_IDX_OURS], (git_tree *)our_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || (error = git_iterator_for_tree(&iterators[TREE_IDX_THEIRS], (git_tree *)their_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0) goto done; - + /* Set up the iterators */ for (i = 0; i < 3; i++) { if ((error = git_iterator_current(&items[i], iterators[i])) < 0) goto done; } - + while (true) { for (i = 0; i < 3; i++) cur_items[i] = NULL; best_cur_item = NULL; cur_item_modified = 0; - + /* Find the next path(s) to consume from each iterator */ for (i = 0; i < 3; i++) { if (items[i] == NULL) { cur_item_modified = 1; continue; } - + if (best_cur_item == NULL) { best_cur_item = items[i]; cur_items[i] = items[i]; } else { int path_diff = entry_compare(items[i], best_cur_item); - + if (path_diff < 0) { /* * Found an item that sorts before our current item, make @@ -1332,21 +1332,21 @@ int git_merge_diff_list__find_differences( cur_item_modified = 1; } else if (path_diff == 0) { cur_items[i] = items[i]; - + if (!cur_item_modified) cur_item_modified = index_entry_cmp(best_cur_item, items[i]); } } } - + if (best_cur_item == NULL) break; - + if (cur_item_modified) error = merge_index_insert_conflict(diff_list, &df_data, cur_items); else error = merge_index_insert_unmodified(diff_list, cur_items); - + /* Advance each iterator that participated */ for (i = 0; i < 3; i++) { if (cur_items[i] != NULL && @@ -1354,29 +1354,29 @@ int git_merge_diff_list__find_differences( goto done; } } - + done: for (i = 0; i < 3; i++) git_iterator_free(iterators[i]); - + return error; } git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo) { git_merge_diff_list *diff_list = git__calloc(1, sizeof(git_merge_diff_list)); - + if (diff_list == NULL) return NULL; - + diff_list->repo = repo; - + if (git_vector_init(&diff_list->staged, 0, NULL) < 0 || git_vector_init(&diff_list->conflicts, 0, NULL) < 0 || git_vector_init(&diff_list->resolved, 0, NULL) < 0 || git_pool_init(&diff_list->pool, 1, 0) < 0) return NULL; - + return diff_list; } @@ -1387,48 +1387,48 @@ static int merge_tree_normalize_opts( { git_config *cfg = NULL; int error = 0; - + assert(repo && opts); - + if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; - + if (given != NULL) memcpy(opts, given, sizeof(git_merge_tree_opts)); else { git_merge_tree_opts init = GIT_MERGE_TREE_OPTS_INIT; memcpy(opts, &init, sizeof(init)); - + opts->flags = GIT_MERGE_TREE_FIND_RENAMES; opts->rename_threshold = GIT_MERGE_TREE_RENAME_THRESHOLD; } - + if (!opts->target_limit) { int32_t limit = 0; - + opts->target_limit = GIT_MERGE_TREE_TARGET_LIMIT; - + if (git_config_get_int32(&limit, cfg, "merge.renameLimit") < 0) { giterr_clear(); - + if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0) giterr_clear(); } - + if (limit > 0) opts->target_limit = limit; } - + /* assign the internal metric with whitespace flag as payload */ if (!opts->metric) { opts->metric = git__malloc(sizeof(git_diff_similarity_metric)); GITERR_CHECK_ALLOC(opts->metric); - + opts->metric->file_signature = git_diff_find_similar__hashsig_for_file; opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf; opts->metric->free_signature = git_diff_find_similar__hashsig_free; opts->metric->similarity = git_diff_find_similar__calc_similarity; - + if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE) opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE; else if (opts->flags & GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE) @@ -1436,7 +1436,7 @@ static int merge_tree_normalize_opts( else opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE; } - + return 0; } @@ -1450,20 +1450,20 @@ static int merge_index_insert_reuc( int mode[3] = { 0, 0, 0 }; git_oid const *oid[3] = { NULL, NULL, NULL }; size_t i; - + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(*entry)) return 0; - + if ((reuc = git_index_reuc_get_bypath(index, entry->path)) != NULL) { for (i = 0; i < 3; i++) { mode[i] = reuc->mode[i]; oid[i] = &reuc->oid[i]; } } - + mode[idx] = entry->mode; oid[idx] = &entry->oid; - + return git_index_reuc_add(index, entry->path, mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]); } @@ -1475,58 +1475,58 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) git_index_entry *entry; git_merge_diff *conflict; int error = 0; - + *out = NULL; - + if ((error = git_index_new(&index)) < 0) return error; - + git_vector_foreach(&diff_list->staged, i, entry) { if ((error = git_index_add(index, entry)) < 0) goto on_error; } - + git_vector_foreach(&diff_list->conflicts, i, conflict) { const git_index_entry *ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? &conflict->ancestor_entry : NULL; - + const git_index_entry *ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? &conflict->our_entry : NULL; - + const git_index_entry *theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? &conflict->their_entry : NULL; - + if ((error = git_index_conflict_add(index, ancestor, ours, theirs)) < 0) goto on_error; } - + /* Add each rename entry to the rename portion of the index. */ git_vector_foreach(&diff_list->conflicts, i, conflict) { const char *ancestor_path, *our_path, *their_path; - + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry)) continue; - + ancestor_path = conflict->ancestor_entry.path; - + our_path = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? conflict->our_entry.path : NULL; - + their_path = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? conflict->their_entry.path : NULL; - + if ((our_path && strcmp(ancestor_path, our_path) != 0) || (their_path && strcmp(ancestor_path, their_path) != 0)) { if ((error = git_index_name_add(index, ancestor_path, our_path, their_path)) < 0) goto on_error; } } - + /* Add each entry in the resolved conflict to the REUC independently, since * the paths may differ due to renames. */ git_vector_foreach(&diff_list->resolved, i, conflict) { @@ -1537,7 +1537,7 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) const git_index_entry *ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? &conflict->our_entry : NULL; - + const git_index_entry *theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? &conflict->their_entry : NULL; @@ -1545,22 +1545,22 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list) if (ancestor != NULL && (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0) goto on_error; - + if (ours != NULL && (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0) goto on_error; - + if (theirs != NULL && (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0) goto on_error; } - + *out = index; return 0; - + on_error: git_index_free(index); - + return error; } @@ -1582,33 +1582,33 @@ int git_merge_trees( assert(out && repo && our_tree && their_tree); *out = NULL; - + if ((error = merge_tree_normalize_opts(repo, &opts, given_opts)) < 0) return error; diff_list = git_merge_diff_list__alloc(repo); GITERR_CHECK_ALLOC(diff_list); - + if ((error = git_merge_diff_list__find_differences(diff_list, ancestor_tree, our_tree, their_tree)) < 0 || (error = git_merge_diff_list__find_renames(repo, diff_list, &opts)) < 0) goto done; - + memcpy(&changes, &diff_list->conflicts, sizeof(git_vector)); git_vector_clear(&diff_list->conflicts); - + git_vector_foreach(&changes, i, conflict) { int resolved = 0; - + if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.automerge_flags)) < 0) goto done; - + if (!resolved) git_vector_insert(&diff_list->conflicts, conflict); } - + if (!given_opts || !given_opts->metric) git__free(opts.metric); - + error = index_from_diff_list(out, diff_list); done: @@ -1621,11 +1621,10 @@ void git_merge_diff_list__free(git_merge_diff_list *diff_list) { if (!diff_list) return; - + git_vector_free(&diff_list->staged); git_vector_free(&diff_list->conflicts); git_vector_free(&diff_list->resolved); git_pool_clear(&diff_list->pool); git__free(diff_list); } - diff --git a/src/merge.h b/src/merge.h index da1fdf472..50538b12b 100644 --- a/src/merge.h +++ b/src/merge.h @@ -24,46 +24,46 @@ typedef enum { /* No conflict - a change only occurs in one branch. */ GIT_MERGE_DIFF_NONE = 0, - + /* Occurs when a file is modified in both branches. */ GIT_MERGE_DIFF_BOTH_MODIFIED = (1 << 0), - + /* Occurs when a file is added in both branches. */ GIT_MERGE_DIFF_BOTH_ADDED = (1 << 1), - + /* Occurs when a file is deleted in both branches. */ GIT_MERGE_DIFF_BOTH_DELETED = (1 << 2), - + /* Occurs when a file is modified in one branch and deleted in the other. */ GIT_MERGE_DIFF_MODIFIED_DELETED = (1 << 3), - + /* Occurs when a file is renamed in one branch and modified in the other. */ GIT_MERGE_DIFF_RENAMED_MODIFIED = (1 << 4), - + /* Occurs when a file is renamed in one branch and deleted in the other. */ GIT_MERGE_DIFF_RENAMED_DELETED = (1 << 5), - + /* Occurs when a file is renamed in one branch and a file with the same * name is added in the other. Eg, A->B and new file B. Core git calls * this a "rename/delete". */ GIT_MERGE_DIFF_RENAMED_ADDED = (1 << 6), - + /* Occurs when both a file is renamed to the same name in the ours and * theirs branches. Eg, A->B and A->B in both. Automergeable. */ GIT_MERGE_DIFF_BOTH_RENAMED = (1 << 7), - + /* Occurs when a file is renamed to different names in the ours and theirs * branches. Eg, A->B and A->C. */ GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 = (1 << 8), - + /* Occurs when two files are renamed to the same name in the ours and * theirs branches. Eg, A->C and B->C. */ GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 = (1 << 9), - + /* Occurs when an item at a path in one branch is a directory, and an * item at the same path in a different branch is a file. */ GIT_MERGE_DIFF_DIRECTORY_FILE = (1 << 10), - + /* The child of a folder that is in a directory/file conflict. */ GIT_MERGE_DIFF_DF_CHILD = (1 << 11), } git_merge_diff_type_t; @@ -72,20 +72,20 @@ typedef enum { typedef struct { git_repository *repo; git_pool pool; - + /* Vector of git_index_entry that represent the merged items that * have been staged, either because only one side changed, or because * the two changes were non-conflicting and mergeable. These items * will be written as staged entries in the main index. */ git_vector staged; - + /* Vector of git_merge_diff entries that represent the conflicts that * have not been automerged. These items will be written to high-stage * entries in the main index. */ git_vector conflicts; - + /* Vector of git_merge_diff that have been automerged. These items * will be written to the REUC when the index is produced. */ @@ -97,12 +97,12 @@ typedef struct { */ typedef struct { git_merge_diff_type_t type; - + git_index_entry ancestor_entry; - + git_index_entry our_entry; git_delta_t our_status; - + git_index_entry their_entry; git_delta_t their_status; } git_merge_diff; diff --git a/src/merge_file.c b/src/merge_file.c index 4b3f3730b..c3477ccb9 100644 --- a/src/merge_file.c +++ b/src/merge_file.c @@ -28,12 +28,12 @@ GIT_INLINE(const char *) merge_file_best_path( return NULL; } - + if (strcmp(ancestor->path, ours->path) == 0) return theirs->path; else if(strcmp(ancestor->path, theirs->path) == 0) return ours->path; - + return NULL; } @@ -51,15 +51,15 @@ GIT_INLINE(int) merge_file_best_mode( if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE || theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE) return GIT_FILEMODE_BLOB_EXECUTABLE; - + return GIT_FILEMODE_BLOB; } - + if (ancestor->mode == ours->mode) return theirs->mode; else if(ancestor->mode == theirs->mode) return ours->mode; - + return 0; } @@ -70,27 +70,27 @@ int git_merge_file_input_from_index_entry( { git_odb *odb = NULL; int error = 0; - + assert(input && repo && entry); - + if (entry->mode == 0) return 0; - + if ((error = git_repository_odb(&odb, repo)) < 0 || (error = git_odb_read(&input->odb_object, odb, &entry->oid)) < 0) goto done; - + input->mode = entry->mode; input->path = git__strdup(entry->path); input->mmfile.size = git_odb_object_size(input->odb_object); input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object); - + if (input->label == NULL) input->label = entry->path; - + done: git_odb_free(odb); - + return error; } @@ -101,27 +101,27 @@ int git_merge_file_input_from_diff_file( { git_odb *odb = NULL; int error = 0; - + assert(input && repo && file); - + if (file->mode == 0) return 0; - + if ((error = git_repository_odb(&odb, repo)) < 0 || (error = git_odb_read(&input->odb_object, odb, &file->oid)) < 0) goto done; - + input->mode = file->mode; input->path = git__strdup(file->path); input->mmfile.size = git_odb_object_size(input->odb_object); input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object); - + if (input->label == NULL) input->label = file->path; - + done: git_odb_free(odb); - + return error; } @@ -138,12 +138,12 @@ int git_merge_files( int error = 0; assert(out && ancestor && ours && theirs); - + memset(out, 0x0, sizeof(git_merge_file_result)); if (!GIT_MERGE_FILE_SIDE_EXISTS(ours) || !GIT_MERGE_FILE_SIDE_EXISTS(theirs)) return 0; - + memset(&xmparam, 0x0, sizeof(xmparam_t)); xmparam.ancestor = ancestor->label; xmparam.file1 = ours->label; @@ -154,7 +154,7 @@ int git_merge_files( if (flags == GIT_MERGE_AUTOMERGE_FAVOR_OURS) xmparam.favor = XDL_MERGE_FAVOR_OURS; - + if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS) xmparam.favor = XDL_MERGE_FAVOR_THEIRS; @@ -164,7 +164,7 @@ int git_merge_files( error = -1; goto done; } - + out->automergeable = (xdl_result == 0); out->data = (unsigned char *)mmbuffer.ptr; out->len = mmbuffer.size; @@ -172,4 +172,3 @@ int git_merge_files( done: return error; } - diff --git a/src/merge_file.h b/src/merge_file.h index 1aa34893d..0af2f0a57 100644 --- a/src/merge_file.h +++ b/src/merge_file.h @@ -16,7 +16,7 @@ typedef struct { char *path; unsigned int mode; mmfile_t mmfile; - + git_odb_object *odb_object; } git_merge_file_input; @@ -24,10 +24,10 @@ typedef struct { typedef struct { bool automergeable; - + const char *path; int mode; - + unsigned char *data; size_t len; } git_merge_file_result; diff --git a/src/pathspec.c b/src/pathspec.c index d4eb12582..35c79ce82 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -141,7 +141,7 @@ bool git_pathspec_match_path( git_vector_foreach(vspec, i, match) { int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : FNM_NOMATCH; - + if (result == FNM_NOMATCH) result = use_strcmp(match->pattern, path) ? FNM_NOMATCH : 0; diff --git a/src/refdb_fs.c b/src/refdb_fs.c index b9df83f81..f964c4182 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -217,7 +217,7 @@ static int packed_load(refdb_fs_backend *backend) backend->peeling_mode = PEELING_NONE; if (buffer_start[0] == '#') { - static const char *traits_header = "# pack-refs with: "; + static const char *traits_header = "# pack-refs with: "; if (git__prefixcmp(buffer_start, traits_header) == 0) { char *traits = (char *)buffer_start + strlen(traits_header); @@ -1060,7 +1060,7 @@ static void refdb_fs_backend__free(git_refdb_backend *_backend) static int setup_namespace(git_buf *path, git_repository *repo) { - char *parts, *start, *end; + char *parts, *start, *end; /* Not all repositories have a path */ if (repo->path_repository == NULL) @@ -1091,7 +1091,7 @@ static int setup_namespace(git_buf *path, git_repository *repo) free(parts); /* Make sure that the folder with the namespace exists */ - if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0) + if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0) return -1; /* Return the root of the namespaced path, i.e. without the trailing '/refs' */ diff --git a/src/reset.c b/src/reset.c index c1e1f865e..cea212a93 100644 --- a/src/reset.c +++ b/src/reset.c @@ -32,7 +32,7 @@ int git_reset_default( int error; git_index *index = NULL; - assert(pathspecs != NULL && pathspecs->count > 0); + assert(pathspecs != NULL && pathspecs->count > 0); memset(&entry, 0, sizeof(git_index_entry)); diff --git a/src/revparse.c b/src/revparse.c index 05231e3fc..97fc91b54 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -855,4 +855,3 @@ int git_revparse( return error; } - diff --git a/src/signature.c b/src/signature.c index 48bdd81ab..1131fb789 100644 --- a/src/signature.c +++ b/src/signature.c @@ -173,7 +173,7 @@ int git_signature__parse(git_signature *sig, const char **buffer_out, tz_start = time_end + 1; - if ((tz_start[0] != '-' && tz_start[0] != '+') || + if ((tz_start[0] != '-' && tz_start[0] != '+') || git__strtol32(&offset, tz_start + 1, &tz_end, 10) < 0) return signature_error("malformed timezone"); diff --git a/src/trace.c b/src/trace.c index 159ac91cc..ee5039f56 100644 --- a/src/trace.c +++ b/src/trace.c @@ -25,7 +25,7 @@ int git_trace_set(git_trace_level_t level, git_trace_callback callback) git_trace__data.level = level; git_trace__data.callback = callback; GIT_MEMORY_BARRIER; - + return 0; #else GIT_UNUSED(level); @@ -36,4 +36,3 @@ int git_trace_set(git_trace_level_t level, git_trace_callback callback) return -1; #endif } - diff --git a/src/trace.h b/src/trace.h index f4bdff88a..77b1e03ef 100644 --- a/src/trace.h +++ b/src/trace.h @@ -25,14 +25,14 @@ GIT_INLINE(void) git_trace__write_fmt( git_trace_level_t level, const char *fmt, ...) { - git_trace_callback callback = git_trace__data.callback; + git_trace_callback callback = git_trace__data.callback; git_buf message = GIT_BUF_INIT; va_list ap; - + va_start(ap, fmt); git_buf_vprintf(&message, fmt, ap); va_end(ap); - + callback(level, git_buf_cstr(&message)); git_buf_free(&message); diff --git a/src/transports/local.c b/src/transports/local.c index 2530a847f..4bf1c876a 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -348,7 +348,7 @@ static int local_push( if ((error = git_repository_open(&remote_repo, push->remote->url)) < 0) return error; - /* We don't currently support pushing locally to non-bare repos. Proper + /* We don't currently support pushing locally to non-bare repos. Proper non-bare repo push support would require checking configs to see if we should override the default 'don't let this happen' behavior */ if (!remote_repo->is_bare) { diff --git a/src/win32/findfile.c b/src/win32/findfile.c index bc36b6b45..5dd3de13d 100644 --- a/src/win32/findfile.c +++ b/src/win32/findfile.c @@ -235,4 +235,3 @@ int git_win32__find_xdg_dirs(git_buf *out) return win32_find_existing_dirs(out, global_tmpls, temp); } - diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index b9115836e..f9967e04a 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -535,7 +535,7 @@ int p_gettimeofday(struct timeval *tv, struct timezone *tz) /*converting file time to unix epoch*/ tmpres /= 10; /*convert into microseconds*/ - tmpres -= DELTA_EPOCH_IN_MICROSECS; + tmpres -= DELTA_EPOCH_IN_MICROSECS; tv->tv_sec = (long)(tmpres / 1000000UL); tv->tv_usec = (long)(tmpres % 1000000UL); } diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index 67357a942..462a46c83 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -363,7 +363,7 @@ void assert_conflict( git_index *index; git_object *hack_tree; git_reference *branch, *head; - git_buf file_path = GIT_BUF_INIT; + git_buf file_path = GIT_BUF_INIT; cl_git_pass(git_repository_index(&index, g_repo)); diff --git a/tests-clar/clone/empty.c b/tests-clar/clone/empty.c index f190523b6..f92fa6cbb 100644 --- a/tests-clar/clone/empty.c +++ b/tests-clar/clone/empty.c @@ -48,13 +48,13 @@ void test_clone_empty__can_clone_an_empty_local_repo_barely(void) cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name)); /* ...one can still retrieve the name of the remote tracking reference */ - cl_assert_equal_i((int)strlen(expected_tracked_branch_name) + 1, + cl_assert_equal_i((int)strlen(expected_tracked_branch_name) + 1, git_branch_upstream_name(buffer, 1024, g_repo_cloned, local_name)); cl_assert_equal_s(expected_tracked_branch_name, buffer); /* ...and the name of the remote... */ - cl_assert_equal_i((int)strlen(expected_remote_name) + 1, + cl_assert_equal_i((int)strlen(expected_remote_name) + 1, git_branch_remote_name(buffer, 1024, g_repo_cloned, expected_tracked_branch_name)); cl_assert_equal_s(expected_remote_name, buffer); diff --git a/tests-clar/commit/write.c b/tests-clar/commit/write.c index e9946af89..73436b74b 100644 --- a/tests-clar/commit/write.c +++ b/tests-clar/commit/write.c @@ -115,7 +115,7 @@ void test_commit_write__root(void) head_old = git__strdup(git_reference_symbolic_target(head)); cl_assert(head_old != NULL); git_reference_free(head); - + cl_git_pass(git_reference_symbolic_create(&head, g_repo, "HEAD", branch_name, 1)); cl_git_pass(git_commit_create_v( diff --git a/tests-clar/index/names.c b/tests-clar/index/names.c index 68615531a..95a560ee4 100644 --- a/tests-clar/index/names.c +++ b/tests-clar/index/names.c @@ -21,7 +21,7 @@ void test_index_names__cleanup(void) { git_index_free(repo_index); repo_index = NULL; - + cl_git_sandbox_cleanup(); } @@ -32,14 +32,14 @@ void test_index_names__add(void) cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs")); cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL)); cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3")); - + cl_assert(git_index_name_entrycount(repo_index) == 3); - + conflict_name = git_index_name_get_byindex(repo_index, 0); cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0); cl_assert(strcmp(conflict_name->ours, "ours") == 0); cl_assert(strcmp(conflict_name->theirs, "theirs") == 0); - + conflict_name = git_index_name_get_byindex(repo_index, 1); cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0); cl_assert(strcmp(conflict_name->ours, "ours2") == 0); @@ -54,31 +54,31 @@ void test_index_names__add(void) void test_index_names__roundtrip(void) { const git_index_name_entry *conflict_name; - + cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs")); cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL)); cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3")); - + cl_git_pass(git_index_write(repo_index)); git_index_clear(repo_index); cl_assert(git_index_name_entrycount(repo_index) == 0); - + cl_git_pass(git_index_read(repo_index)); cl_assert(git_index_name_entrycount(repo_index) == 3); - + conflict_name = git_index_name_get_byindex(repo_index, 0); cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0); cl_assert(strcmp(conflict_name->ours, "ours") == 0); cl_assert(strcmp(conflict_name->theirs, "theirs") == 0); - + conflict_name = git_index_name_get_byindex(repo_index, 1); cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0); cl_assert(strcmp(conflict_name->ours, "ours2") == 0); cl_assert(conflict_name->theirs == NULL); - + conflict_name = git_index_name_get_byindex(repo_index, 2); cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0); cl_assert(conflict_name->ours == NULL); cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0); - + } diff --git a/tests-clar/index/reuc.c b/tests-clar/index/reuc.c index 0e38a92f3..69ed4a933 100644 --- a/tests-clar/index/reuc.c +++ b/tests-clar/index/reuc.c @@ -232,7 +232,7 @@ void test_index_reuc__remove(void) cl_git_pass(git_index_reuc_remove(repo_index, 0)); cl_git_fail(git_index_reuc_remove(repo_index, 1)); - + cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index)); cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0)); @@ -283,7 +283,7 @@ void test_index_reuc__write(void) /* ensure sort order was round-tripped correct */ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0)); cl_assert_equal_s("one.txt", reuc->path); - + cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1)); cl_assert_equal_s("two.txt", reuc->path); } @@ -296,41 +296,41 @@ static int reuc_entry_exists(void) void test_index_reuc__cleaned_on_reset_hard(void) { git_object *target; - + retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876"); - + test_index_reuc__add(); cl_git_pass(git_reset(repo, target, GIT_RESET_HARD)); cl_assert(reuc_entry_exists() == false); - + git_object_free(target); } void test_index_reuc__cleaned_on_reset_mixed(void) { git_object *target; - + retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876"); - test_index_reuc__add(); + test_index_reuc__add(); cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED)); cl_assert(reuc_entry_exists() == false); - + git_object_free(target); } void test_index_reuc__retained_on_reset_soft(void) { git_object *target; - + retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876"); - + git_reset(repo, target, GIT_RESET_HARD); test_index_reuc__add(); cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT)); cl_assert(reuc_entry_exists() == true); - + git_object_free(target); } @@ -339,7 +339,7 @@ void test_index_reuc__cleaned_on_checkout_tree(void) git_oid oid; git_object *obj; git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; - + opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY; test_index_reuc__add(); @@ -347,16 +347,16 @@ void test_index_reuc__cleaned_on_checkout_tree(void) git_object_lookup(&obj, repo, &oid, GIT_OBJ_ANY); git_checkout_tree(repo, obj, &opts); cl_assert(reuc_entry_exists() == false); - + git_object_free(obj); } void test_index_reuc__cleaned_on_checkout_head(void) { git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; - + opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY; - + test_index_reuc__add(); git_checkout_head(repo, &opts); cl_assert(reuc_entry_exists() == false); @@ -365,9 +365,9 @@ void test_index_reuc__cleaned_on_checkout_head(void) void test_index_reuc__retained_on_checkout_index(void) { git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; - + opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY; - + test_index_reuc__add(); git_checkout_index(repo, repo_index, &opts); cl_assert(reuc_entry_exists() == true); diff --git a/tests-clar/merge/merge_helpers.c b/tests-clar/merge/merge_helpers.c index 71bb96781..bc31b1f44 100644 --- a/tests-clar/merge/merge_helpers.c +++ b/tests-clar/merge/merge_helpers.c @@ -56,11 +56,11 @@ void merge__dump_index_entries(git_vector *index_entries) { size_t i; const git_index_entry *index_entry; - + printf ("\nINDEX [%d]:\n", (int)index_entries->length); for (i = 0; i < index_entries->length; i++) { index_entry = index_entries->contents[i]; - + printf("%o ", index_entry->mode); printf("%s ", git_oid_allocfmt(&index_entry->oid)); printf("%d ", git_index_entry_stage(index_entry)); @@ -77,7 +77,7 @@ void merge__dump_names(git_index *index) for (i = 0; i < git_index_name_entrycount(index); i++) { conflict_name = git_index_name_get_byindex(index, i); - + printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs); } printf("\n"); @@ -91,7 +91,7 @@ void merge__dump_reuc(git_index *index) printf ("\nREUC:\n"); for (i = 0; i < git_index_reuc_entrycount(index); i++) { reuc = git_index_reuc_get_byindex(index, i); - + printf("%s ", reuc->path); printf("%o ", reuc->mode[0]); printf("%s\n", git_oid_allocfmt(&reuc->oid[0])); @@ -114,18 +114,18 @@ static int index_entry_eq_merge_index_entry(const struct merge_index_entry *expe test_oid = 1; } else test_oid = 0; - + if (actual->mode != expected->mode || (test_oid && git_oid_cmp(&actual->oid, &expected_oid) != 0) || git_index_entry_stage(actual) != expected->stage) return 0; - + if (actual->mode == 0 && (actual->path != NULL || strlen(expected->path) > 0)) return 0; if (actual->mode != 0 && (strcmp(actual->path, expected->path) != 0)) return 0; - + return 1; } @@ -133,7 +133,7 @@ static int name_entry_eq(const char *expected, const char *actual) { if (strlen(expected) == 0) return (actual == NULL) ? 1 : 0; - + return (strcmp(expected, actual) == 0) ? 1 : 0; } @@ -153,11 +153,11 @@ static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_d !index_entry_eq_merge_index_entry(&expected->ours.entry, &actual->our_entry) || !index_entry_eq_merge_index_entry(&expected->theirs.entry, &actual->their_entry)) return 0; - + if (expected->ours.status != actual->our_status || expected->theirs.status != actual->their_status) return 0; - + return 1; } @@ -165,48 +165,48 @@ int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_c { git_merge_diff *actual; size_t i; - + if (conflicts->length != expected_len) return 0; for (i = 0; i < expected_len; i++) { actual = conflicts->contents[i]; - + if (!index_conflict_data_eq_merge_diff(&expected[i], actual)) return 0; } - return 1; + return 1; } int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len) { - size_t i; - const git_index_entry *index_entry; - + size_t i; + const git_index_entry *index_entry; + /* dump_index_entries(&index->entries); */ - if (git_index_entrycount(index) != expected_len) - return 0; - - for (i = 0; i < expected_len; i++) { - if ((index_entry = git_index_get_byindex(index, i)) == NULL) - return 0; - + if (git_index_entrycount(index) != expected_len) + return 0; + + for (i = 0; i < expected_len; i++) { + if ((index_entry = git_index_get_byindex(index, i)) == NULL) + return 0; + if (!index_entry_eq_merge_index_entry(&expected[i], index_entry)) return 0; - } - - return 1; + } + + return 1; } int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len) { size_t i; const git_index_name_entry *name_entry; - + /* dump_names(index); */ @@ -221,26 +221,26 @@ int merge_test_names(git_index *index, const struct merge_name_entry expected[], if (! name_entry_eq_merge_name_entry(&expected[i], name_entry)) return 0; } - + return 1; } int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len) { - size_t i; + size_t i; const git_index_reuc_entry *reuc_entry; - git_oid expected_oid; - + git_oid expected_oid; + /* dump_reuc(index); */ - - if (git_index_reuc_entrycount(index) != expected_len) - return 0; - - for (i = 0; i < expected_len; i++) { - if ((reuc_entry = git_index_reuc_get_byindex(index, i)) == NULL) - return 0; + + if (git_index_reuc_entrycount(index) != expected_len) + return 0; + + for (i = 0; i < expected_len; i++) { + if ((reuc_entry = git_index_reuc_get_byindex(index, i)) == NULL) + return 0; if (strcmp(reuc_entry->path, expected[i].path) != 0 || reuc_entry->mode[0] != expected[i].ancestor_mode || @@ -268,19 +268,19 @@ int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], if (git_oid_cmp(&reuc_entry->oid[2], &expected_oid) != 0) return 0; } - } - - return 1; + } + + return 1; } int dircount(void *payload, git_buf *pathbuf) { int *entries = payload; size_t len = git_buf_len(pathbuf); - + if (len < 5 || strcmp(pathbuf->ptr + (git_buf_len(pathbuf) - 5), "/.git") != 0) (*entries)++; - + return 0; } @@ -289,22 +289,22 @@ int merge_test_workdir(git_repository *repo, const struct merge_index_entry expe size_t actual_len = 0, i; git_oid actual_oid, expected_oid; git_buf wd = GIT_BUF_INIT; - - git_buf_puts(&wd, repo->workdir); + + git_buf_puts(&wd, repo->workdir); git_path_direach(&wd, dircount, &actual_len); - + if (actual_len != expected_len) return 0; - + for (i = 0; i < expected_len; i++) { git_blob_create_fromworkdir(&actual_oid, repo, expected[i].path); git_oid_fromstr(&expected_oid, expected[i].oid_str); - + if (git_oid_cmp(&actual_oid, &expected_oid) != 0) return 0; } - + git_buf_free(&wd); - + return 1; } diff --git a/tests-clar/merge/trees/automerge.c b/tests-clar/merge/trees/automerge.c index 7592a926e..04a7beff6 100644 --- a/tests-clar/merge/trees/automerge.c +++ b/tests-clar/merge/trees/automerge.c @@ -93,7 +93,7 @@ void test_merge_trees_automerge__automerge(void) const git_index_entry *entry; git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; git_blob *blob; - + struct merge_index_entry merge_index_entries[] = { ADDED_IN_MASTER_INDEX_ENTRY, AUTOMERGEABLE_INDEX_ENTRY, @@ -105,7 +105,7 @@ void test_merge_trees_automerge__automerge(void) { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" }, UNCHANGED_INDEX_ENTRY, - }; + }; struct merge_reuc_entry merge_reuc_entries[] = { AUTOMERGEABLE_REUC_ENTRY, @@ -120,10 +120,10 @@ void test_merge_trees_automerge__automerge(void) cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL); cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE)); - + cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->oid, GIT_OBJ_BLOB)); cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, entry->file_size) == 0); - + git_index_free(index); git_blob_free(blob); } @@ -132,7 +132,7 @@ void test_merge_trees_automerge__favor_ours(void) { git_index *index; git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; - + struct merge_index_entry merge_index_entries[] = { ADDED_IN_MASTER_INDEX_ENTRY, AUTOMERGEABLE_INDEX_ENTRY, @@ -148,9 +148,9 @@ void test_merge_trees_automerge__favor_ours(void) REMOVED_IN_BRANCH_REUC_ENTRY, REMOVED_IN_MASTER_REUC_ENTRY, }; - + opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_OURS; - + cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts)); cl_assert(merge_test_index(index, merge_index_entries, 6)); @@ -163,7 +163,7 @@ void test_merge_trees_automerge__favor_theirs(void) { git_index *index; git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; - + struct merge_index_entry merge_index_entries[] = { ADDED_IN_MASTER_INDEX_ENTRY, AUTOMERGEABLE_INDEX_ENTRY, @@ -181,12 +181,12 @@ void test_merge_trees_automerge__favor_theirs(void) }; opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_THEIRS; - + cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts)); cl_assert(merge_test_index(index, merge_index_entries, 6)); cl_assert(merge_test_reuc(index, merge_reuc_entries, 4)); - + git_index_free(index); } @@ -194,7 +194,7 @@ void test_merge_trees_automerge__unrelated(void) { git_index *index; git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; - + struct merge_index_entry merge_index_entries[] = { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" }, @@ -208,10 +208,10 @@ void test_merge_trees_automerge__unrelated(void) { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, }; - + cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_UNRELATED_BRANCH, &opts)); - + cl_assert(merge_test_index(index, merge_index_entries, 11)); - + git_index_free(index); } diff --git a/tests-clar/merge/trees/modeconflict.c b/tests-clar/merge/trees/modeconflict.c index 0661ce5b3..d858b8f66 100644 --- a/tests-clar/merge/trees/modeconflict.c +++ b/tests-clar/merge/trees/modeconflict.c @@ -27,7 +27,7 @@ void test_merge_trees_modeconflict__cleanup(void) void test_merge_trees_modeconflict__df_conflict(void) { git_index *index; - + struct merge_index_entry merge_index_entries[] = { { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 2, "dir-10" }, { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 3, "dir-10" }, @@ -49,7 +49,7 @@ void test_merge_trees_modeconflict__df_conflict(void) { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" }, { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 2, "file-5/new" }, { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" }, - }; + }; cl_git_pass(merge_trees_from_branches(&index, repo, DF_SIDE1_BRANCH, DF_SIDE2_BRANCH, NULL)); diff --git a/tests-clar/merge/trees/renames.c b/tests-clar/merge/trees/renames.c index dc0564bc4..427b6bd8f 100644 --- a/tests-clar/merge/trees/renames.c +++ b/tests-clar/merge/trees/renames.c @@ -72,14 +72,14 @@ void test_merge_trees_renames__index(void) { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" }, { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }, }; - + struct merge_name_entry merge_name_entries[] = { { "3a-renamed-in-ours-deleted-in-theirs.txt", "3a-newname-in-ours-deleted-in-theirs.txt", "" }, - + { "3b-renamed-in-theirs-deleted-in-ours.txt", "", @@ -97,7 +97,7 @@ void test_merge_trees_renames__index(void) "", "4b-newname-in-theirs-added-in-ours.txt", }, - + { "5a-renamed-in-ours-added-in-theirs.txt", "5a-newname-in-ours-added-in-theirs.txt", @@ -115,13 +115,13 @@ void test_merge_trees_renames__index(void) "6-both-renamed-1-to-2-ours.txt", "6-both-renamed-1-to-2-theirs.txt", }, - + { "7-both-renamed-side-1.txt", "7-both-renamed.txt", "7-both-renamed-side-1.txt", }, - + { "7-both-renamed-side-2.txt", "7-both-renamed-side-2.txt", @@ -159,7 +159,7 @@ void test_merge_trees_renames__index(void) "", "", "241a1005cd9b980732741b74385b891142bcba28" }, - + { "1b-newname-in-theirs.txt", 0, 0, 0100644, "", @@ -194,11 +194,11 @@ void test_merge_trees_renames__index(void) cl_git_pass(merge_trees_from_branches(&index, repo, BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS, opts)); - + cl_assert(merge_test_index(index, merge_index_entries, 41)); cl_assert(merge_test_names(index, merge_name_entries, 9)); cl_assert(merge_test_reuc(index, merge_reuc_entries, 10)); - + git_index_free(index); } @@ -206,7 +206,7 @@ void test_merge_trees_renames__no_rename_index(void) { git_index *index; git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; - + struct merge_index_entry merge_index_entries[] = { { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" }, { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" }, @@ -241,13 +241,12 @@ void test_merge_trees_renames__no_rename_index(void) { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" }, { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }, }; - + cl_git_pass(merge_trees_from_branches(&index, repo, BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS, &opts)); - + cl_assert(merge_test_index(index, merge_index_entries, 32)); - + git_index_free(index); } - diff --git a/tests-clar/merge/trees/treediff.c b/tests-clar/merge/trees/treediff.c index 06ea94e0d..357859df3 100644 --- a/tests-clar/merge/trees/treediff.c +++ b/tests-clar/merge/trees/treediff.c @@ -58,37 +58,37 @@ static void test_find_differences( opts.metric->similarity = git_diff_find_similar__calc_similarity; opts.metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE; - cl_git_pass(git_oid_fromstr(&ancestor_oid, ancestor_oidstr)); - cl_git_pass(git_oid_fromstr(&ours_oid, ours_oidstr)); - cl_git_pass(git_oid_fromstr(&theirs_oid, theirs_oidstr)); - - cl_git_pass(git_tree_lookup(&ancestor_tree, repo, &ancestor_oid)); - cl_git_pass(git_tree_lookup(&ours_tree, repo, &ours_oid)); - cl_git_pass(git_tree_lookup(&theirs_tree, repo, &theirs_oid)); - + cl_git_pass(git_oid_fromstr(&ancestor_oid, ancestor_oidstr)); + cl_git_pass(git_oid_fromstr(&ours_oid, ours_oidstr)); + cl_git_pass(git_oid_fromstr(&theirs_oid, theirs_oidstr)); + + cl_git_pass(git_tree_lookup(&ancestor_tree, repo, &ancestor_oid)); + cl_git_pass(git_tree_lookup(&ours_tree, repo, &ours_oid)); + cl_git_pass(git_tree_lookup(&theirs_tree, repo, &theirs_oid)); + cl_git_pass(git_merge_diff_list__find_differences(merge_diff_list, ancestor_tree, ours_tree, theirs_tree)); cl_git_pass(git_merge_diff_list__find_renames(repo, merge_diff_list, &opts)); /* dump_merge_index(merge_index); */ - - cl_assert(treediff_conflict_data_len == merge_diff_list->conflicts.length); + + cl_assert(treediff_conflict_data_len == merge_diff_list->conflicts.length); cl_assert(merge_test_merge_conflicts(&merge_diff_list->conflicts, treediff_conflict_data, treediff_conflict_data_len)); - git_tree_free(ancestor_tree); - git_tree_free(ours_tree); - git_tree_free(theirs_tree); - + git_tree_free(ancestor_tree); + git_tree_free(ours_tree); + git_tree_free(theirs_tree); + git_merge_diff_list__free(merge_diff_list); - + git__free(opts.metric); } void test_merge_trees_treediff__simple(void) { - struct merge_index_conflict_data treediff_conflict_data[] = { + struct merge_index_conflict_data treediff_conflict_data[] = { { { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED }, @@ -96,41 +96,41 @@ void test_merge_trees_treediff__simple(void) GIT_MERGE_DIFF_NONE }, - { + { { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED }, { { 0100644, "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_BOTH_MODIFIED }, - + { { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_NONE }, - + { { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED }, { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE }, - + { { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED }, { { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_BOTH_MODIFIED }, - + { { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED }, { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_NONE }, - + { { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, { { 0, "", 0, "" }, GIT_DELTA_DELETED }, @@ -138,13 +138,13 @@ void test_merge_trees_treediff__simple(void) GIT_MERGE_DIFF_NONE }, }; - + test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_BRANCH, treediff_conflict_data, 7); } void test_merge_trees_treediff__df_conflicts(void) { - struct merge_index_conflict_data treediff_conflict_data[] = { + struct merge_index_conflict_data treediff_conflict_data[] = { { { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 0, "dir-10" }, GIT_DELTA_ADDED }, @@ -158,7 +158,7 @@ void test_merge_trees_treediff__df_conflicts(void) { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_BOTH_DELETED, }, - + { { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, @@ -172,7 +172,7 @@ void test_merge_trees_treediff__df_conflicts(void) { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_NONE, }, - + { { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, @@ -186,7 +186,7 @@ void test_merge_trees_treediff__df_conflicts(void) { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_DF_CHILD, }, - + { { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" }, GIT_DELTA_ADDED }, @@ -200,7 +200,7 @@ void test_merge_trees_treediff__df_conflicts(void) { { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, - + { { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 0, "dir-9" }, GIT_DELTA_ADDED }, @@ -235,7 +235,7 @@ void test_merge_trees_treediff__df_conflicts(void) { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_DIRECTORY_FILE, }, - + { { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, @@ -277,7 +277,7 @@ void test_merge_trees_treediff__df_conflicts(void) { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_BOTH_DELETED, }, - + { { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 0, "file-5/new" }, GIT_DELTA_ADDED }, @@ -285,7 +285,7 @@ void test_merge_trees_treediff__df_conflicts(void) GIT_MERGE_DIFF_BOTH_ADDED, }, }; - + test_find_differences(TREE_OID_DF_ANCESTOR, TREE_OID_DF_SIDE1, TREE_OID_DF_SIDE2, treediff_conflict_data, 20); } @@ -298,7 +298,7 @@ void test_merge_trees_treediff__strict_renames(void) { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, - + { { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED }, @@ -348,8 +348,8 @@ void test_merge_trees_treediff__strict_renames(void) GIT_MERGE_DIFF_NONE, }, }; - - test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES1, treediff_conflict_data, 8); + + test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES1, treediff_conflict_data, 8); } void test_merge_trees_treediff__rename_conflicts(void) @@ -375,7 +375,7 @@ void test_merge_trees_treediff__rename_conflicts(void) { { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_NONE, }, - + { { { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_MODIFIED }, @@ -389,21 +389,21 @@ void test_merge_trees_treediff__rename_conflicts(void) { { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-renamed-in-ours-edited-in-theirs.txt" }, GIT_DELTA_MODIFIED }, GIT_MERGE_DIFF_RENAMED_MODIFIED, }, - + { { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" }, GIT_DELTA_RENAMED }, { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, }, - + { { { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-renamed-in-theirs-edited-in-ours.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-renamed-in-theirs-edited-in-ours.txt" }, GIT_DELTA_MODIFIED }, { { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_RENAMED_MODIFIED, }, - + { { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, @@ -417,28 +417,28 @@ void test_merge_trees_treediff__rename_conflicts(void) { { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_BOTH_RENAMED, }, - + { { { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-renamed-in-ours-deleted-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" }, GIT_DELTA_RENAMED }, { { 0, "", 0, "" }, GIT_DELTA_DELETED }, GIT_MERGE_DIFF_RENAMED_DELETED, }, - + { { { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-renamed-in-theirs-deleted-in-ours.txt" }, GIT_DELTA_UNMODIFIED }, { { 0, "", 0, "" }, GIT_DELTA_DELETED }, { { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_RENAMED_DELETED, }, - + { { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt" }, GIT_DELTA_ADDED }, GIT_MERGE_DIFF_RENAMED_ADDED, - }, - + }, + { { { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-renamed-in-ours-added-in-theirs.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt" }, GIT_DELTA_RENAMED }, @@ -459,21 +459,21 @@ void test_merge_trees_treediff__rename_conflicts(void) { { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_RENAMED_ADDED, }, - + { { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-ours.txt" }, GIT_DELTA_RENAMED }, { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-theirs.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2, }, - + { { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed.txt" }, GIT_DELTA_RENAMED }, { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1, }, - + { { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt" }, GIT_DELTA_UNMODIFIED }, @@ -481,8 +481,7 @@ void test_merge_trees_treediff__rename_conflicts(void) GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1, }, }; - - test_find_differences(TREE_OID_RENAME_CONFLICT_ANCESTOR, + test_find_differences(TREE_OID_RENAME_CONFLICT_ANCESTOR, TREE_OID_RENAME_CONFLICT_OURS, TREE_OID_RENAME_CONFLICT_THEIRS, treediff_conflict_data, 18); } @@ -494,7 +493,7 @@ void test_merge_trees_treediff__best_renames(void) { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED }, { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_NONE, - }, + }, { { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED }, @@ -502,7 +501,7 @@ void test_merge_trees_treediff__best_renames(void) { { 0100644, "45299c1ca5e07bba1fd90843056fb559f96b1f5a", 0, "renamed-90.txt" }, GIT_DELTA_RENAMED }, GIT_MERGE_DIFF_RENAMED_MODIFIED, }, - + { { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED }, @@ -523,7 +522,7 @@ void test_merge_trees_treediff__best_renames(void) { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED }, GIT_MERGE_DIFF_MODIFIED_DELETED, }, - + { { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED }, @@ -538,6 +537,6 @@ void test_merge_trees_treediff__best_renames(void) GIT_MERGE_DIFF_NONE, }, }; - - test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES2, treediff_conflict_data, 7); + + test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES2, treediff_conflict_data, 7); } diff --git a/tests-clar/merge/trees/trivial.c b/tests-clar/merge/trees/trivial.c index e6096e2dc..bfd5dfed3 100644 --- a/tests-clar/merge/trees/trivial.c +++ b/tests-clar/merge/trees/trivial.c @@ -34,7 +34,7 @@ static int merge_trivial(git_index **index, const char *ours, const char *theirs git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; opts.automerge_flags |= automerge ? 0 : GIT_MERGE_AUTOMERGE_NONE; - + git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours); cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr)); cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid)); @@ -46,7 +46,7 @@ static int merge_trivial(git_index **index, const char *ours, const char *theirs cl_git_pass(git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit))); cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid)); - + cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit)); cl_git_pass(git_commit_tree(&our_tree, our_commit)); cl_git_pass(git_commit_tree(&their_tree, their_commit)); @@ -106,7 +106,7 @@ void test_merge_trees_trivial__3alt(void) cl_assert(entry = git_index_get_bypath(result, "new-in-3alt.txt", 0)); cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(merge_trivial_conflict_entrycount(result) == 0); - + git_index_free(result); } @@ -124,7 +124,7 @@ void test_merge_trees_trivial__4(void) cl_assert(merge_trivial_conflict_entrycount(result) == 2); cl_assert(entry = git_index_get_bypath(result, "new-and-different.txt", 2)); cl_assert(entry = git_index_get_bypath(result, "new-and-different.txt", 3)); - + git_index_free(result); } @@ -139,7 +139,7 @@ void test_merge_trees_trivial__5alt_1(void) cl_assert(entry = git_index_get_bypath(result, "new-and-same.txt", 0)); cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(merge_trivial_conflict_entrycount(result) == 0); - + git_index_free(result); } @@ -154,7 +154,7 @@ void test_merge_trees_trivial__5alt_2(void) cl_assert(entry = git_index_get_bypath(result, "modified-to-same.txt", 0)); cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(merge_trivial_conflict_entrycount(result) == 0); - + git_index_free(result); } @@ -171,7 +171,7 @@ void test_merge_trees_trivial__6(void) cl_assert(merge_trivial_conflict_entrycount(result) == 1); cl_assert(entry = git_index_get_bypath(result, "removed-in-both.txt", 1)); - + git_index_free(result); } @@ -189,7 +189,7 @@ void test_merge_trees_trivial__6_automerge(void) cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-both.txt")); cl_assert(merge_trivial_conflict_entrycount(result) == 0); - + git_index_free(result); } @@ -207,7 +207,7 @@ void test_merge_trees_trivial__8(void) cl_assert(merge_trivial_conflict_entrycount(result) == 2); cl_assert(entry = git_index_get_bypath(result, "removed-in-8.txt", 1)); cl_assert(entry = git_index_get_bypath(result, "removed-in-8.txt", 3)); - + git_index_free(result); } @@ -226,7 +226,7 @@ void test_merge_trees_trivial__8_automerge(void) cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-8.txt")); cl_assert(merge_trivial_conflict_entrycount(result) == 0); - + git_index_free(result); } @@ -244,7 +244,7 @@ void test_merge_trees_trivial__7(void) cl_assert(merge_trivial_conflict_entrycount(result) == 2); cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 1)); cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 3)); - + git_index_free(result); } @@ -262,7 +262,7 @@ void test_merge_trees_trivial__7_automerge(void) cl_assert(merge_trivial_conflict_entrycount(result) == 2); cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 1)); cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 3)); - + git_index_free(result); } @@ -280,7 +280,7 @@ void test_merge_trees_trivial__10(void) cl_assert(merge_trivial_conflict_entrycount(result) == 2); cl_assert(entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 1)); cl_assert(entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 2)); - + git_index_free(result); } @@ -299,7 +299,7 @@ void test_merge_trees_trivial__10_automerge(void) cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-10-branch.txt")); cl_assert(merge_trivial_conflict_entrycount(result) == 0); - + git_index_free(result); } @@ -317,7 +317,7 @@ void test_merge_trees_trivial__9(void) cl_assert(merge_trivial_conflict_entrycount(result) == 2); cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 1)); cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 2)); - + git_index_free(result); } @@ -335,7 +335,7 @@ void test_merge_trees_trivial__9_automerge(void) cl_assert(merge_trivial_conflict_entrycount(result) == 2); cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 1)); cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 2)); - + git_index_free(result); } @@ -354,7 +354,7 @@ void test_merge_trees_trivial__13(void) cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(merge_trivial_conflict_entrycount(result) == 0); - + git_index_free(result); } @@ -373,7 +373,7 @@ void test_merge_trees_trivial__14(void) cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(merge_trivial_conflict_entrycount(result) == 0); - + git_index_free(result); } @@ -392,6 +392,6 @@ void test_merge_trees_trivial__11(void) cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 1)); cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 2)); cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 3)); - + git_index_free(result); } diff --git a/tests-clar/refs/branches/move.c b/tests-clar/refs/branches/move.c index 7267f941d..ecf14e006 100644 --- a/tests-clar/refs/branches/move.c +++ b/tests-clar/refs/branches/move.c @@ -24,7 +24,7 @@ void test_refs_branches_move__can_move_a_local_branch(void) cl_git_pass(git_branch_move(&new_ref, original_ref, NEW_BRANCH_NAME, 0)); cl_assert_equal_s(GIT_REFS_HEADS_DIR NEW_BRANCH_NAME, git_reference_name(new_ref)); - + git_reference_free(original_ref); git_reference_free(new_ref); } @@ -32,7 +32,7 @@ void test_refs_branches_move__can_move_a_local_branch(void) void test_refs_branches_move__can_move_a_local_branch_to_a_different_namespace(void) { git_reference *original_ref, *new_ref, *newer_ref; - + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); /* Downward */ @@ -42,14 +42,14 @@ void test_refs_branches_move__can_move_a_local_branch_to_a_different_namespace(v /* Upward */ cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0)); git_reference_free(new_ref); - + git_reference_free(newer_ref); } void test_refs_branches_move__can_move_a_local_branch_to_a_partially_colliding_namespace(void) { git_reference *original_ref, *new_ref, *newer_ref; - + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); /* Downward */ @@ -59,29 +59,29 @@ void test_refs_branches_move__can_move_a_local_branch_to_a_partially_colliding_n /* Upward */ cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0)); git_reference_free(new_ref); - + git_reference_free(newer_ref); } void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_collide_with_an_existing_one(void) { git_reference *original_ref, *new_ref; - + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); cl_assert_equal_i(GIT_EEXISTS, git_branch_move(&new_ref, original_ref, "master", 0)); - + git_reference_free(original_ref); } void test_refs_branches_move__moving_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) { git_reference *original_ref, *new_ref; - + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); cl_assert_equal_i(GIT_EINVALIDSPEC, git_branch_move(&new_ref, original_ref, "Inv@{id", 0)); - + git_reference_free(original_ref); } @@ -98,11 +98,11 @@ void test_refs_branches_move__can_not_move_a_non_branch(void) void test_refs_branches_move__can_force_move_over_an_existing_branch(void) { git_reference *original_ref, *new_ref; - + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); cl_git_pass(git_branch_move(&new_ref, original_ref, "master", 1)); - + git_reference_free(original_ref); git_reference_free(new_ref); } diff --git a/tests-clar/refs/ref_helpers.c b/tests-clar/refs/ref_helpers.c index 16ab9e6ef..7676e65a7 100644 --- a/tests-clar/refs/ref_helpers.c +++ b/tests-clar/refs/ref_helpers.c @@ -11,15 +11,15 @@ int reference_is_packed(git_reference *ref) int packed; assert(ref); - + if (git_buf_joinpath(&ref_path, git_repository_path(git_reference_owner(ref)), git_reference_name(ref)) < 0) return -1; packed = !git_path_isfile(ref_path.ptr); - + git_buf_free(&ref_path); - + return packed; } diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index 74472b175..43406e239 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -557,12 +557,12 @@ void test_refs_revparse__issue_994(void) /** * $ git rev-parse blah-7-gc47800c * c47800c7266a2be04c571c04d5a6614691ea99bd - * + * * $ git rev-parse HEAD~3 * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 - * + * * $ git branch blah-7-gc47800c HEAD~3 - * + * * $ git rev-parse blah-7-gc47800c * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 */ @@ -592,15 +592,15 @@ void test_refs_revparse__try_to_retrieve_branch_before_described_tag(void) /** * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750 * a65fedf39aefe402d3bb6e24df4d4f5fe4547750 - * + * * $ git rev-parse HEAD~3 * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 - * + * * $ git branch a65fedf39aefe402d3bb6e24df4d4f5fe4547750 HEAD~3 - * + * * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750 * a65fedf39aefe402d3bb6e24df4d4f5fe4547750 - * + * * $ git rev-parse heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750 * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 */ @@ -631,12 +631,12 @@ void test_refs_revparse__try_to_retrieve_sha_before_branch(void) /** * $ git rev-parse c47800 * c47800c7266a2be04c571c04d5a6614691ea99bd - * + * * $ git rev-parse HEAD~3 * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 - * + * * $ git branch c47800 HEAD~3 - * + * * $ git rev-parse c47800 * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 */ @@ -694,4 +694,3 @@ void test_refs_revparse__parses_range_operator(void) "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE); } - diff --git a/tests-clar/refs/setter.c b/tests-clar/refs/setter.c index 713af814f..6d875f9b6 100644 --- a/tests-clar/refs/setter.c +++ b/tests-clar/refs/setter.c @@ -25,7 +25,7 @@ void test_refs_setter__update_direct(void) { git_reference *ref, *test_ref, *new_ref; git_oid id; - + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); cl_assert(git_reference_type(ref) == GIT_REF_OID); git_oid_cpy(&id, git_reference_target(ref)); @@ -48,7 +48,7 @@ void test_refs_setter__update_direct(void) void test_refs_setter__update_symbolic(void) { git_reference *head, *new_head; - + cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); cl_assert(git_reference_type(head) == GIT_REF_SYMBOLIC); cl_assert(strcmp(git_reference_symbolic_target(head), ref_master_name) == 0); @@ -56,7 +56,7 @@ void test_refs_setter__update_symbolic(void) cl_git_pass(git_reference_symbolic_set_target(&new_head, head, ref_test_name)); git_reference_free(new_head); git_reference_free(head); - + cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); cl_assert(git_reference_type(head) == GIT_REF_SYMBOLIC); cl_assert(strcmp(git_reference_symbolic_target(head), ref_test_name) == 0); @@ -68,13 +68,13 @@ void test_refs_setter__cant_update_direct_with_symbolic(void) // Overwrite an existing object id reference with a symbolic one git_reference *ref, *new; git_oid id; - + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); cl_assert(git_reference_type(ref) == GIT_REF_OID); git_oid_cpy(&id, git_reference_target(ref)); - + cl_git_fail(git_reference_symbolic_set_target(&new, ref, ref_name)); - + git_reference_free(ref); } @@ -83,7 +83,7 @@ void test_refs_setter__cant_update_symbolic_with_direct(void) // Overwrite an existing symbolic reference with an object id one git_reference *ref, *new; git_oid id; - + cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name)); cl_assert(git_reference_type(ref) == GIT_REF_OID); git_oid_cpy(&id, git_reference_target(ref)); @@ -94,6 +94,6 @@ void test_refs_setter__cant_update_symbolic_with_direct(void) /* Can't set an OID on a direct ref */ cl_git_fail(git_reference_set_target(&new, ref, &id)); - + git_reference_free(ref); } diff --git a/tests-clar/reset/hard.c b/tests-clar/reset/hard.c index 62371f83f..1c0c84135 100644 --- a/tests-clar/reset/hard.c +++ b/tests-clar/reset/hard.c @@ -105,14 +105,14 @@ void test_reset_hard__cannot_reset_in_a_bare_repository(void) static void index_entry_init(git_index *index, int side, git_oid *oid) { git_index_entry entry; - + memset(&entry, 0x0, sizeof(git_index_entry)); - + entry.path = "conflicting_file"; entry.flags = (side << GIT_IDXENTRY_STAGESHIFT); entry.mode = 0100644; git_oid_cpy(&entry.oid, oid); - + cl_git_pass(git_index_add(index, &entry)); } @@ -122,19 +122,19 @@ static void unmerged_index_init(git_index *index, int entries) int write_ours = 2; int write_theirs = 4; git_oid ancestor, ours, theirs; - + git_oid_fromstr(&ancestor, "6bb0d9f700543ba3d318ba7075fc3bd696b4287b"); git_oid_fromstr(&ours, "b19a1e93bec1317dc6097229e12afaffbfa74dc2"); git_oid_fromstr(&theirs, "950b81b7eee953d050aa05a641f8e056c85dd1bd"); - + cl_git_rewritefile("status/conflicting_file", "conflicting file\n"); - + if (entries & write_ancestor) index_entry_init(index, 1, &ancestor); - + if (entries & write_ours) index_entry_init(index, 2, &ours); - + if (entries & write_theirs) index_entry_init(index, 3, &theirs); } @@ -143,24 +143,24 @@ void test_reset_hard__resetting_reverts_unmerged(void) { git_index *index; int entries; - + /* Ensure every permutation of non-zero stage entries results in the * path being cleaned up. */ for (entries = 1; entries < 8; entries++) { cl_git_pass(git_repository_index(&index, repo)); - + unmerged_index_init(index, entries); cl_git_pass(git_index_write(index)); - + retrieve_target_from_oid(&target, repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f"); cl_git_pass(git_reset(repo, target, GIT_RESET_HARD)); - + cl_assert(git_path_exists("status/conflicting_file") == 0); - + git_object_free(target); target = NULL; - - git_index_free(index); + + git_index_free(index); } } diff --git a/tests-clar/status/submodules.c b/tests-clar/status/submodules.c index 8365a7f5a..af8707721 100644 --- a/tests-clar/status/submodules.c +++ b/tests-clar/status/submodules.c @@ -219,4 +219,3 @@ void test_status_submodules__dirty_workdir_only(void) git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts)); cl_assert_equal_i(6, counts.entry_count); } - diff --git a/tests-clar/trace/trace.c b/tests-clar/trace/trace.c index cc99cd187..87b325378 100644 --- a/tests-clar/trace/trace.c +++ b/tests-clar/trace/trace.c @@ -85,4 +85,3 @@ void test_trace_trace__writes_lower_level(void) cl_assert(written == 1); #endif } - From 79ef3be449c9d81dd0b37a30999563aa92e4679e Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 15 May 2013 14:50:05 -0700 Subject: [PATCH 234/384] Fix diff crash when last item is untracked dir When the last item in a diff was an untracked directory that only contained ignored items, the loop to scan the contents would run off the end of the iterator and dereference a NULL pointer. This includes a test that reproduces the problem and a fix. --- src/diff.c | 6 ++++-- tests-clar/diff/workdir.c | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/diff.c b/src/diff.c index f466546bb..d93506984 100644 --- a/src/diff.c +++ b/src/diff.c @@ -747,7 +747,8 @@ static int diff_scan_inside_untracked_dir( } /* look for actual untracked file */ - while (!diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) { + while (info->nitem != NULL && + !diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) { is_ignored = git_iterator_current_is_ignored(info->new_iter); /* need to recurse into non-ignored directories */ @@ -769,7 +770,8 @@ static int diff_scan_inside_untracked_dir( } /* finish off scan */ - while (!diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) { + while (info->nitem != NULL && + !diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) { if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0) break; } diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c index 94fd7165d..18182ea96 100644 --- a/tests-clar/diff/workdir.c +++ b/tests-clar/diff/workdir.c @@ -1220,3 +1220,28 @@ void test_diff_workdir__untracked_directory_scenarios(void) git_diff_list_free(diff); } + + +void test_diff_workdir__untracked_directory_comes_last(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff = NULL; + + g_repo = cl_git_sandbox_init("renames"); + + cl_git_mkfile("renames/.gitignore", "*.ign\n"); + cl_git_pass(p_mkdir("renames/zzz_untracked", 0777)); + cl_git_mkfile("renames/zzz_untracked/an.ign", "ignore me please"); + cl_git_mkfile("renames/zzz_untracked/skip.ign", "ignore me really"); + cl_git_mkfile("renames/zzz_untracked/test.ign", "ignore me now"); + + opts.context_lines = 3; + opts.interhunk_lines = 1; + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_assert(diff != NULL); + + git_diff_list_free(diff); +} From 55d3a39098bfc513b12ad6cb56658cb2f87e6a91 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 15 May 2013 14:52:12 -0700 Subject: [PATCH 235/384] Remove old symlinks before updating Unlike blob updates, symlink updates cannot be done "in place" writing over an old symlink. This means that in checkout when we realize that we can safely update a symlink, we still need to remove the old one before writing the new. --- src/checkout.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 68028dfef..4d019dbd1 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -189,6 +189,10 @@ static int checkout_action_common( action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) | CHECKOUT_ACTION__UPDATE_SUBMODULE; + /* to "update" a symlink, we must remove the old one first */ + if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL) + action |= CHECKOUT_ACTION__REMOVE; + notify = GIT_CHECKOUT_NOTIFY_UPDATED; } @@ -764,20 +768,24 @@ cleanup: } static int blob_content_to_link( - struct stat *st, git_blob *blob, const char *path, mode_t dir_mode, int can_symlink) + struct stat *st, + git_blob *blob, + const char *path, + mode_t dir_mode, + int can_symlink) { git_buf linktarget = GIT_BUF_INIT; int error; if ((error = git_futils_mkpath2file(path, dir_mode)) < 0) return error; - + if ((error = git_blob__getbuf(&linktarget, blob)) < 0) return error; if (can_symlink) { if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0) - giterr_set(GITERR_CHECKOUT, "Could not create symlink %s\n", path); + giterr_set(GITERR_OS, "Could not create symlink %s\n", path); } else { error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path); } From dcb0f7c061554de06db5879361b22eab3517a4ee Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 15 May 2013 14:54:02 -0700 Subject: [PATCH 236/384] Fix checkout of submodules with no .gitmodules It is possible for there to be a submodule in a repository with no .gitmodules file (for example, if the user forgot to commit the .gitmodules file). In this case, core Git will just create an empty directory as a placeholder for the submodule but otherwise ignore it. We were generating an error and stopping the checkout. This makes our behavior match that of core git. --- include/git2/index.h | 2 ++ src/checkout.c | 57 ++++++++++++++++++++++++++++---------------- src/index.c | 2 +- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/include/git2/index.h b/include/git2/index.h index d23c3a8ea..bde38a9dd 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -57,6 +57,8 @@ GIT_BEGIN_DECL #define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE) +#define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) + /** Time used in a git index entry */ typedef struct { git_time_t seconds; diff --git a/src/checkout.c b/src/checkout.c index 4d019dbd1..5820f626a 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -820,6 +820,31 @@ static int checkout_update_index( return git_index_add(data->index, &entry); } +static int checkout_submodule_update_index( + checkout_data *data, + const git_diff_file *file) +{ + struct stat st; + + /* update the index unless prevented */ + if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0) + return 0; + + git_buf_truncate(&data->path, data->workdir_len); + if (git_buf_puts(&data->path, file->path) < 0) + return -1; + + if (p_stat(git_buf_cstr(&data->path), &st) < 0) { + giterr_set( + GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path); + return GIT_ENOTFOUND; + } + + st.st_mode = GIT_FILEMODE_COMMIT; + + return checkout_update_index(data, file, &st); +} + static int checkout_submodule( checkout_data *data, const git_diff_file *file) @@ -836,8 +861,17 @@ static int checkout_submodule( data->opts.dir_mode, GIT_MKDIR_PATH)) < 0) return error; - if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0) + if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0) { + /* I've observed repos with submodules in the tree that do not + * have a .gitmodules - core Git just makes an empty directory + */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); + return checkout_submodule_update_index(data, file); + } + return error; + } /* TODO: Support checkout_strategy options. Two circumstances: * 1 - submodule already checked out, but we need to move the HEAD @@ -848,26 +882,7 @@ static int checkout_submodule( * command should probably be able to. Do we need a submodule callback? */ - /* update the index unless prevented */ - if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) { - struct stat st; - - git_buf_truncate(&data->path, data->workdir_len); - if (git_buf_puts(&data->path, file->path) < 0) - return -1; - - if ((error = p_stat(git_buf_cstr(&data->path), &st)) < 0) { - giterr_set( - GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path); - return error; - } - - st.st_mode = GIT_FILEMODE_COMMIT; - - error = checkout_update_index(data, file, &st); - } - - return error; + return checkout_submodule_update_index(data, file); } static void report_progress( diff --git a/src/index.c b/src/index.c index 53edb4874..bdbe545bf 100644 --- a/src/index.c +++ b/src/index.c @@ -106,7 +106,7 @@ static void index_entry_reuc_free(git_index_reuc_entry *reuc); GIT_INLINE(int) index_entry_stage(const git_index_entry *entry) { - return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT; + return GIT_IDXENTRY_STAGE(entry); } static int index_srch(const void *key, const void *array_member) From 09fae31d45c3b9112cb7ce7d5c7d1d63840e407d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 15 May 2013 14:58:26 -0700 Subject: [PATCH 237/384] Improve robustness of diff rename detection Under some strange circumstances, diffs can end up listing files that we can't actually open successfully. Instead of aborting the git_diff_find_similar, this makes it so that those files just won't be considered as valid rename/copy targets instead. --- src/diff_tform.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/diff_tform.c b/src/diff_tform.c index 201a0e896..de2d5f616 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -386,8 +386,12 @@ static int similarity_calc( /* TODO: apply wd-to-odb filters to file data if necessary */ - if (!(error = git_buf_joinpath( - &path, git_repository_workdir(diff->repo), file->path))) + if ((error = git_buf_joinpath( + &path, git_repository_workdir(diff->repo), file->path)) < 0) + return error; + + /* if path is not a regular file, just skip this item */ + if (git_path_isfile(path.ptr)) error = opts->metric->file_signature( &cache[file_idx], file, path.ptr, opts->metric->payload); @@ -398,8 +402,11 @@ static int similarity_calc( /* TODO: add max size threshold a la diff? */ - if ((error = git_blob_lookup(&blob, diff->repo, &file->oid)) < 0) - return error; + if (git_blob_lookup(&blob, diff->repo, &file->oid) < 0) { + /* if lookup fails, just skip this item in similarity calc */ + giterr_clear(); + return 0; + } blobsize = git_blob_rawsize(blob); if (!git__is_sizet(blobsize)) /* ? what to do ? */ @@ -437,7 +444,7 @@ static int similarity_measure( return -1; if (!cache[b_idx] && similarity_calc(diff, opts, b_idx, cache) < 0) return -1; - + /* some metrics may not wish to process this file (too big / too small) */ if (!cache[a_idx] || !cache[b_idx]) return 0; From 72b3dd4a5ca2f6572e741c243cd973963d0ef419 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 15 May 2013 15:23:33 -0700 Subject: [PATCH 238/384] Use GIT_IDXENTRY_STAGE macro Since I added the GIT_IDXENTRY_STAGE macro to extract the stage from a git_index_entry, we probably don't need an internal inline function to do the same thing. --- src/index.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/index.c b/src/index.c index bdbe545bf..35f282b35 100644 --- a/src/index.c +++ b/src/index.c @@ -104,11 +104,6 @@ static int index_find(size_t *at_pos, git_index *index, const char *path, int st static void index_entry_free(git_index_entry *entry); static void index_entry_reuc_free(git_index_reuc_entry *reuc); -GIT_INLINE(int) index_entry_stage(const git_index_entry *entry) -{ - return GIT_IDXENTRY_STAGE(entry); -} - static int index_srch(const void *key, const void *array_member) { const struct entry_srch_key *srch_key = key; @@ -118,7 +113,7 @@ static int index_srch(const void *key, const void *array_member) ret = strcmp(srch_key->path, entry->path); if (ret == 0) - ret = srch_key->stage - index_entry_stage(entry); + ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry); return ret; } @@ -132,7 +127,7 @@ static int index_isrch(const void *key, const void *array_member) ret = strcasecmp(srch_key->path, entry->path); if (ret == 0) - ret = srch_key->stage - index_entry_stage(entry); + ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry); return ret; } @@ -170,7 +165,7 @@ static int index_cmp(const void *a, const void *b) diff = strcmp(entry_a->path, entry_b->path); if (diff == 0) - diff = (index_entry_stage(entry_a) - index_entry_stage(entry_b)); + diff = (GIT_IDXENTRY_STAGE(entry_a) - GIT_IDXENTRY_STAGE(entry_b)); return diff; } @@ -184,7 +179,7 @@ static int index_icmp(const void *a, const void *b) diff = strcasecmp(entry_a->path, entry_b->path); if (diff == 0) - diff = (index_entry_stage(entry_a) - index_entry_stage(entry_b)); + diff = (GIT_IDXENTRY_STAGE(entry_a) - GIT_IDXENTRY_STAGE(entry_b)); return diff; } @@ -721,7 +716,7 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace) entry->flags |= GIT_IDXENTRY_NAMEMASK; /* look if an entry with this path already exists */ - if (!index_find(&position, index, entry->path, index_entry_stage(entry))) { + if (!index_find(&position, index, entry->path, GIT_IDXENTRY_STAGE(entry))) { existing = (git_index_entry **)&index->entries.contents[position]; /* update filemode to existing values if stat is not trusted */ @@ -869,7 +864,7 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage) if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0) break; - if (index_entry_stage(entry) != stage) { + if (GIT_IDXENTRY_STAGE(entry) != stage) { ++pos; continue; } @@ -1008,7 +1003,7 @@ int git_index_conflict_get(git_index_entry **ancestor_out, if (index->entries_cmp_path(conflict_entry->path, path) != 0) break; - stage = index_entry_stage(conflict_entry); + stage = GIT_IDXENTRY_STAGE(conflict_entry); switch (stage) { case 3: @@ -1050,7 +1045,7 @@ int git_index_conflict_remove(git_index *index, const char *path) if (index->entries_cmp_path(conflict_entry->path, path) != 0) break; - if (index_entry_stage(conflict_entry) == 0) { + if (GIT_IDXENTRY_STAGE(conflict_entry) == 0) { pos++; continue; } @@ -1069,7 +1064,7 @@ static int index_conflicts_match(const git_vector *v, size_t idx) { git_index_entry *entry = git_vector_get(v, idx); - if (index_entry_stage(entry) > 0) { + if (GIT_IDXENTRY_STAGE(entry) > 0) { index_entry_free(entry); return 1; } @@ -1091,7 +1086,7 @@ int git_index_has_conflicts(const git_index *index) assert(index); git_vector_foreach(&index->entries, i, entry) { - if (index_entry_stage(entry) > 0) + if (GIT_IDXENTRY_STAGE(entry) > 0) return 1; } @@ -1858,7 +1853,7 @@ static int write_index(git_index *index, git_filebuf *file) int git_index_entry_stage(const git_index_entry *entry) { - return index_entry_stage(entry); + return GIT_IDXENTRY_STAGE(entry); } typedef struct read_tree_data { From 487884a9306e744eaebe659a7d0edb1c0c6c7ba7 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 13 May 2013 16:07:29 -0700 Subject: [PATCH 239/384] Improve docs for git_index_entry flag masks The constants for extracting data from git_index_entry flags and flags_extended are not named in a way that makes it easy to know where to use each one. This improves the docs for the flags (and slightly reorganizes them), so it should be more obvious. --- include/git2/index.h | 58 ++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/include/git2/index.h b/include/git2/index.h index bde38a9dd..42cc4d640 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -21,18 +21,44 @@ */ GIT_BEGIN_DECL +/** + * Bitmasks for on-disk fields of `git_index_entry` `flags` + * + * These bitmasks match the four fields in the `git_index_entry` `flags` + * value both in memory and on disk. You can use them to interpret the + * data in the `flags`. + */ #define GIT_IDXENTRY_NAMEMASK (0x0fff) #define GIT_IDXENTRY_STAGEMASK (0x3000) #define GIT_IDXENTRY_EXTENDED (0x4000) #define GIT_IDXENTRY_VALID (0x8000) #define GIT_IDXENTRY_STAGESHIFT 12 -/* - * Flags are divided into two parts: in-memory flags and - * on-disk ones. Flags in GIT_IDXENTRY_EXTENDED_FLAGS - * will get saved on-disk. +/** + * Bitmasks for on-disk fields of `git_index_entry` `flags_extended` * - * In-memory only flags: + * In memory, the `flags_extended` fields are divided into two parts: the + * fields that are read from and written to disk, and other fields that + * in-memory only and used by libgit2. Only the flags in + * `GIT_IDXENTRY_EXTENDED_FLAGS` will get saved on-disk. + * + * These bitmasks match the three fields in the `git_index_entry` + * `flags_extended` value that belong on disk. You can use them to + * interpret the data in the `flags_extended`. + */ +#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13) +#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14) +/* GIT_IDXENTRY_EXTENDED2 is reserved for future extension */ +#define GIT_IDXENTRY_EXTENDED2 (1 << 15) + +#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE) + +/** + * Bitmasks for in-memory only fields of `git_index_entry` `flags_extended` + * + * These bitmasks match the other fields in the `git_index_entry` + * `flags_extended` value that are only used in-memory by libgit2. You + * can use them to interpret the data in the `flags_extended`. */ #define GIT_IDXENTRY_UPDATE (1 << 0) #define GIT_IDXENTRY_REMOVE (1 << 1) @@ -47,15 +73,6 @@ GIT_BEGIN_DECL #define GIT_IDXENTRY_UNPACKED (1 << 8) #define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9) -/* - * Extended on-disk flags: - */ -#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13) -#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14) -/* GIT_IDXENTRY_EXTENDED2 is for future extension */ -#define GIT_IDXENTRY_EXTENDED2 (1 << 15) - -#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE) #define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) @@ -87,12 +104,12 @@ typedef struct git_index_entry { } git_index_entry; /** Capabilities of system that affect index actions. */ -enum { +typedef enum { GIT_INDEXCAP_IGNORE_CASE = 1, GIT_INDEXCAP_NO_FILEMODE = 2, GIT_INDEXCAP_NO_SYMLINKS = 4, GIT_INDEXCAP_FROM_OWNER = ~0u -}; +} git_indexcap_t; /** @name Index File Functions * @@ -429,7 +446,7 @@ GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *pat * @return 0 or an error code */ GIT_EXTERN(int) git_index_conflict_add( - git_index *index, + git_index *index, const git_index_entry *ancestor_entry, const git_index_entry *our_entry, const git_index_entry *their_entry); @@ -447,7 +464,12 @@ GIT_EXTERN(int) git_index_conflict_add( * @param index an existing index object * @param path path to search */ -GIT_EXTERN(int) git_index_conflict_get(git_index_entry **ancestor_out, git_index_entry **our_out, git_index_entry **their_out, git_index *index, const char *path); +GIT_EXTERN(int) git_index_conflict_get( + git_index_entry **ancestor_out, + git_index_entry **our_out, + git_index_entry **their_out, + git_index *index, + const char *path); /** * Removes the index entries that represent a conflict of a single file. From 797dfb28fea6a3db542dc32d11a530570e147b73 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 13 May 2013 16:09:33 -0700 Subject: [PATCH 240/384] Add APIs to dup and free git_index_entrys This adds git_index_entry_dup to make a copy of an existing entry and git_index_entry_free to release the memory of the copy. It also updates the documentation for git_index_get_bypath and git_index_get_byindex to make it clear that the returned structure should *not* be modified. --- include/git2/index.h | 48 ++++++++++++++++++++++++++++++---------- src/index.c | 45 +++++++++++++++++++++++++++++++++++++ tests-clar/index/tests.c | 48 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 12 deletions(-) diff --git a/include/git2/index.h b/include/git2/index.h index 42cc4d640..cfb55ae5b 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -73,6 +73,8 @@ GIT_BEGIN_DECL #define GIT_IDXENTRY_UNPACKED (1 << 8) #define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9) +#define GIT_IDXENTRY_ALLOCATED (1 << 10) +#define GIT_IDXENTRY_ALLOCATED_PATH (1 << 11) #define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) @@ -284,11 +286,9 @@ GIT_EXTERN(void) git_index_clear(git_index *index); /** * Get a pointer to one of the entries in the index * - * The values of this entry can be modified (except the path) - * and the changes will be written back to disk on the next - * write() call. - * - * The entry should not be freed by the caller. + * The entry is not modifiable and should not be freed. If you need a + * permanent copy of the entry, use `git_index_entry_dup()` (after which + * you will be responsible for calling `git_index_entry_free()`) * * @param index an existing index object * @param n the position of the entry @@ -300,11 +300,9 @@ GIT_EXTERN(const git_index_entry *) git_index_get_byindex( /** * Get a pointer to one of the entries in the index * - * The values of this entry can be modified (except the path) - * and the changes will be written back to disk on the next - * write() call. - * - * The entry should not be freed by the caller. + * The entry is not modifiable and should not be freed. If you need a + * permanent copy of the entry, use `git_index_entry_dup()` (after which + * you will be responsible for calling `git_index_entry_free()`). * * @param index an existing index object * @param path path to search @@ -354,8 +352,7 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en /** * Return the stage number from a git index entry * - * This entry is calculated from the entry's flag - * attribute like this: + * This entry is calculated from the entry's flag attribute like this: * * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT * @@ -364,6 +361,33 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en */ GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry); +/** + * Make a copy of an entry that you can keep + * + * The `git_index_entry` pointers that are returned by the accessor + * functions are `const git_index_entry *` so they can't be modified + * and their lifetime as objects matches that of the `git_index`. + * + * This function allows you to make a copy of those entries that can + * be modified and which you are responsible for freeing. + * + * @param entry The entry to be copied + * @returns Newly allocated entry (or NULL if allocation failure) + */ +GIT_EXTERN(git_index_entry *) git_index_entry_dup(const git_index_entry *entry); + +/** + * Release the memory for a git_index_entry + * + * You should only call this on `git_index_entry` objects that you have + * obtained through `git_index_entry_dup()`. It is an error to call this + * on the values returned by `git_index_get_byindex()` or + * `git_index_get_bypath()`. + * + * @param entry The entry to be freed + */ +GIT_EXTERN(void) git_index_entry_free(git_index_entry *entry); + /**@}*/ /** @name Workdir Index Entry Functions diff --git a/src/index.c b/src/index.c index f767dfab7..f68c81769 100644 --- a/src/index.c +++ b/src/index.c @@ -550,6 +550,51 @@ const git_index_entry *git_index_get_bypath( return git_index_get_byindex(index, pos); } +typedef struct { + git_index_entry entry; + char pathdata[GIT_FLEX_ARRAY]; +} git_index_entry_with_path; + +git_index_entry *git_index_entry_dup(const git_index_entry *src) +{ + git_index_entry_with_path *tgt; + size_t pathlen; + + if (!src) + return NULL; + + pathlen = strlen(src->path); + + tgt = git__calloc(sizeof(git_index_entry_with_path) + pathlen + 1, 1); + if (!tgt) + return NULL; + + memcpy(&tgt->entry, src, sizeof(tgt->entry)); + tgt->entry.flags_extended |= GIT_IDXENTRY_ALLOCATED; + + memcpy(tgt->pathdata, src->path, pathlen + 1); + tgt->entry.path = tgt->pathdata; + + return (git_index_entry *)tgt; +} + +void git_index_entry_free(git_index_entry *entry) +{ + assert(entry); + + if (!(entry->flags_extended & GIT_IDXENTRY_ALLOCATED)) + return; + + if ((entry->flags_extended & GIT_IDXENTRY_ALLOCATED_PATH) != 0 && + entry->path != ((git_index_entry_with_path *)entry)->pathdata) + git__free(entry->path); + + /* ward off accidental double free */ + entry->flags_extended = (entry->flags_extended & ~GIT_IDXENTRY_ALLOCATED); + + git__free(entry); +} + void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st) { entry->ctime.seconds = (git_time_t)st->st_ctime; diff --git a/tests-clar/index/tests.c b/tests-clar/index/tests.c index 88e374e6e..565f11073 100644 --- a/tests-clar/index/tests.c +++ b/tests-clar/index/tests.c @@ -416,3 +416,51 @@ void test_index_tests__remove_directory(void) git_repository_free(repo); cl_fixture_cleanup("index_test"); } + +void test_index_tests__dup_and_free_entries(void) +{ + git_repository *repo; + git_index *index; + const git_index_entry *entry; + git_index_entry *dup1, *dup2; + + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + cl_git_pass(git_repository_index(&index, repo)); + + cl_assert(entry = git_index_get_bypath(index, "COPYING", 0)); + cl_assert((entry->flags_extended & GIT_IDXENTRY_ALLOCATED) == 0); + + cl_assert(dup1 = git_index_entry_dup(entry)); + cl_assert((dup1->flags_extended & GIT_IDXENTRY_ALLOCATED) != 0); + + cl_assert_equal_s(entry->path, dup1->path); + cl_assert(git_oid_equal(&entry->oid, &dup1->oid)); + + cl_assert(entry = git_index_get_byindex(index, 0)); + cl_assert((entry->flags_extended & GIT_IDXENTRY_ALLOCATED) == 0); + + cl_assert(dup2 = git_index_entry_dup(entry)); + cl_assert((dup2->flags_extended & GIT_IDXENTRY_ALLOCATED) != 0); + + cl_assert_equal_s(entry->path, dup2->path); + cl_assert(git_oid_equal(&entry->oid, &dup2->oid)); + + git_index_free(index); + git_repository_free(repo); + + /* entry is no longer pointing to valid memory, but dup1 and dup2 are */ + + cl_assert_equal_s("COPYING", dup1->path); + git_index_entry_free(dup1); + + cl_assert_equal_s(".HEADER", dup2->path); + + /* what would a binding that wanted to set the path do? */ + dup2->path = git__strdup("newpath"); + dup2->flags_extended |= GIT_IDXENTRY_ALLOCATED_PATH; + git_index_entry_free(dup2); + + /* at this point there should be no memory leaks nor double-frees in + * this function; that will have to be checked by an external tool. + */ +} From 96c01991c166a25d8906c031422f4215edeb8ba0 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 15 May 2013 09:24:51 -0700 Subject: [PATCH 241/384] Remove entry dup/free functions and fix comments This removes the functions to duplicate and free copies of a git_index_entry and updates the comments to explain that you should just use the public definition of the struct as needed. --- include/git2/index.h | 46 +++++++------------------------------- src/index.c | 45 ------------------------------------- tests-clar/index/tests.c | 48 ---------------------------------------- 3 files changed, 8 insertions(+), 131 deletions(-) diff --git a/include/git2/index.h b/include/git2/index.h index cfb55ae5b..d6aa183b4 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -34,6 +34,8 @@ GIT_BEGIN_DECL #define GIT_IDXENTRY_VALID (0x8000) #define GIT_IDXENTRY_STAGESHIFT 12 +#define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) + /** * Bitmasks for on-disk fields of `git_index_entry` `flags_extended` * @@ -73,11 +75,6 @@ GIT_BEGIN_DECL #define GIT_IDXENTRY_UNPACKED (1 << 8) #define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9) -#define GIT_IDXENTRY_ALLOCATED (1 << 10) -#define GIT_IDXENTRY_ALLOCATED_PATH (1 << 11) - -#define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) - /** Time used in a git index entry */ typedef struct { git_time_t seconds; @@ -286,9 +283,9 @@ GIT_EXTERN(void) git_index_clear(git_index *index); /** * Get a pointer to one of the entries in the index * - * The entry is not modifiable and should not be freed. If you need a - * permanent copy of the entry, use `git_index_entry_dup()` (after which - * you will be responsible for calling `git_index_entry_free()`) + * The entry is not modifiable and should not be freed. Because the + * `git_index_entry` struct is a publicly defined struct, you should + * be able to make your own permanent copy of the data if necessary. * * @param index an existing index object * @param n the position of the entry @@ -300,9 +297,9 @@ GIT_EXTERN(const git_index_entry *) git_index_get_byindex( /** * Get a pointer to one of the entries in the index * - * The entry is not modifiable and should not be freed. If you need a - * permanent copy of the entry, use `git_index_entry_dup()` (after which - * you will be responsible for calling `git_index_entry_free()`). + * The entry is not modifiable and should not be freed. Because the + * `git_index_entry` struct is a publicly defined struct, you should + * be able to make your own permanent copy of the data if necessary. * * @param index an existing index object * @param path path to search @@ -361,33 +358,6 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en */ GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry); -/** - * Make a copy of an entry that you can keep - * - * The `git_index_entry` pointers that are returned by the accessor - * functions are `const git_index_entry *` so they can't be modified - * and their lifetime as objects matches that of the `git_index`. - * - * This function allows you to make a copy of those entries that can - * be modified and which you are responsible for freeing. - * - * @param entry The entry to be copied - * @returns Newly allocated entry (or NULL if allocation failure) - */ -GIT_EXTERN(git_index_entry *) git_index_entry_dup(const git_index_entry *entry); - -/** - * Release the memory for a git_index_entry - * - * You should only call this on `git_index_entry` objects that you have - * obtained through `git_index_entry_dup()`. It is an error to call this - * on the values returned by `git_index_get_byindex()` or - * `git_index_get_bypath()`. - * - * @param entry The entry to be freed - */ -GIT_EXTERN(void) git_index_entry_free(git_index_entry *entry); - /**@}*/ /** @name Workdir Index Entry Functions diff --git a/src/index.c b/src/index.c index f68c81769..f767dfab7 100644 --- a/src/index.c +++ b/src/index.c @@ -550,51 +550,6 @@ const git_index_entry *git_index_get_bypath( return git_index_get_byindex(index, pos); } -typedef struct { - git_index_entry entry; - char pathdata[GIT_FLEX_ARRAY]; -} git_index_entry_with_path; - -git_index_entry *git_index_entry_dup(const git_index_entry *src) -{ - git_index_entry_with_path *tgt; - size_t pathlen; - - if (!src) - return NULL; - - pathlen = strlen(src->path); - - tgt = git__calloc(sizeof(git_index_entry_with_path) + pathlen + 1, 1); - if (!tgt) - return NULL; - - memcpy(&tgt->entry, src, sizeof(tgt->entry)); - tgt->entry.flags_extended |= GIT_IDXENTRY_ALLOCATED; - - memcpy(tgt->pathdata, src->path, pathlen + 1); - tgt->entry.path = tgt->pathdata; - - return (git_index_entry *)tgt; -} - -void git_index_entry_free(git_index_entry *entry) -{ - assert(entry); - - if (!(entry->flags_extended & GIT_IDXENTRY_ALLOCATED)) - return; - - if ((entry->flags_extended & GIT_IDXENTRY_ALLOCATED_PATH) != 0 && - entry->path != ((git_index_entry_with_path *)entry)->pathdata) - git__free(entry->path); - - /* ward off accidental double free */ - entry->flags_extended = (entry->flags_extended & ~GIT_IDXENTRY_ALLOCATED); - - git__free(entry); -} - void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st) { entry->ctime.seconds = (git_time_t)st->st_ctime; diff --git a/tests-clar/index/tests.c b/tests-clar/index/tests.c index 565f11073..88e374e6e 100644 --- a/tests-clar/index/tests.c +++ b/tests-clar/index/tests.c @@ -416,51 +416,3 @@ void test_index_tests__remove_directory(void) git_repository_free(repo); cl_fixture_cleanup("index_test"); } - -void test_index_tests__dup_and_free_entries(void) -{ - git_repository *repo; - git_index *index; - const git_index_entry *entry; - git_index_entry *dup1, *dup2; - - cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); - cl_git_pass(git_repository_index(&index, repo)); - - cl_assert(entry = git_index_get_bypath(index, "COPYING", 0)); - cl_assert((entry->flags_extended & GIT_IDXENTRY_ALLOCATED) == 0); - - cl_assert(dup1 = git_index_entry_dup(entry)); - cl_assert((dup1->flags_extended & GIT_IDXENTRY_ALLOCATED) != 0); - - cl_assert_equal_s(entry->path, dup1->path); - cl_assert(git_oid_equal(&entry->oid, &dup1->oid)); - - cl_assert(entry = git_index_get_byindex(index, 0)); - cl_assert((entry->flags_extended & GIT_IDXENTRY_ALLOCATED) == 0); - - cl_assert(dup2 = git_index_entry_dup(entry)); - cl_assert((dup2->flags_extended & GIT_IDXENTRY_ALLOCATED) != 0); - - cl_assert_equal_s(entry->path, dup2->path); - cl_assert(git_oid_equal(&entry->oid, &dup2->oid)); - - git_index_free(index); - git_repository_free(repo); - - /* entry is no longer pointing to valid memory, but dup1 and dup2 are */ - - cl_assert_equal_s("COPYING", dup1->path); - git_index_entry_free(dup1); - - cl_assert_equal_s(".HEADER", dup2->path); - - /* what would a binding that wanted to set the path do? */ - dup2->path = git__strdup("newpath"); - dup2->flags_extended |= GIT_IDXENTRY_ALLOCATED_PATH; - git_index_entry_free(dup2); - - /* at this point there should be no memory leaks nor double-frees in - * this function; that will have to be checked by an external tool. - */ -} From 89251b283b8dd0bba2062519d9f3f1b212f8a7a8 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 15 May 2013 16:25:11 -0700 Subject: [PATCH 242/384] Update index.h docs Move the git_index_entry to the very top, since it provides the main structure that needs to be understood by the reader, then move the bitmasks for the flags and the flags_extended under that since they are details for looking at particular fields of the structure. --- include/git2/index.h | 127 ++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 56 deletions(-) diff --git a/include/git2/index.h b/include/git2/index.h index d6aa183b4..8a1ccce55 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -21,68 +21,29 @@ */ GIT_BEGIN_DECL -/** - * Bitmasks for on-disk fields of `git_index_entry` `flags` - * - * These bitmasks match the four fields in the `git_index_entry` `flags` - * value both in memory and on disk. You can use them to interpret the - * data in the `flags`. - */ -#define GIT_IDXENTRY_NAMEMASK (0x0fff) -#define GIT_IDXENTRY_STAGEMASK (0x3000) -#define GIT_IDXENTRY_EXTENDED (0x4000) -#define GIT_IDXENTRY_VALID (0x8000) -#define GIT_IDXENTRY_STAGESHIFT 12 - -#define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) - -/** - * Bitmasks for on-disk fields of `git_index_entry` `flags_extended` - * - * In memory, the `flags_extended` fields are divided into two parts: the - * fields that are read from and written to disk, and other fields that - * in-memory only and used by libgit2. Only the flags in - * `GIT_IDXENTRY_EXTENDED_FLAGS` will get saved on-disk. - * - * These bitmasks match the three fields in the `git_index_entry` - * `flags_extended` value that belong on disk. You can use them to - * interpret the data in the `flags_extended`. - */ -#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13) -#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14) -/* GIT_IDXENTRY_EXTENDED2 is reserved for future extension */ -#define GIT_IDXENTRY_EXTENDED2 (1 << 15) - -#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE) - -/** - * Bitmasks for in-memory only fields of `git_index_entry` `flags_extended` - * - * These bitmasks match the other fields in the `git_index_entry` - * `flags_extended` value that are only used in-memory by libgit2. You - * can use them to interpret the data in the `flags_extended`. - */ -#define GIT_IDXENTRY_UPDATE (1 << 0) -#define GIT_IDXENTRY_REMOVE (1 << 1) -#define GIT_IDXENTRY_UPTODATE (1 << 2) -#define GIT_IDXENTRY_ADDED (1 << 3) - -#define GIT_IDXENTRY_HASHED (1 << 4) -#define GIT_IDXENTRY_UNHASHED (1 << 5) -#define GIT_IDXENTRY_WT_REMOVE (1 << 6) /* remove in work directory */ -#define GIT_IDXENTRY_CONFLICTED (1 << 7) - -#define GIT_IDXENTRY_UNPACKED (1 << 8) -#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9) - -/** Time used in a git index entry */ +/** Time structure used in a git index entry */ typedef struct { git_time_t seconds; /* nsec should not be stored as time_t compatible */ unsigned int nanoseconds; } git_index_time; -/** Memory representation of a file entry in the index. */ +/** + * In-memory representation of a file entry in the index. + * + * This is a public structure that represents a file entry in the index. + * The meaning of the fields corresponds to core Git's documentation (in + * "Documentation/technical/index-format.txt"). + * + * The `flags` field consists of a number of bit fields which can be + * accessed via the first set of `GIT_IDXENTRY_...` bitmasks below. These + * flags are all read from and persisted to disk. + * + * The `flags_extended` field also has a number of bit fields which can be + * accessed via the later `GIT_IDXENTRY_...` bitmasks below. Some of + * these flags are read from and written to disk, but some are set aside + * for in-memory only reference. + */ typedef struct git_index_entry { git_index_time ctime; git_index_time mtime; @@ -102,6 +63,60 @@ typedef struct git_index_entry { char *path; } git_index_entry; +/** + * Bitmasks for on-disk fields of `git_index_entry`'s `flags` + * + * These bitmasks match the four fields in the `git_index_entry` `flags` + * value both in memory and on disk. You can use them to interpret the + * data in the `flags`. + */ +#define GIT_IDXENTRY_NAMEMASK (0x0fff) +#define GIT_IDXENTRY_STAGEMASK (0x3000) +#define GIT_IDXENTRY_EXTENDED (0x4000) +#define GIT_IDXENTRY_VALID (0x8000) +#define GIT_IDXENTRY_STAGESHIFT 12 + +#define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) + +/** + * Bitmasks for on-disk fields of `git_index_entry`'s `flags_extended` + * + * In memory, the `flags_extended` fields are divided into two parts: the + * fields that are read from and written to disk, and other fields that + * in-memory only and used by libgit2. Only the flags in + * `GIT_IDXENTRY_EXTENDED_FLAGS` will get saved on-disk. + * + * These bitmasks match the three fields in the `git_index_entry` + * `flags_extended` value that belong on disk. You can use them to + * interpret the data in the `flags_extended`. + */ +#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13) +#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14) +/* GIT_IDXENTRY_EXTENDED2 is reserved for future extension */ +#define GIT_IDXENTRY_EXTENDED2 (1 << 15) + +#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE) + +/** + * Bitmasks for in-memory only fields of `git_index_entry`'s `flags_extended` + * + * These bitmasks match the other fields in the `git_index_entry` + * `flags_extended` value that are only used in-memory by libgit2. You + * can use them to interpret the data in the `flags_extended`. + */ +#define GIT_IDXENTRY_UPDATE (1 << 0) +#define GIT_IDXENTRY_REMOVE (1 << 1) +#define GIT_IDXENTRY_UPTODATE (1 << 2) +#define GIT_IDXENTRY_ADDED (1 << 3) + +#define GIT_IDXENTRY_HASHED (1 << 4) +#define GIT_IDXENTRY_UNHASHED (1 << 5) +#define GIT_IDXENTRY_WT_REMOVE (1 << 6) /* remove in work directory */ +#define GIT_IDXENTRY_CONFLICTED (1 << 7) + +#define GIT_IDXENTRY_UNPACKED (1 << 8) +#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9) + /** Capabilities of system that affect index actions. */ typedef enum { GIT_INDEXCAP_IGNORE_CASE = 1, From 7026ad893b5ba54b11c7397f10d6fdf64683062f Mon Sep 17 00:00:00 2001 From: Linquize Date: Thu, 16 May 2013 21:08:55 +0800 Subject: [PATCH 243/384] calloc() to initialize memory --- src/indexer.c | 4 ++-- src/transports/smart_protocol.c | 2 +- src/win32/dir.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/indexer.c b/src/indexer.c index 91b7ba5d9..1b5339f23 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -259,7 +259,7 @@ static int store_object(git_indexer_stream *idx) entry = git__calloc(1, sizeof(*entry)); GITERR_CHECK_ALLOC(entry); - pentry = git__malloc(sizeof(struct git_pack_entry)); + pentry = git__calloc(1, sizeof(struct git_pack_entry)); GITERR_CHECK_ALLOC(pentry); git_hash_final(&oid, ctx); @@ -328,7 +328,7 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent return -1; } - pentry = git__malloc(sizeof(struct git_pack_entry)); + pentry = git__calloc(1, sizeof(struct git_pack_entry)); GITERR_CHECK_ALLOC(pentry); git_oid_cpy(&pentry->sha1, &oid); diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 67d309523..636616717 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -576,7 +576,7 @@ static int add_push_report_pkt(git_push *push, git_pkt *pkt) switch (pkt->type) { case GIT_PKT_OK: - status = git__malloc(sizeof(push_status)); + status = git__calloc(1, sizeof(push_status)); GITERR_CHECK_ALLOC(status); status->msg = NULL; status->ref = git__strdup(((git_pkt_ok *)pkt)->ref); diff --git a/src/win32/dir.c b/src/win32/dir.c index 95ae5060e..8c51d8378 100644 --- a/src/win32/dir.c +++ b/src/win32/dir.c @@ -32,7 +32,7 @@ git__DIR *git__opendir(const char *dir) if (!dir || !init_filter(filter, sizeof(filter), dir)) return NULL; - new = git__malloc(sizeof(*new)); + new = git__calloc(1, sizeof(*new)); if (!new) return NULL; From 58206c9ae79af8ef675e30087e5430065b078bbb Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 16 May 2013 10:38:27 -0700 Subject: [PATCH 244/384] Add cat-file example and increase const use in API This adds an example implementation that emulates git cat-file. It is a convenient and relatively simple example of getting data out of a repository. Implementing this also revealed that there are a number of APIs that are still not using const pointers to objects that really ought to be. The main cause of this is that `git_vector_bsearch` may need to call `git_vector_sort` before doing the search, so a const pointer to the vector is not allowed. However, for tree objects, with a little care, we can ensure that the vector of tree entries is always sorted and allow lookups to take a const pointer. Also, the missing const in commit objects just looks like an oversight. --- examples/Makefile | 2 +- examples/cat-file.c | 229 ++++++++++++++++++++++++++++++++++++++++++ include/git2/commit.h | 9 +- include/git2/tree.h | 6 +- src/commit.c | 6 +- src/tree.c | 23 +++-- src/tree.h | 2 +- 7 files changed, 260 insertions(+), 17 deletions(-) create mode 100644 examples/cat-file.c diff --git a/examples/Makefile b/examples/Makefile index 2c18731fd..c5d555566 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -3,7 +3,7 @@ CC = gcc CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers LFLAGS = -L../build -lgit2 -lz -APPS = general showindex diff rev-list +APPS = general showindex diff rev-list cat-file all: $(APPS) diff --git a/examples/cat-file.c b/examples/cat-file.c new file mode 100644 index 000000000..ebb6cb0ca --- /dev/null +++ b/examples/cat-file.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include + +static git_repository *g_repo; + +static void check(int error, const char *message) +{ + if (error) { + fprintf(stderr, "%s (%d)\n", message, error); + exit(1); + } +} + +static void usage(const char *message, const char *arg) +{ + if (message && arg) + fprintf(stderr, "%s: %s\n", message, arg); + else if (message) + fprintf(stderr, "%s\n", message); + fprintf(stderr, "usage: cat-file (-t | -s | -e | -p) [] \n"); + exit(1); +} + +static int check_str_param( + const char *arg, const char *pattern, const char **val) +{ + size_t len = strlen(pattern); + if (strncmp(arg, pattern, len)) + return 0; + *val = (const char *)(arg + len); + return 1; +} + +static void print_signature(const char *header, const git_signature *sig) +{ + char sign; + int offset, hours, minutes; + + if (!sig) + return; + + offset = sig->when.offset; + if (offset < 0) { + sign = '-'; + offset = -offset; + } else { + sign = '+'; + } + + hours = offset / 60; + minutes = offset % 60; + + printf("%s %s <%s> %ld %c%02d%02d\n", + header, sig->name, sig->email, (long)sig->when.time, + sign, hours, minutes); +} + +static void show_blob(const git_blob *blob) +{ + /* ? Does this need crlf filtering? */ + fwrite(git_blob_rawcontent(blob), git_blob_rawsize(blob), 1, stdout); +} + +static void show_tree(const git_tree *tree) +{ + size_t i, max_i = (int)git_tree_entrycount(tree); + char oidstr[GIT_OID_HEXSZ + 1]; + const git_tree_entry *te; + + for (i = 0; i < max_i; ++i) { + te = git_tree_entry_byindex(tree, i); + + git_oid_tostr(oidstr, sizeof(oidstr), git_tree_entry_id(te)); + + printf("%06o %s %s\t%s\n", + git_tree_entry_filemode(te), + git_object_type2string(git_tree_entry_type(te)), + oidstr, git_tree_entry_name(te)); + } +} + +static void show_commit(const git_commit *commit) +{ + unsigned int i, max_i; + char oidstr[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(oidstr, sizeof(oidstr), git_commit_tree_id(commit)); + printf("tree %s\n", oidstr); + + max_i = (unsigned int)git_commit_parentcount(commit); + for (i = 0; i < max_i; ++i) { + git_oid_tostr(oidstr, sizeof(oidstr), git_commit_parent_id(commit, i)); + printf("parent %s\n", oidstr); + } + + print_signature("author", git_commit_author(commit)); + print_signature("committer", git_commit_committer(commit)); + + if (git_commit_message(commit)) + printf("\n%s\n", git_commit_message(commit)); +} + +static void show_tag(const git_tag *tag) +{ + char oidstr[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(oidstr, sizeof(oidstr), git_tag_target_id(tag));; + printf("object %s\n", oidstr); + printf("type %s\n", git_object_type2string(git_tag_target_type(tag))); + printf("tag %s\n", git_tag_name(tag)); + print_signature("tagger", git_tag_tagger(tag)); + + if (git_tag_message(tag)) + printf("\n%s\n", git_tag_message(tag)); +} + +enum { + SHOW_TYPE = 1, + SHOW_SIZE = 2, + SHOW_NONE = 3, + SHOW_PRETTY = 4 +}; + +int main(int argc, char *argv[]) +{ + const char *dir = ".", *rev = NULL; + int i, action = 0, verbose = 0; + git_object *obj = NULL; + char oidstr[GIT_OID_HEXSZ + 1]; + + git_threads_init(); + + for (i = 1; i < argc; ++i) { + char *a = argv[i]; + + if (a[0] != '-') { + if (rev != NULL) + usage("Only one rev should be provided", NULL); + else + rev = a; + } + else if (!strcmp(a, "-t")) + action = SHOW_TYPE; + else if (!strcmp(a, "-s")) + action = SHOW_SIZE; + else if (!strcmp(a, "-e")) + action = SHOW_NONE; + else if (!strcmp(a, "-p")) + action = SHOW_PRETTY; + else if (!strcmp(a, "-q")) + verbose = 0; + else if (!strcmp(a, "-v")) + verbose = 1; + else if (!strcmp(a, "--help") || !strcmp(a, "-h")) + usage(NULL, NULL); + else if (!check_str_param(a, "--git-dir=", &dir)) + usage("Unknown option", a); + } + + if (!action || !rev) + usage(NULL, NULL); + + check(git_repository_open_ext(&g_repo, dir, 0, NULL), + "Could not open repository"); + + if (git_revparse_single(&obj, g_repo, rev) < 0) { + fprintf(stderr, "Could not resolve '%s'\n", rev); + exit(1); + } + if (verbose) { + char oidstr[GIT_OID_HEXSZ + 1]; + git_oid_tostr(oidstr, sizeof(oidstr), git_object_id(obj)); + + printf("%s %s\n--\n", + git_object_type2string(git_object_type(obj)), oidstr); + } + + switch (action) { + case SHOW_TYPE: + printf("%s\n", git_object_type2string(git_object_type(obj))); + break; + case SHOW_SIZE: { + git_odb *odb; + git_odb_object *odbobj; + + check(git_repository_odb(&odb, g_repo), "Could not open ODB"); + check(git_odb_read(&odbobj, odb, git_object_id(obj)), + "Could not find obj"); + + printf("%ld\n", (long)git_odb_object_size(odbobj)); + + git_odb_object_free(odbobj); + git_odb_free(odb); + } + break; + case SHOW_NONE: + /* just want return result */ + break; + case SHOW_PRETTY: + + switch (git_object_type(obj)) { + case GIT_OBJ_BLOB: + show_blob((const git_blob *)obj); + break; + case GIT_OBJ_COMMIT: + show_commit((const git_commit *)obj); + break; + case GIT_OBJ_TREE: + show_tree((const git_tree *)obj); + break; + case GIT_OBJ_TAG: + show_tag((const git_tag *)obj); + break; + default: + printf("unknown %s\n", oidstr); + break; + } + break; + } + + git_object_free(obj); + git_repository_free(g_repo); + + git_threads_shutdown(); + + return 0; +} diff --git a/include/git2/commit.h b/include/git2/commit.h index a420ba635..20b345f84 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -164,7 +164,10 @@ GIT_EXTERN(unsigned int) git_commit_parentcount(const git_commit *commit); * @param n the position of the parent (from 0 to `parentcount`) * @return 0 or an error code */ -GIT_EXTERN(int) git_commit_parent(git_commit **out, git_commit *commit, unsigned int n); +GIT_EXTERN(int) git_commit_parent( + git_commit **out, + const git_commit *commit, + unsigned int n); /** * Get the oid of a specified parent for a commit. This is different from @@ -175,7 +178,9 @@ GIT_EXTERN(int) git_commit_parent(git_commit **out, git_commit *commit, unsigned * @param n the position of the parent (from 0 to `parentcount`) * @return the id of the parent, NULL on error. */ -GIT_EXTERN(const git_oid *) git_commit_parent_id(git_commit *commit, unsigned int n); +GIT_EXTERN(const git_oid *) git_commit_parent_id( + const git_commit *commit, + unsigned int n); /** * Get the commit object that is the th generation ancestor diff --git a/include/git2/tree.h b/include/git2/tree.h index 6ad722048..d673f50c4 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -97,7 +97,7 @@ GIT_EXTERN(size_t) git_tree_entrycount(const git_tree *tree); * @return the tree entry; NULL if not found */ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname( - git_tree *tree, const char *filename); + const git_tree *tree, const char *filename); /** * Lookup a tree entry by its position in the tree @@ -110,7 +110,7 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname( * @return the tree entry; NULL if not found */ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex( - git_tree *tree, size_t idx); + const git_tree *tree, size_t idx); /** * Lookup a tree entry by SHA value. @@ -141,7 +141,7 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byoid( */ GIT_EXTERN(int) git_tree_entry_bypath( git_tree_entry **out, - git_tree *root, + const git_tree *root, const char *path); /** diff --git a/src/commit.c b/src/commit.c index be6e32c76..1ab9b34f7 100644 --- a/src/commit.c +++ b/src/commit.c @@ -266,14 +266,16 @@ int git_commit_tree(git_tree **tree_out, const git_commit *commit) return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id); } -const git_oid *git_commit_parent_id(git_commit *commit, unsigned int n) +const git_oid *git_commit_parent_id( + const git_commit *commit, unsigned int n) { assert(commit); return git_vector_get(&commit->parent_ids, n); } -int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n) +int git_commit_parent( + git_commit **parent, const git_commit *commit, unsigned int n) { const git_oid *parent_id; assert(commit); diff --git a/src/tree.c b/src/tree.c index a48b322d4..10d131438 100644 --- a/src/tree.c +++ b/src/tree.c @@ -271,25 +271,27 @@ int git_tree_entry_to_object( } static const git_tree_entry *entry_fromname( - git_tree *tree, const char *name, size_t name_len) + const git_tree *tree, const char *name, size_t name_len) { size_t idx; - if (tree_key_search(&idx, &tree->entries, name, name_len) < 0) + assert(tree->entries.sorted); /* be safe when we cast away constness */ + + if (tree_key_search(&idx, (git_vector *)&tree->entries, name, name_len) < 0) return NULL; return git_vector_get(&tree->entries, idx); } const git_tree_entry *git_tree_entry_byname( - git_tree *tree, const char *filename) + const git_tree *tree, const char *filename) { assert(tree && filename); return entry_fromname(tree, filename, strlen(filename)); } const git_tree_entry *git_tree_entry_byindex( - git_tree *tree, size_t idx) + const git_tree *tree, size_t idx) { assert(tree); return git_vector_get(&tree->entries, idx); @@ -311,9 +313,9 @@ const git_tree_entry *git_tree_entry_byoid( return NULL; } -int git_tree__prefix_position(git_tree *tree, const char *path) +int git_tree__prefix_position(const git_tree *tree, const char *path) { - git_vector *entries = &tree->entries; + const git_vector *entries = &tree->entries; struct tree_key_search ksearch; size_t at_pos; @@ -323,8 +325,11 @@ int git_tree__prefix_position(git_tree *tree, const char *path) ksearch.filename = path; ksearch.filename_len = strlen(path); + assert(tree->entries.sorted); /* be safe when we cast away constness */ + /* Find tree entry with appropriate prefix */ - git_vector_bsearch2(&at_pos, entries, &homing_search_cmp, &ksearch); + git_vector_bsearch2( + &at_pos, (git_vector *)entries, &homing_search_cmp, &ksearch); for (; at_pos < entries->length; ++at_pos) { const git_tree_entry *entry = entries->contents[at_pos]; @@ -408,6 +413,8 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj) buffer += GIT_OID_RAWSZ; } + git_vector_sort(&tree->entries); + return 0; } @@ -796,7 +803,7 @@ static size_t subpath_len(const char *path) int git_tree_entry_bypath( git_tree_entry **entry_out, - git_tree *root, + const git_tree *root, const char *path) { int error = 0; diff --git a/src/tree.h b/src/tree.h index 7cb2dd36c..f07039a07 100644 --- a/src/tree.h +++ b/src/tree.h @@ -47,7 +47,7 @@ int git_tree__parse(void *tree, git_odb_object *obj); * @param prefix the beginning of a path to find in the tree. * @return index of the first item at or after the given prefix. */ -int git_tree__prefix_position(git_tree *tree, const char *prefix); +int git_tree__prefix_position(const git_tree *tree, const char *prefix); /** From 8d78400142bc001e18e0b0687290d6b446e05130 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 16 May 2013 10:43:10 -0700 Subject: [PATCH 245/384] Make examples/diff.c compile vs threadsafe library --- examples/diff.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/diff.c b/examples/diff.c index 2ef405665..bb4f0ec21 100644 --- a/examples/diff.c +++ b/examples/diff.c @@ -117,7 +117,10 @@ int main(int argc, char *argv[]) git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff; int i, color = -1, compact = 0, cached = 0; - char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL; + char *a, *treeish1 = NULL, *treeish2 = NULL; + const char *dir = "."; + + git_threads_init(); /* parse arguments as copied from git-diff */ @@ -162,7 +165,8 @@ int main(int argc, char *argv[]) !check_uint16_param(a, "--inter-hunk-context=", &opts.interhunk_lines) && !check_str_param(a, "--src-prefix=", &opts.old_prefix) && - !check_str_param(a, "--dst-prefix=", &opts.new_prefix)) + !check_str_param(a, "--dst-prefix=", &opts.new_prefix) && + !check_str_param(a, "--git-dir=", &dir)) usage("Unknown arg", a); } @@ -216,6 +220,8 @@ int main(int argc, char *argv[]) git_tree_free(t2); git_repository_free(repo); + git_threads_shutdown(); + return 0; } From 57908bb3a3b3a0f3de75d13ef432e3964dab9212 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 16 May 2013 11:03:55 -0700 Subject: [PATCH 246/384] Ensure reuc vector is always valid In theory, if there was a problem reading the REUC data, the read_reuc() routine could have left uninitialized and invalid data in the git_index vector. This moves the line that inserts a new entry into the vector down to the bottom of the routine so we know all the content is already valid. Also, per @linquize, this uses calloc to ensure no uninitialized data. --- src/index.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/index.c b/src/index.c index f767dfab7..f7f7133d6 100644 --- a/src/index.c +++ b/src/index.c @@ -1283,8 +1283,9 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) size_t len; int i; - /* This gets called multiple times, the vector might already be initialized */ - if (index->reuc._alloc_size == 0 && git_vector_init(&index->reuc, 16, reuc_cmp) < 0) + /* If called multiple times, the vector might already be initialized */ + if (index->reuc._alloc_size == 0 && + git_vector_init(&index->reuc, 16, reuc_cmp) < 0) return -1; while (size) { @@ -1294,12 +1295,9 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) if (size <= len) return index_error_invalid("reading reuc entries"); - lost = git__malloc(sizeof(git_index_reuc_entry)); + lost = git__calloc(1, sizeof(git_index_reuc_entry)); GITERR_CHECK_ALLOC(lost); - if (git_vector_insert(&index->reuc, lost) < 0) - return -1; - /* read NUL-terminated pathname for entry */ lost->path = git__strdup(buffer); GITERR_CHECK_ALLOC(lost->path); @@ -1337,6 +1335,10 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) size -= 20; buffer += 20; } + + /* entry was read successfully - insert into reuc vector */ + if (git_vector_insert(&index->reuc, lost) < 0) + return -1; } /* entries are guaranteed to be sorted on-disk */ From dcaa898d8200d5eefa5ba8c7618c5526d8392da0 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Thu, 9 May 2013 16:52:04 +0200 Subject: [PATCH 247/384] revparse: Simplify temporary reference freeing --- src/revparse.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/revparse.c b/src/revparse.c index 97fc91b54..175b5e907 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -609,7 +609,7 @@ static int object_from_reference(git_object **object, git_reference *reference) return error; } -static int ensure_base_rev_loaded(git_object **object, git_reference **reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier) +static int ensure_base_rev_loaded(git_object **object, git_reference *reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier) { int error; git_buf identifier = GIT_BUF_INIT; @@ -617,14 +617,8 @@ static int ensure_base_rev_loaded(git_object **object, git_reference **reference if (*object != NULL) return 0; - if (*reference != NULL) { - if ((error = object_from_reference(object, *reference)) < 0) - return error; - - git_reference_free(*reference); - *reference = NULL; - return 0; - } + if (reference != NULL) + return object_from_reference(object, reference); if (!allow_empty_identifier && identifier_len == 0) return GIT_EINVALIDSPEC; @@ -684,7 +678,7 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec while (spec[pos]) { switch (spec[pos]) { case '^': - if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) + if ((error = ensure_base_rev_loaded(&base_rev, reference, spec, identifier_len, repo, false)) < 0) goto cleanup; if (spec[pos+1] == '{') { @@ -719,7 +713,7 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec if ((error = extract_how_many(&n, spec, &pos)) < 0) goto cleanup; - if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) + if ((error = ensure_base_rev_loaded(&base_rev, reference, spec, identifier_len, repo, false)) < 0) goto cleanup; if ((error = handle_linear_syntax(&temp_object, base_rev, n)) < 0) @@ -738,7 +732,7 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec goto cleanup; if (any_left_hand_identifier(base_rev, reference, identifier_len)) { - if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, true)) < 0) + if ((error = ensure_base_rev_loaded(&base_rev, reference, spec, identifier_len, repo, true)) < 0) goto cleanup; if ((error = handle_colon_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0) @@ -795,7 +789,7 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec } } - if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) + if ((error = ensure_base_rev_loaded(&base_rev, reference, spec, identifier_len, repo, false)) < 0) goto cleanup; *out = base_rev; From e841c533d75c12156e0be8811d02f0537e32a458 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Thu, 9 May 2013 16:42:39 +0200 Subject: [PATCH 248/384] revparse: Introduce git_revparse_ext() Expose a way to retrieve, along with the target git_object, the reference pointed at by some revparse expression (`@{<-n>}` or `@{upstream}` syntax). --- include/git2/revparse.h | 22 +++++++++++++ src/revparse.c | 65 +++++++++++++++++++++++++++++++++++--- tests-clar/refs/revparse.c | 49 ++++++++++++++++++++++++++-- 3 files changed, 129 insertions(+), 7 deletions(-) diff --git a/include/git2/revparse.h b/include/git2/revparse.h index e155c7012..786a9da57 100644 --- a/include/git2/revparse.h +++ b/include/git2/revparse.h @@ -32,6 +32,28 @@ GIT_BEGIN_DECL */ GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec); +/** + * Find a single object, as specified by a revision string. + * See `man gitrevisions`, + * or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for + * information on the syntax accepted. + * + * In some cases (`@{<-n>}` or `@{upstream}`), the expression may + * point to an intermediate reference. When such expressions are being passed + * in, `reference_out` will be valued as well. + * + * @param object_out pointer to output object + * @param reference_out pointer to output reference or NULL + * @param repo the repository to search in + * @param spec the textual specification for an object + * @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC + * or an error code + */ +GIT_EXTERN(int) git_revparse_ext( + git_object **object_out, + git_reference **reference_out, + git_repository *repo, + const char *spec); /** * Revparse flags. These indicate the intended behavior of the spec passed to diff --git a/src/revparse.c b/src/revparse.c index 175b5e907..f4c03ad51 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -662,7 +662,12 @@ static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_ return GIT_EINVALIDSPEC; } -int git_revparse_single(git_object **out, git_repository *repo, const char *spec) +int revparse__ext( + git_object **object_out, + git_reference **reference_out, + int *identifier_len_out, + git_repository *repo, + const char *spec) { size_t pos = 0, identifier_len = 0; int error = -1, n; @@ -671,9 +676,10 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec git_reference *reference = NULL; git_object *base_rev = NULL; - assert(out && repo && spec); + assert(object_out && reference_out && repo && spec); - *out = NULL; + *object_out = NULL; + *reference_out = NULL; while (spec[pos]) { switch (spec[pos]) { @@ -792,7 +798,9 @@ int git_revparse_single(git_object **out, git_repository *repo, const char *spec if ((error = ensure_base_rev_loaded(&base_rev, reference, spec, identifier_len, repo, false)) < 0) goto cleanup; - *out = base_rev; + *object_out = base_rev; + *reference_out = reference; + *identifier_len_out = identifier_len; error = 0; cleanup: @@ -802,12 +810,59 @@ cleanup: "Failed to parse revision specifier - Invalid pattern '%s'", spec); git_object_free(base_rev); + git_reference_free(reference); } - git_reference_free(reference); + git_buf_free(&buf); return error; } +int git_revparse_ext( + git_object **object_out, + git_reference **reference_out, + git_repository *repo, + const char *spec) +{ + int error, identifier_len; + git_object *obj = NULL; + git_reference *ref = NULL; + + if ((error = revparse__ext(&obj, &ref, &identifier_len, repo, spec)) < 0) + goto cleanup; + + *object_out = obj; + *reference_out = ref; + + return 0; + +cleanup: + git_object_free(obj); + git_reference_free(ref); + return error; +} + +int git_revparse_single(git_object **out, git_repository *repo, const char *spec) +{ + int error; + git_object *obj = NULL; + git_reference *ref = NULL; + + *out = NULL; + + if ((error = git_revparse_ext(&obj, &ref, repo, spec)) < 0) + goto cleanup; + + git_reference_free(ref); + + *out = obj; + + return 0; + +cleanup: + git_object_free(obj); + git_reference_free(ref); + return error; +} int git_revparse( git_revspec *revspec, diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index 43406e239..1d156f567 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -9,13 +9,19 @@ static git_repository *g_repo; static git_object *g_obj; /* Helpers */ -static void test_object_inrepo(const char *spec, const char *expected_oid, git_repository *repo) +static void test_object_and_ref_inrepo( + const char *spec, + const char *expected_oid, + const char *expected_refname, + git_repository *repo, + bool assert_reference_retrieval) { char objstr[64] = {0}; git_object *obj = NULL; + git_reference *ref = NULL; int error; - error = git_revparse_single(&obj, repo, spec); + error = git_revparse_ext(&obj, &ref, repo, spec); if (expected_oid != NULL) { cl_assert_equal_i(0, error); @@ -24,7 +30,20 @@ static void test_object_inrepo(const char *spec, const char *expected_oid, git_r } else cl_assert_equal_i(GIT_ENOTFOUND, error); + if (assert_reference_retrieval) { + if (expected_refname == NULL) + cl_assert(NULL == ref); + else + cl_assert_equal_s(expected_refname, git_reference_name(ref)); + } + git_object_free(obj); + git_reference_free(ref); +} + +static void test_object_inrepo(const char *spec, const char *expected_oid, git_repository *repo) +{ + test_object_and_ref_inrepo(spec, expected_oid, NULL, repo, false); } static void test_id_inrepo( @@ -63,6 +82,11 @@ static void test_object(const char *spec, const char *expected_oid) test_object_inrepo(spec, expected_oid, g_repo); } +static void test_object_and_ref(const char *spec, const char *expected_oid, const char *expected_refname) +{ + test_object_and_ref_inrepo(spec, expected_oid, expected_refname, g_repo, true); +} + static void test_rangelike(const char *rangelike, const char *expected_left, const char *expected_right, @@ -694,3 +718,24 @@ void test_refs_revparse__parses_range_operator(void) "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE); } + +void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void) +{ + test_object_and_ref( + "master@{upstream}", + "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", + "refs/remotes/test/master"); + + test_object_and_ref( + "@{-1}", + "a4a7dce85cf63874e984719f4fdd239f5145052f", + "refs/heads/br2"); +} + +void test_refs_revparse__ext_only_retrieve_the_object_target(void) +{ + test_object_and_ref( + "master", + "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + NULL); +} From f672cd2a0925c230421efccdefd8e1f640bba41b Mon Sep 17 00:00:00 2001 From: nulltoken Date: Fri, 10 May 2013 20:15:57 +0200 Subject: [PATCH 249/384] revparse: Make revparse_ext() return git_reference from names as well --- src/revparse.c | 37 +++++++++++++++++++++++-------------- tests-clar/refs/revparse.c | 4 ++-- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/revparse.c b/src/revparse.c index f4c03ad51..3e3edb6cc 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -84,12 +84,16 @@ static int maybe_describe(git_object**out, git_repository *repo, const char *spe return maybe_abbrev(out, repo, substr+2); } -static int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec) +static int revparse_lookup_object( + git_object **object_out, + git_reference **reference_out, + git_repository *repo, + const char *spec) { int error; git_reference *ref; - error = maybe_sha(out, repo, spec); + error = maybe_sha(object_out, repo, spec); if (!error) return 0; @@ -98,22 +102,27 @@ static int revparse_lookup_object(git_object **out, git_repository *repo, const error = git_reference_dwim(&ref, repo, spec); if (!error) { - error = git_object_lookup(out, repo, git_reference_target(ref), GIT_OBJ_ANY); - git_reference_free(ref); + + error = git_object_lookup( + object_out, repo, git_reference_target(ref), GIT_OBJ_ANY); + + if (!error) + *reference_out = ref; + return error; } if (error < 0 && error != GIT_ENOTFOUND) return error; - error = maybe_abbrev(out, repo, spec); + error = maybe_abbrev(object_out, repo, spec); if (!error) return 0; if (error < 0 && error != GIT_ENOTFOUND) return error; - error = maybe_describe(out, repo, spec); + error = maybe_describe(object_out, repo, spec); if (!error) return 0; @@ -609,7 +618,7 @@ static int object_from_reference(git_object **object, git_reference *reference) return error; } -static int ensure_base_rev_loaded(git_object **object, git_reference *reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier) +static int ensure_base_rev_loaded(git_object **object, git_reference **reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier) { int error; git_buf identifier = GIT_BUF_INIT; @@ -617,8 +626,8 @@ static int ensure_base_rev_loaded(git_object **object, git_reference *reference, if (*object != NULL) return 0; - if (reference != NULL) - return object_from_reference(object, reference); + if (*reference != NULL) + return object_from_reference(object, *reference); if (!allow_empty_identifier && identifier_len == 0) return GIT_EINVALIDSPEC; @@ -626,7 +635,7 @@ static int ensure_base_rev_loaded(git_object **object, git_reference *reference, if (git_buf_put(&identifier, spec, identifier_len) < 0) return -1; - error = revparse_lookup_object(object, repo, git_buf_cstr(&identifier)); + error = revparse_lookup_object(object, reference, repo, git_buf_cstr(&identifier)); git_buf_free(&identifier); return error; @@ -684,7 +693,7 @@ int revparse__ext( while (spec[pos]) { switch (spec[pos]) { case '^': - if ((error = ensure_base_rev_loaded(&base_rev, reference, spec, identifier_len, repo, false)) < 0) + if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) goto cleanup; if (spec[pos+1] == '{') { @@ -719,7 +728,7 @@ int revparse__ext( if ((error = extract_how_many(&n, spec, &pos)) < 0) goto cleanup; - if ((error = ensure_base_rev_loaded(&base_rev, reference, spec, identifier_len, repo, false)) < 0) + if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) goto cleanup; if ((error = handle_linear_syntax(&temp_object, base_rev, n)) < 0) @@ -738,7 +747,7 @@ int revparse__ext( goto cleanup; if (any_left_hand_identifier(base_rev, reference, identifier_len)) { - if ((error = ensure_base_rev_loaded(&base_rev, reference, spec, identifier_len, repo, true)) < 0) + if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, true)) < 0) goto cleanup; if ((error = handle_colon_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0) @@ -795,7 +804,7 @@ int revparse__ext( } } - if ((error = ensure_base_rev_loaded(&base_rev, reference, spec, identifier_len, repo, false)) < 0) + if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0) goto cleanup; *object_out = base_rev; diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c index 1d156f567..69d92745c 100644 --- a/tests-clar/refs/revparse.c +++ b/tests-clar/refs/revparse.c @@ -732,10 +732,10 @@ void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void) "refs/heads/br2"); } -void test_refs_revparse__ext_only_retrieve_the_object_target(void) +void test_refs_revparse__ext_can_expand_short_reference_names(void) { test_object_and_ref( "master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", - NULL); + "refs/heads/master"); } From 9c06b25054dd43802cbf6620c1082fa5d4498bf5 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 16 May 2013 13:04:37 -0500 Subject: [PATCH 250/384] merge setup --- include/git2/merge.h | 59 ++- include/git2/types.h | 3 + src/merge.c | 595 +++++++++++++++++++-- src/merge.h | 22 + tests-clar/merge/workdir/setup.c | 856 ++++++++++++++++++++++++++++++- 5 files changed, 1476 insertions(+), 59 deletions(-) diff --git a/include/git2/merge.h b/include/git2/merge.h index 955840569..af8f36063 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -23,13 +23,13 @@ GIT_BEGIN_DECL /** - * Flags for tree_many diff options. A combination of these flags can be - * passed in via the `flags` value in the `git_diff_tree_many_options`. + * Flags for `git_mrege_tree` options. A combination of these flags can be + * passed in via the `flags` vlaue in the `git_merge_tree_opts`. */ typedef enum { /** Detect renames */ GIT_MERGE_TREE_FIND_RENAMES = (1 << 0), -} git_merge_tree_flags; +} git_merge_tree_flag_t; /** * Automerge options for `git_merge_trees_opts`. @@ -44,7 +44,7 @@ typedef enum { typedef struct { unsigned int version; - git_merge_tree_flags flags; + git_merge_tree_flag_t flags; /** Similarity to consider a file renamed (default 50) */ unsigned int rename_threshold; @@ -95,6 +95,57 @@ GIT_EXTERN(int) git_merge_base_many( const git_oid input_array[], size_t length); +/** + * Creates a `git_merge_head` from the given reference + * + * @param out pointer to store the git_merge_head result in + * @param repo repository that contains the given reference + * @param ref reference to use as a merge input + * @return zero on success, -1 on failure. + */ +GIT_EXTERN(int) git_merge_head_from_ref( + git_merge_head **out, + git_repository *repo, + git_reference *ref); + +/** + * Creates a `git_merge_head` from the given fetch head data + * + * @param out pointer to store the git_merge_head result in + * @param repo repository that contains the given commit + * @param branch_name name of the (remote) branch + * @param remote_url url of the remote + * @param oid the commit object id to use as a merge input + * @return zero on success, -1 on failure. + */ +GIT_EXTERN(int) git_merge_head_from_fetchhead( + git_merge_head **out, + git_repository *repo, + const char *branch_name, + const char *remote_url, + const git_oid *oid); + +/** + * Creates a `git_merge_head` from the given commit id + * + * @param out pointer to store the git_merge_head result in + * @param repo repository that contains the given commit + * @param oid the commit object id to use as a merge input + * @return zero on success, -1 on failure. + */ +GIT_EXTERN(int) git_merge_head_from_oid( + git_merge_head **out, + git_repository *repo, + const git_oid *oid); + +/** + * Frees a `git_merge_head` + * + * @param the merge head to free + */ +GIT_EXTERN(void) git_merge_head_free( + git_merge_head *head); + /** * Merge two trees, producing a `git_index` that reflects the result of * the merge. diff --git a/include/git2/types.h b/include/git2/types.h index 43751d3b0..d97bbcb30 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -168,6 +168,9 @@ typedef struct git_reference git_reference; /** Iterator for references */ typedef struct git_reference_iterator git_reference_iterator; +/** Merge heads, the input to merge */ +typedef struct git_merge_head git_merge_head; + /** Basic type of any Git reference. */ typedef enum { diff --git a/src/merge.c b/src/merge.c index de5d65ac0..11345587c 100644 --- a/src/merge.c +++ b/src/merge.c @@ -54,39 +54,6 @@ struct merge_diff_df_data { }; -int git_repository_merge_cleanup(git_repository *repo) -{ - int error = 0; - git_buf merge_head_path = GIT_BUF_INIT, - merge_mode_path = GIT_BUF_INIT, - merge_msg_path = GIT_BUF_INIT; - - assert(repo); - - if (git_buf_joinpath(&merge_head_path, repo->path_repository, GIT_MERGE_HEAD_FILE) < 0 || - git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0 || - git_buf_joinpath(&merge_msg_path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) - return -1; - - if (git_path_isfile(merge_head_path.ptr)) { - if ((error = p_unlink(merge_head_path.ptr)) < 0) - goto cleanup; - } - - if (git_path_isfile(merge_mode_path.ptr)) - (void)p_unlink(merge_mode_path.ptr); - - if (git_path_isfile(merge_msg_path.ptr)) - (void)p_unlink(merge_msg_path.ptr); - -cleanup: - git_buf_free(&merge_msg_path); - git_buf_free(&merge_mode_path); - git_buf_free(&merge_head_path); - - return error; -} - /* Merge base computation */ int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_array[], size_t length) @@ -1380,6 +1347,18 @@ git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo) return diff_list; } +void git_merge_diff_list__free(git_merge_diff_list *diff_list) +{ + if (!diff_list) + return; + + git_vector_free(&diff_list->staged); + git_vector_free(&diff_list->conflicts); + git_vector_free(&diff_list->resolved); + git_pool_clear(&diff_list->pool); + git__free(diff_list); +} + static int merge_tree_normalize_opts( git_repository *repo, git_merge_tree_opts *opts, @@ -1617,14 +1596,550 @@ done: return error; } -void git_merge_diff_list__free(git_merge_diff_list *diff_list) +/* Merge setup / cleanup */ + +static int write_orig_head( + git_repository *repo, + const git_merge_head *our_head) { - if (!diff_list) + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + char orig_oid_str[GIT_OID_HEXSZ + 1]; + int error = 0; + + assert(repo && our_head); + + git_oid_tostr(orig_oid_str, GIT_OID_HEXSZ+1, &our_head->oid); + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 && + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) == 0 && + (error = git_filebuf_printf(&file, "%s\n", orig_oid_str)) == 0) + error = git_filebuf_commit(&file, 0666); + + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + +static int write_merge_head( + git_repository *repo, + const git_merge_head *heads[], + size_t heads_len) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + char merge_oid_str[GIT_OID_HEXSZ + 1]; + size_t i; + int error = 0; + + assert(repo && heads); + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_HEAD_FILE)) < 0 || + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0) + goto cleanup; + + for (i = 0; i < heads_len; i++) { + git_oid_tostr(merge_oid_str, GIT_OID_HEXSZ+1, &heads[i]->oid); + + if ((error = git_filebuf_printf(&file, "%s\n", merge_oid_str)) < 0) + goto cleanup; + } + + error = git_filebuf_commit(&file, 0666); + +cleanup: + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + +static int write_merge_mode(git_repository *repo, unsigned int flags) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + int error = 0; + + /* For future expansion */ + GIT_UNUSED(flags); + + assert(repo); + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MODE_FILE)) < 0 || + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0) + goto cleanup; + + error = git_filebuf_commit(&file, 0666); + +cleanup: + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + +struct merge_msg_entry { + const git_merge_head *merge_head; + bool written; +}; + +static int msg_entry_is_branch( + const struct merge_msg_entry *entry, + git_vector *entries) +{ + GIT_UNUSED(entries); + + return (entry->written == 0 && + entry->merge_head->remote_url == NULL && + entry->merge_head->ref_name != NULL && + git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0); +} + +static int msg_entry_is_tracking( + const struct merge_msg_entry *entry, + git_vector *entries) +{ + GIT_UNUSED(entries); + + return (entry->written == 0 && + entry->merge_head->remote_url == NULL && + entry->merge_head->ref_name != NULL && + git__strncmp(GIT_REFS_REMOTES_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_REMOTES_DIR)) == 0); +} + +static int msg_entry_is_tag( + const struct merge_msg_entry *entry, + git_vector *entries) +{ + GIT_UNUSED(entries); + + return (entry->written == 0 && + entry->merge_head->remote_url == NULL && + entry->merge_head->ref_name != NULL && + git__strncmp(GIT_REFS_TAGS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_TAGS_DIR)) == 0); +} + +static int msg_entry_is_remote( + const struct merge_msg_entry *entry, + git_vector *entries) +{ + if (entry->written == 0 && + entry->merge_head->remote_url != NULL && + entry->merge_head->ref_name != NULL && + git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0) + { + struct merge_msg_entry *existing; + + /* Match only branches from the same remote */ + if (entries->length == 0) + return 1; + + existing = git_vector_get(entries, 0); + + return (git__strcmp(existing->merge_head->remote_url, + entry->merge_head->remote_url) == 0); + } + + return 0; +} + +static int msg_entry_is_oid( + const struct merge_msg_entry *merge_msg_entry) +{ + return (merge_msg_entry->written == 0 && + merge_msg_entry->merge_head->ref_name == NULL && + merge_msg_entry->merge_head->remote_url == NULL); +} + +static int merge_msg_entry_written( + const struct merge_msg_entry *merge_msg_entry) +{ + return (merge_msg_entry->written == 1); +} + +static int merge_msg_entries( + git_vector *v, + const struct merge_msg_entry *entries, + size_t len, + int (*match)(const struct merge_msg_entry *entry, git_vector *entries)) +{ + size_t i; + int matches, total = 0; + + git_vector_clear(v); + + for (i = 0; i < len; i++) { + if ((matches = match(&entries[i], v)) < 0) + return matches; + else if (!matches) + continue; + + git_vector_insert(v, (struct merge_msg_entry *)&entries[i]); + total++; + } + + return total; +} + +static int merge_msg_write_entries( + git_filebuf *file, + git_vector *entries, + const char *item_name, + const char *item_plural_name, + size_t ref_name_skip, + const char *source, + char sep) +{ + struct merge_msg_entry *entry; + size_t i; + int error = 0; + + if (entries->length == 0) + return 0; + + if (sep && (error = git_filebuf_printf(file, "%c ", sep)) < 0) + goto done; + + if ((error = git_filebuf_printf(file, "%s ", + (entries->length == 1) ? item_name : item_plural_name)) < 0) + goto done; + + git_vector_foreach(entries, i, entry) { + if (i > 0 && + (error = git_filebuf_printf(file, "%s", (i == entries->length - 1) ? " and " : ", ")) < 0) + goto done; + + if ((error = git_filebuf_printf(file, "'%s'", entry->merge_head->ref_name + ref_name_skip)) < 0) + goto done; + + entry->written = 1; + } + + if (source) + error = git_filebuf_printf(file, " of %s", source); + +done: + return error; +} + +static int merge_msg_write_branches( + git_filebuf *file, + git_vector *entries, + char sep) +{ + return merge_msg_write_entries(file, entries, + "branch", "branches", strlen(GIT_REFS_HEADS_DIR), NULL, sep); +} + +static int merge_msg_write_tracking( + git_filebuf *file, + git_vector *entries, + char sep) +{ + return merge_msg_write_entries(file, entries, + "remote-tracking branch", "remote-tracking branches", 0, NULL, sep); +} + +static int merge_msg_write_tags( + git_filebuf *file, + git_vector *entries, + char sep) +{ + return merge_msg_write_entries(file, entries, + "tag", "tags", strlen(GIT_REFS_TAGS_DIR), NULL, sep); +} + +static int merge_msg_write_remotes( + git_filebuf *file, + git_vector *entries, + char sep) +{ + const char *source; + + if (entries->length == 0) + return 0; + + source = ((struct merge_msg_entry *)entries->contents[0])->merge_head->remote_url; + + return merge_msg_write_entries(file, entries, + "branch", "branches", strlen(GIT_REFS_HEADS_DIR), source, sep); +} + +static int write_merge_msg( + git_repository *repo, + const git_merge_head *heads[], + size_t heads_len) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + char oid_str[GIT_OID_HEXSZ + 1]; + struct merge_msg_entry *entries; + git_vector matching = GIT_VECTOR_INIT; + size_t i; + char sep = 0; + int error = 0; + + assert(repo && heads); + + entries = git__calloc(heads_len, sizeof(struct merge_msg_entry)); + GITERR_CHECK_ALLOC(entries); + + if (git_vector_init(&matching, heads_len, NULL) < 0) + return -1; + + for (i = 0; i < heads_len; i++) + entries[i].merge_head = heads[i]; + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0 || + (error = git_filebuf_write(&file, "Merge ", 6)) < 0) + goto cleanup; + + /* + * This is to emulate the format of MERGE_MSG by core git. + * + * Core git will write all the commits specified by OID, in the order + * provided, until the first named branch or tag is reached, at which + * point all branches will be written in the order provided, then all + * tags, then all remote tracking branches and finally all commits that + * were specified by OID that were not already written. + * + * Yes. Really. + */ + for (i = 0; i < heads_len; i++) { + if (!msg_entry_is_oid(&entries[i])) + break; + + git_oid_fmt(oid_str, &entries[i].merge_head->oid); + oid_str[GIT_OID_HEXSZ] = '\0'; + + if ((error = git_filebuf_printf(&file, "%scommit '%s'", (i > 0) ? "; " : "", oid_str)) < 0) + goto cleanup; + + entries[i].written = 1; + } + + if (i) + sep = ';'; + + if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_branch)) < 0 || + (error = merge_msg_write_branches(&file, &matching, sep)) < 0) + goto cleanup; + + if (matching.length) + sep =','; + + if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tracking)) < 0 || + (error = merge_msg_write_tracking(&file, &matching, sep)) < 0) + goto cleanup; + + if (matching.length) + sep =','; + + if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tag)) < 0 || + (error = merge_msg_write_tags(&file, &matching, sep)) < 0) + goto cleanup; + + if (matching.length) + sep =','; + + /* We should never be called with multiple remote branches, but handle + * it in case we are... */ + while ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_remote)) > 0) { + if ((error = merge_msg_write_remotes(&file, &matching, sep)) < 0) + goto cleanup; + + if (matching.length) + sep =','; + } + + if (error < 0) + goto cleanup; + + for (i = 0; i < heads_len; i++) { + if (merge_msg_entry_written(&entries[i])) + continue; + + git_oid_fmt(oid_str, &entries[i].merge_head->oid); + oid_str[GIT_OID_HEXSZ] = '\0'; + + if ((error = git_filebuf_printf(&file, "; commit '%s'", oid_str)) < 0) + goto cleanup; + } + + if ((error = git_filebuf_printf(&file, "\n")) < 0 || + (error = git_filebuf_commit(&file, 0666)) < 0) + goto cleanup; + +cleanup: + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + git_vector_free(&matching); + git__free(entries); + + return error; +} + +int git_merge__setup( + git_repository *repo, + const git_merge_head *our_head, + const git_merge_head *heads[], + size_t heads_len, + unsigned int flags) +{ + int error = 0; + + assert (repo && our_head && heads); + + if ((error = write_orig_head(repo, our_head)) == 0 && + (error = write_merge_head(repo, heads, heads_len)) == 0 && + (error = write_merge_mode(repo, flags)) == 0) { + error = write_merge_msg(repo, heads, heads_len); + } + + return error; +} + +int git_repository_merge_cleanup(git_repository *repo) +{ + int error = 0; + git_buf merge_head_path = GIT_BUF_INIT, + merge_mode_path = GIT_BUF_INIT, + merge_msg_path = GIT_BUF_INIT; + + assert(repo); + + if (git_buf_joinpath(&merge_head_path, repo->path_repository, GIT_MERGE_HEAD_FILE) < 0 || + git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0 || + git_buf_joinpath(&merge_msg_path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) + return -1; + + if (git_path_isfile(merge_head_path.ptr)) { + if ((error = p_unlink(merge_head_path.ptr)) < 0) + goto cleanup; + } + + if (git_path_isfile(merge_mode_path.ptr)) + (void)p_unlink(merge_mode_path.ptr); + + if (git_path_isfile(merge_msg_path.ptr)) + (void)p_unlink(merge_msg_path.ptr); + +cleanup: + git_buf_free(&merge_msg_path); + git_buf_free(&merge_mode_path); + git_buf_free(&merge_head_path); + + return error; +} + +/* Merge heads are the input to merge */ + +static int merge_head_init( + git_merge_head **out, + git_repository *repo, + const char *ref_name, + const char *remote_url, + const git_oid *oid) +{ + git_merge_head *head; + int error = 0; + + assert(out && oid); + + *out = NULL; + + head = git__calloc(1, sizeof(git_merge_head)); + GITERR_CHECK_ALLOC(head); + + if (ref_name) { + head->ref_name = git__strdup(ref_name); + GITERR_CHECK_ALLOC(head->ref_name); + } + + if (remote_url) { + head->remote_url = git__strdup(remote_url); + GITERR_CHECK_ALLOC(head->remote_url); + } + + git_oid_cpy(&head->oid, oid); + + if ((error = git_commit_lookup(&head->commit, repo, &head->oid)) < 0) { + git_merge_head_free(head); + return error; + } + + *out = head; + return error; +} + +int git_merge_head_from_ref( + git_merge_head **out, + git_repository *repo, + git_reference *ref) +{ + git_reference *resolved; + int error = 0; + + assert(out && repo && ref); + + *out = NULL; + + if ((error = git_reference_resolve(&resolved, ref)) < 0) + return error; + + error = merge_head_init(out, repo, git_reference_name(ref), NULL, + git_reference_target(resolved)); + + git_reference_free(resolved); + return error; +} + +int git_merge_head_from_oid( + git_merge_head **out, + git_repository *repo, + const git_oid *oid) +{ + assert(out && repo && oid); + + return merge_head_init(out, repo, NULL, NULL, oid); +} + +int git_merge_head_from_fetchhead( + git_merge_head **out, + git_repository *repo, + const char *branch_name, + const char *remote_url, + const git_oid *oid) +{ + assert(repo && branch_name && remote_url && oid); + + return merge_head_init(out, repo, branch_name, remote_url, oid); +} + +void git_merge_head_free(git_merge_head *head) +{ + if (head == NULL) return; - git_vector_free(&diff_list->staged); - git_vector_free(&diff_list->conflicts); - git_vector_free(&diff_list->resolved); - git_pool_clear(&diff_list->pool); - git__free(diff_list); + if (head->commit != NULL) + git_object_free((git_object *)head->commit); + + if (head->ref_name != NULL) + git__free(head->ref_name); + + if (head->remote_url != NULL) + git__free(head->remote_url); + + git__free(head); } diff --git a/src/merge.h b/src/merge.h index 50538b12b..ba6725de9 100644 --- a/src/merge.h +++ b/src/merge.h @@ -107,12 +107,25 @@ typedef struct { git_delta_t their_status; } git_merge_diff; +/** Internal structure for merge inputs */ +struct git_merge_head { + char *ref_name; + char *remote_url; + + git_oid oid; + git_commit *commit; +}; + int git_merge__bases_many( git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos); +/* + * Three-way tree differencing + */ + git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo); int git_merge_diff_list__find_differences(git_merge_diff_list *merge_diff_list, @@ -124,4 +137,13 @@ int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list void git_merge_diff_list__free(git_merge_diff_list *diff_list); +/* Merge metadata setup */ + +int git_merge__setup( + git_repository *repo, + const git_merge_head *our_head, + const git_merge_head *their_heads[], + size_t their_heads_len, + unsigned int flags); + #endif diff --git a/tests-clar/merge/workdir/setup.c b/tests-clar/merge/workdir/setup.c index 6ae9dbb14..1c8403221 100644 --- a/tests-clar/merge/workdir/setup.c +++ b/tests-clar/merge/workdir/setup.c @@ -8,28 +8,28 @@ static git_repository *repo; static git_index *repo_index; -#define TEST_REPO_PATH "testrepo" -#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" +#define TEST_REPO_PATH "merge-resolve" +#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" -#define ORIG_HEAD "bd593285fc7fe4ca18ccdbabf027f5d689101452" +#define ORIG_HEAD "bd593285fc7fe4ca18ccdbabf027f5d689101452" -#define THEIRS_SIMPLE_BRANCH "branch" -#define THEIRS_SIMPLE_OID "7cb63eed597130ba4abb87b3e544b85021905520" +#define THEIRS_SIMPLE_BRANCH "branch" +#define THEIRS_SIMPLE_OID "7cb63eed597130ba4abb87b3e544b85021905520" -#define OCTO1_BRANCH "octo1" -#define OCTO1_OID "16f825815cfd20a07a75c71554e82d8eede0b061" +#define OCTO1_BRANCH "octo1" +#define OCTO1_OID "16f825815cfd20a07a75c71554e82d8eede0b061" -#define OCTO2_BRANCH "octo2" -#define OCTO2_OID "158dc7bedb202f5b26502bf3574faa7f4238d56c" +#define OCTO2_BRANCH "octo2" +#define OCTO2_OID "158dc7bedb202f5b26502bf3574faa7f4238d56c" -#define OCTO3_BRANCH "octo3" -#define OCTO3_OID "50ce7d7d01217679e26c55939eef119e0c93e272" +#define OCTO3_BRANCH "octo3" +#define OCTO3_OID "50ce7d7d01217679e26c55939eef119e0c93e272" -#define OCTO4_BRANCH "octo4" -#define OCTO4_OID "54269b3f6ec3d7d4ede24dd350dd5d605495c3ae" +#define OCTO4_BRANCH "octo4" +#define OCTO4_OID "54269b3f6ec3d7d4ede24dd350dd5d605495c3ae" -#define OCTO5_BRANCH "octo5" -#define OCTO5_OID "e4f618a2c3ed0669308735727df5ebf2447f022f" +#define OCTO5_BRANCH "octo5" +#define OCTO5_OID "e4f618a2c3ed0669308735727df5ebf2447f022f" // Fixture setup and teardown void test_merge_workdir_setup__initialize(void) @@ -44,6 +44,22 @@ void test_merge_workdir_setup__cleanup(void) cl_git_sandbox_cleanup(); } +static bool test_file_contents(const char *filename, const char *expected) +{ + git_buf file_path_buf = GIT_BUF_INIT, file_buf = GIT_BUF_INIT; + bool equals; + + git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo), filename); + + cl_git_pass(git_futils_readbuffer(&file_buf, file_path_buf.ptr)); + equals = (strcmp(file_buf.ptr, expected) == 0); + + git_buf_free(&file_path_buf); + git_buf_free(&file_buf); + + return equals; +} + static void write_file_contents(const char *filename, const char *output) { git_buf file_path_buf = GIT_BUF_INIT; @@ -55,6 +71,816 @@ static void write_file_contents(const char *filename, const char *output) git_buf_free(&file_path_buf); } +/* git merge --no-ff octo1 */ +void test_merge_workdir_setup__one_branch(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_merge_head *our_head, *their_heads[1]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n")); + + git_reference_free(octo1_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); +} + +/* git merge --no-ff 16f825815cfd20a07a75c71554e82d8eede0b061 */ +void test_merge_workdir_setup__one_oid(void) +{ + git_oid our_oid; + git_oid octo1_oid; + git_merge_head *our_head, *their_heads[1]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'\n")); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); +} + +/* git merge octo1 octo2 */ +void test_merge_workdir_setup__two_branches(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_reference *octo2_ref; + git_merge_head *our_head, *their_heads[2]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO2_BRANCH "'\n")); + + git_reference_free(octo1_ref); + git_reference_free(octo2_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); +} + +/* git merge octo1 octo2 octo3 */ +void test_merge_workdir_setup__three_branches(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_reference *octo2_ref; + git_reference *octo3_ref; + git_merge_head *our_head, *their_heads[3]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + + cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO2_BRANCH "' and '" OCTO3_BRANCH "'\n")); + + git_reference_free(octo1_ref); + git_reference_free(octo2_ref); + git_reference_free(octo3_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); +} + +/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 158dc7bedb202f5b26502bf3574faa7f4238d56c 50ce7d7d01217679e26c55939eef119e0c93e272 */ +void test_merge_workdir_setup__three_oids(void) +{ + git_oid our_oid; + git_oid octo1_oid; + git_oid octo2_oid; + git_oid octo3_oid; + git_merge_head *our_head, *their_heads[3]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid)); + + cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid)); + + cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; commit '" OCTO2_OID "'; commit '" OCTO3_OID "'\n")); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); +} + +/* git merge octo1 158dc7bedb202f5b26502bf3574faa7f4238d56c */ +void test_merge_workdir_setup__branches_and_oids_1(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_oid octo2_oid; + git_merge_head *our_head, *their_heads[2]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'; commit '" OCTO2_OID "'\n")); + + git_reference_free(octo1_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); +} + +/* git merge octo1 158dc7bedb202f5b26502bf3574faa7f4238d56c octo3 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae */ +void test_merge_workdir_setup__branches_and_oids_2(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_oid octo2_oid; + git_reference *octo3_ref; + git_oid octo4_oid; + git_merge_head *our_head, *their_heads[4]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid)); + + cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref)); + + cl_git_pass(git_oid_fromstr(&octo4_oid, OCTO4_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[3], repo, &octo4_oid)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "'; commit '" OCTO2_OID "'; commit '" OCTO4_OID "'\n")); + + git_reference_free(octo1_ref); + git_reference_free(octo3_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); + git_merge_head_free(their_heads[3]); +} + +/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 octo2 50ce7d7d01217679e26c55939eef119e0c93e272 octo4 */ +void test_merge_workdir_setup__branches_and_oids_3(void) +{ + git_oid our_oid; + git_oid octo1_oid; + git_reference *octo2_ref; + git_oid octo3_oid; + git_reference *octo4_ref; + git_merge_head *our_head, *their_heads[4]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid)); + + cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + + cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid)); + + cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; branches '" OCTO2_BRANCH "' and '" OCTO4_BRANCH "'; commit '" OCTO3_OID "'\n")); + + git_reference_free(octo2_ref); + git_reference_free(octo4_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); + git_merge_head_free(their_heads[3]); +} + +/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 octo2 50ce7d7d01217679e26c55939eef119e0c93e272 octo4 octo5 */ +void test_merge_workdir_setup__branches_and_oids_4(void) +{ + git_oid our_oid; + git_oid octo1_oid; + git_reference *octo2_ref; + git_oid octo3_oid; + git_reference *octo4_ref; + git_reference *octo5_ref; + git_merge_head *our_head, *their_heads[5]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid)); + + cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + + cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid)); + + cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref)); + + cl_git_pass(git_reference_lookup(&octo5_ref, repo, GIT_REFS_HEADS_DIR OCTO5_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[4], repo, octo5_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 5, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n" OCTO5_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; branches '" OCTO2_BRANCH "', '" OCTO4_BRANCH "' and '" OCTO5_BRANCH "'; commit '" OCTO3_OID "'\n")); + + git_reference_free(octo2_ref); + git_reference_free(octo4_ref); + git_reference_free(octo5_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); + git_merge_head_free(their_heads[3]); + git_merge_head_free(their_heads[4]); +} + +/* git merge octo1 octo1 octo1 */ +void test_merge_workdir_setup__three_same_branches(void) +{ + git_oid our_oid; + git_reference *octo1_1_ref; + git_reference *octo1_2_ref; + git_reference *octo1_3_ref; + git_merge_head *our_head, *their_heads[3]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_1_ref)); + + cl_git_pass(git_reference_lookup(&octo1_2_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo1_2_ref)); + + cl_git_pass(git_reference_lookup(&octo1_3_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo1_3_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO1_OID "\n" OCTO1_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO1_BRANCH "' and '" OCTO1_BRANCH "'\n")); + + git_reference_free(octo1_1_ref); + git_reference_free(octo1_2_ref); + git_reference_free(octo1_3_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); +} + +/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 16f825815cfd20a07a75c71554e82d8eede0b061 16f825815cfd20a07a75c71554e82d8eede0b061 */ +void test_merge_workdir_setup__three_same_oids(void) +{ + git_oid our_oid; + git_oid octo1_1_oid; + git_oid octo1_2_oid; + git_oid octo1_3_oid; + git_merge_head *our_head, *their_heads[3]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_1_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_1_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_2_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo1_2_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_3_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo1_3_oid)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO1_OID "\n" OCTO1_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; commit '" OCTO1_OID "'; commit '" OCTO1_OID "'\n")); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); +} + +static int create_remote_tracking_branch(const char *branch_name, const char *oid_str) +{ + int error = 0; + + git_buf remotes_path = GIT_BUF_INIT, + origin_path = GIT_BUF_INIT, + filename = GIT_BUF_INIT, + data = GIT_BUF_INIT; + + if ((error = git_buf_puts(&remotes_path, git_repository_path(repo))) < 0 || + (error = git_buf_puts(&remotes_path, GIT_REFS_REMOTES_DIR)) < 0) + goto done; + + if (!git_path_exists(git_buf_cstr(&remotes_path)) && + (error = p_mkdir(git_buf_cstr(&remotes_path), 0777)) < 0) + goto done; + + if ((error = git_buf_puts(&origin_path, git_buf_cstr(&remotes_path))) < 0 || + (error = git_buf_puts(&origin_path, "origin")) < 0) + goto done; + + if (!git_path_exists(git_buf_cstr(&origin_path)) && + (error = p_mkdir(git_buf_cstr(&origin_path), 0777)) < 0) + goto done; + + if ((error = git_buf_puts(&filename, git_buf_cstr(&origin_path))) < 0 || + (error = git_buf_puts(&filename, "/")) < 0 || + (error = git_buf_puts(&filename, branch_name)) < 0 || + (error = git_buf_puts(&data, oid_str)) < 0 || + (error = git_buf_puts(&data, "\n")) < 0) + goto done; + + cl_git_rewritefile(git_buf_cstr(&filename), git_buf_cstr(&data)); + +done: + git_buf_free(&remotes_path); + git_buf_free(&origin_path); + git_buf_free(&filename); + git_buf_free(&data); + + return error; +} + +/* git merge refs/remotes/origin/octo1 */ +void test_merge_workdir_setup__remote_tracking_one_branch(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_merge_head *our_head, *their_heads[1]; + + cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID)); + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branch 'refs/remotes/origin/" OCTO1_BRANCH "'\n")); + + git_reference_free(octo1_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); +} + +/* git merge refs/remotes/origin/octo1 refs/remotes/origin/octo2 */ +void test_merge_workdir_setup__remote_tracking_two_branches(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_reference *octo2_ref; + git_merge_head *our_head, *their_heads[2]; + + cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID)); + cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID)); + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branches 'refs/remotes/origin/" OCTO1_BRANCH "' and 'refs/remotes/origin/" OCTO2_BRANCH "'\n")); + + git_reference_free(octo1_ref); + git_reference_free(octo2_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); +} + +/* git merge refs/remotes/origin/octo1 refs/remotes/origin/octo2 refs/remotes/origin/octo3 */ +void test_merge_workdir_setup__remote_tracking_three_branches(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_reference *octo2_ref; + git_reference *octo3_ref; + git_merge_head *our_head, *their_heads[3]; + + cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID)); + cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID)); + cl_git_pass(create_remote_tracking_branch(OCTO3_BRANCH, OCTO3_OID)); + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + + cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO3_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branches 'refs/remotes/origin/" OCTO1_BRANCH "', 'refs/remotes/origin/" OCTO2_BRANCH "' and 'refs/remotes/origin/" OCTO3_BRANCH "'\n")); + + git_reference_free(octo1_ref); + git_reference_free(octo2_ref); + git_reference_free(octo3_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); +} + +/* git merge octo1 refs/remotes/origin/octo2 */ +void test_merge_workdir_setup__normal_branch_and_remote_tracking_branch(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_reference *octo2_ref; + git_merge_head *our_head, *their_heads[2]; + + cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID)); + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "', remote-tracking branch 'refs/remotes/origin/" OCTO2_BRANCH "'\n")); + + git_reference_free(octo1_ref); + git_reference_free(octo2_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); +} + +/* git merge refs/remotes/origin/octo1 octo2 */ +void test_merge_workdir_setup__remote_tracking_branch_and_normal_branch(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_reference *octo2_ref; + git_merge_head *our_head, *their_heads[2]; + + cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID)); + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO2_BRANCH "', remote-tracking branch 'refs/remotes/origin/" OCTO1_BRANCH "'\n")); + + git_reference_free(octo1_ref); + git_reference_free(octo2_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); +} + +/* git merge octo1 refs/remotes/origin/octo2 octo3 refs/remotes/origin/octo4 */ +void test_merge_workdir_setup__two_remote_tracking_branch_and_two_normal_branches(void) +{ + git_oid our_oid; + git_reference *octo1_ref; + git_reference *octo2_ref; + git_reference *octo3_ref; + git_reference *octo4_ref; + git_merge_head *our_head, *their_heads[4]; + + cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID)); + cl_git_pass(create_remote_tracking_branch(OCTO4_BRANCH, OCTO4_OID)); + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + + cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + + cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref)); + + cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO4_BRANCH)); + cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "', remote-tracking branches 'refs/remotes/origin/" OCTO2_BRANCH "' and 'refs/remotes/origin/" OCTO4_BRANCH "'\n")); + + git_reference_free(octo1_ref); + git_reference_free(octo2_ref); + git_reference_free(octo3_ref); + git_reference_free(octo4_ref); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); + git_merge_head_free(their_heads[3]); +} + +/* git pull origin branch octo1 */ +void test_merge_workdir_setup__pull_one(void) +{ + git_oid our_oid; + git_oid octo1_1_oid; + git_merge_head *our_head, *their_heads[1]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_1_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_1_oid)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch 'octo1' of http://remote.url/repo.git\n")); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); +} + +/* git pull origin octo1 octo2 */ +void test_merge_workdir_setup__pull_two(void) +{ + git_oid our_oid; + git_oid octo1_oid; + git_oid octo2_oid; + git_merge_head *our_head, *their_heads[2]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid)); + + cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO2_BRANCH "' of http://remote.url/repo.git\n")); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); +} + +/* git pull origin octo1 octo2 octo3 */ +void test_merge_workdir_setup__pull_three(void) +{ + git_oid our_oid; + git_oid octo1_oid; + git_oid octo2_oid; + git_oid octo3_oid; + git_merge_head *our_head, *their_heads[3]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid)); + + cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid)); + + cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.url/repo.git", &octo3_oid)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO2_BRANCH "' and '" OCTO3_BRANCH "' of http://remote.url/repo.git\n")); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); +} + +void test_merge_workdir_setup__three_remotes(void) +{ + git_oid our_oid; + git_oid octo1_oid; + git_oid octo2_oid; + git_oid octo3_oid; + git_merge_head *our_head, *their_heads[3]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid)); + + cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid)); + + cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.third/repo.git", &octo3_oid)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "' of http://remote.first/repo.git, branch '" OCTO2_BRANCH "' of http://remote.second/repo.git, branch '" OCTO3_BRANCH "' of http://remote.third/repo.git\n")); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); +} + +void test_merge_workdir_setup__two_remotes(void) +{ + git_oid our_oid; + git_oid octo1_oid; + git_oid octo2_oid; + git_oid octo3_oid; + git_oid octo4_oid; + git_merge_head *our_head, *their_heads[4]; + + cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); + cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + + cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid)); + + cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid)); + + cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.first/repo.git", &octo3_oid)); + + cl_git_pass(git_oid_fromstr(&octo4_oid, OCTO4_OID)); + cl_git_pass(git_merge_head_from_fetchhead(&their_heads[3], repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH, "http://remote.second/repo.git", &octo4_oid)); + + cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0)); + + cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n")); + cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "' of http://remote.first/repo.git, branches '" OCTO2_BRANCH "' and '" OCTO4_BRANCH "' of http://remote.second/repo.git\n")); + + git_merge_head_free(our_head); + git_merge_head_free(their_heads[0]); + git_merge_head_free(their_heads[1]); + git_merge_head_free(their_heads[2]); + git_merge_head_free(their_heads[3]); +} + struct merge_head_cb_data { const char **oid_str; unsigned int len; From 0e0108f73f83c7cedeaafd480fdcfe3cd6b69d1f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 17 May 2013 15:59:57 -0500 Subject: [PATCH 251/384] introduce git_conflict_iterator --- include/git2/index.h | 46 +++++++-- include/git2/types.h | 3 + src/index.c | 174 ++++++++++++++++++++++++++--------- src/index.h | 5 + tests-clar/index/conflicts.c | 51 +++++++++- tests-clar/reset/default.c | 2 +- 6 files changed, 228 insertions(+), 53 deletions(-) diff --git a/include/git2/index.h b/include/git2/index.h index 8a1ccce55..58b0243e0 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -463,9 +463,9 @@ GIT_EXTERN(int) git_index_conflict_add( /** * Get the index entries that represent a conflict of a single file. * - * The values of this entry can be modified (except the paths) - * and the changes will be written back to disk on the next - * write() call. + * The entries are not modifiable and should not be freed. Because the + * `git_index_entry` struct is a publicly defined struct, you should + * be able to make your own permanent copy of the data if necessary. * * @param ancestor_out Pointer to store the ancestor entry * @param our_out Pointer to store the our entry @@ -474,9 +474,9 @@ GIT_EXTERN(int) git_index_conflict_add( * @param path path to search */ GIT_EXTERN(int) git_index_conflict_get( - git_index_entry **ancestor_out, - git_index_entry **our_out, - git_index_entry **their_out, + const git_index_entry **ancestor_out, + const git_index_entry **our_out, + const git_index_entry **their_out, git_index *index, const char *path); @@ -502,6 +502,40 @@ GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index); */ GIT_EXTERN(int) git_index_has_conflicts(const git_index *index); +/** + * Create an iterator for the conflicts in the index. You may not modify the + * index while iterating, the results are undefined. + * + * @return 0 or an error code + */ +GIT_EXTERN(int) git_index_conflict_iterator_new( + git_index_conflict_iterator **iterator_out, + git_index *index); + +/** + * Returns the current conflict (ancestor, ours and theirs entry) and + * advance the iterator internally to the next value. + * + * @param ancestor_out Pointer to store the ancestor side of the conflict + * @param our_out Pointer to store our side of the conflict + * @param their_out Pointer to store their side of the conflict + * @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code + * (negative value) + */ +GIT_EXTERN(int) git_index_conflict_next( + const git_index_entry **ancestor_out, + const git_index_entry **our_out, + const git_index_entry **their_out, + git_index_conflict_iterator *iterator); + +/** + * Frees a `git_index_conflict_iterator`. + * + * @param it pointer to the iterator + */ +GIT_EXTERN(void) git_index_conflict_iterator_free( + git_index_conflict_iterator *iterator); + /**@}*/ /** @} */ diff --git a/include/git2/types.h b/include/git2/types.h index 43751d3b0..a589cdbaf 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -131,6 +131,9 @@ typedef struct git_treebuilder git_treebuilder; /** Memory representation of an index file. */ typedef struct git_index git_index; +/** An interator for conflicts in the index. */ +typedef struct git_index_conflict_iterator git_index_conflict_iterator; + /** Memory representation of a set of config files */ typedef struct git_config git_config; diff --git a/src/index.c b/src/index.c index f7f7133d6..ec45a5c0e 100644 --- a/src/index.c +++ b/src/index.c @@ -739,7 +739,7 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace) static int index_conflict_to_reuc(git_index *index, const char *path) { - git_index_entry *conflict_entries[3]; + const git_index_entry *conflict_entries[3]; int ancestor_mode, our_mode, their_mode; git_oid const *ancestor_oid, *our_oid, *their_oid; int ret; @@ -978,15 +978,63 @@ on_error: return ret; } -int git_index_conflict_get(git_index_entry **ancestor_out, - git_index_entry **our_out, - git_index_entry **their_out, - git_index *index, const char *path) +static int index_conflict__get_byindex( + const git_index_entry **ancestor_out, + const git_index_entry **our_out, + const git_index_entry **their_out, + git_index *index, + size_t n) { - size_t pos, posmax; - int stage; - git_index_entry *conflict_entry; - int error = GIT_ENOTFOUND; + const git_index_entry *conflict_entry; + const char *path = NULL; + size_t count; + int stage, len = 0; + + assert(ancestor_out && our_out && their_out && index); + + *ancestor_out = NULL; + *our_out = NULL; + *their_out = NULL; + + for (count = git_index_entrycount(index); n < count; ++n) { + conflict_entry = git_vector_get(&index->entries, n); + + if (path && index->entries_cmp_path(conflict_entry->path, path) != 0) + break; + + stage = GIT_IDXENTRY_STAGE(conflict_entry); + path = conflict_entry->path; + + switch (stage) { + case 3: + *their_out = conflict_entry; + len++; + break; + case 2: + *our_out = conflict_entry; + len++; + break; + case 1: + *ancestor_out = conflict_entry; + len++; + break; + default: + break; + }; + } + + return len; +} + +int git_index_conflict_get( + const git_index_entry **ancestor_out, + const git_index_entry **our_out, + const git_index_entry **their_out, + git_index *index, + const char *path) +{ + size_t pos; + int len = 0; assert(ancestor_out && our_out && their_out && index && path); @@ -997,33 +1045,13 @@ int git_index_conflict_get(git_index_entry **ancestor_out, if (git_index_find(&pos, index, path) < 0) return GIT_ENOTFOUND; - for (posmax = git_index_entrycount(index); pos < posmax; ++pos) { - conflict_entry = git_vector_get(&index->entries, pos); + if ((len = index_conflict__get_byindex( + ancestor_out, our_out, their_out, index, pos)) < 0) + return len; + else if (len == 0) + return GIT_ENOTFOUND; - if (index->entries_cmp_path(conflict_entry->path, path) != 0) - break; - - stage = GIT_IDXENTRY_STAGE(conflict_entry); - - switch (stage) { - case 3: - *their_out = conflict_entry; - error = 0; - break; - case 2: - *our_out = conflict_entry; - error = 0; - break; - case 1: - *ancestor_out = conflict_entry; - error = 0; - break; - default: - break; - }; - } - - return error; + return 0; } int git_index_conflict_remove(git_index *index, const char *path) @@ -1093,6 +1121,68 @@ int git_index_has_conflicts(const git_index *index) return 0; } +int git_index_conflict_iterator_new( + git_index_conflict_iterator **iterator_out, + git_index *index) +{ + git_index_conflict_iterator *it = NULL; + + assert(iterator_out && index); + + it = git__calloc(1, sizeof(git_index_conflict_iterator)); + GITERR_CHECK_ALLOC(it); + + it->index = index; + + *iterator_out = it; + return 0; +} + +int git_index_conflict_next( + const git_index_entry **ancestor_out, + const git_index_entry **our_out, + const git_index_entry **their_out, + git_index_conflict_iterator *iterator) +{ + const git_index_entry *entry; + int len; + + assert(ancestor_out && our_out && their_out && iterator); + + *ancestor_out = NULL; + *our_out = NULL; + *their_out = NULL; + + while (iterator->cur < iterator->index->entries.length) { + entry = git_index_get_byindex(iterator->index, iterator->cur); + + if (git_index_entry_stage(entry) > 0) { + if ((len = index_conflict__get_byindex( + ancestor_out, + our_out, + their_out, + iterator->index, + iterator->cur)) < 0) + return len; + + iterator->cur += len; + return 0; + } + + iterator->cur++; + } + + return GIT_ITEROVER; +} + +void git_index_conflict_iterator_free(git_index_conflict_iterator *iterator) +{ + if (iterator == NULL) + return; + + git__free(iterator); +} + unsigned int git_index_name_entrycount(git_index *index) { assert(index); @@ -1283,9 +1373,8 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) size_t len; int i; - /* If called multiple times, the vector might already be initialized */ - if (index->reuc._alloc_size == 0 && - git_vector_init(&index->reuc, 16, reuc_cmp) < 0) + /* This gets called multiple times, the vector might already be initialized */ + if (index->reuc._alloc_size == 0 && git_vector_init(&index->reuc, 16, reuc_cmp) < 0) return -1; while (size) { @@ -1295,9 +1384,12 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) if (size <= len) return index_error_invalid("reading reuc entries"); - lost = git__calloc(1, sizeof(git_index_reuc_entry)); + lost = git__malloc(sizeof(git_index_reuc_entry)); GITERR_CHECK_ALLOC(lost); + if (git_vector_insert(&index->reuc, lost) < 0) + return -1; + /* read NUL-terminated pathname for entry */ lost->path = git__strdup(buffer); GITERR_CHECK_ALLOC(lost->path); @@ -1335,10 +1427,6 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) size -= 20; buffer += 20; } - - /* entry was read successfully - insert into reuc vector */ - if (git_vector_insert(&index->reuc, lost) < 0) - return -1; } /* entries are guaranteed to be sorted on-disk */ diff --git a/src/index.h b/src/index.h index 2ad401741..a59107a7b 100644 --- a/src/index.h +++ b/src/index.h @@ -42,6 +42,11 @@ struct git_index { git_vector_cmp reuc_search; }; +struct git_index_conflict_iterator { + git_index *index; + size_t cur; +}; + extern void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st); extern size_t git_index__prefix_position(git_index *index, const char *path); diff --git a/tests-clar/index/conflicts.c b/tests-clar/index/conflicts.c index 7eee496de..6311b3a75 100644 --- a/tests-clar/index/conflicts.c +++ b/tests-clar/index/conflicts.c @@ -65,7 +65,7 @@ void test_index_conflicts__add(void) void test_index_conflicts__add_fixes_incorrect_stage(void) { git_index_entry ancestor_entry, our_entry, their_entry; - git_index_entry *conflict_entry[3]; + const git_index_entry *conflict_entry[3]; cl_assert(git_index_entrycount(repo_index) == 8); @@ -98,7 +98,7 @@ void test_index_conflicts__add_fixes_incorrect_stage(void) void test_index_conflicts__get(void) { - git_index_entry *conflict_entry[3]; + const git_index_entry *conflict_entry[3]; git_oid oid; cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], @@ -130,6 +130,51 @@ void test_index_conflicts__get(void) cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0); } +void test_index_conflicts__iterate(void) +{ + git_index_conflict_iterator *iterator; + const git_index_entry *conflict_entry[3]; + git_oid oid; + + cl_git_pass(git_index_conflict_iterator_new(&iterator, repo_index)); + + cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator)); + + git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID); + cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0); + + git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID); + cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0); + + git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID); + cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0); + + cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator)); + + git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID); + cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0); + + git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID); + cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0); + + git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID); + cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0); + cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0); + + cl_assert(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator) == GIT_ITEROVER); + + cl_assert(conflict_entry[0] == NULL); + cl_assert(conflict_entry[2] == NULL); + cl_assert(conflict_entry[2] == NULL); + + git_index_conflict_iterator_free(iterator); +} + void test_index_conflicts__remove(void) { const git_index_entry *entry; @@ -218,7 +263,7 @@ void test_index_conflicts__remove_all_conflicts(void) void test_index_conflicts__partial(void) { git_index_entry ancestor_entry, our_entry, their_entry; - git_index_entry *conflict_entry[3]; + const git_index_entry *conflict_entry[3]; cl_assert(git_index_entrycount(repo_index) == 8); diff --git a/tests-clar/reset/default.c b/tests-clar/reset/default.c index 506d971ff..e29e63550 100644 --- a/tests-clar/reset/default.c +++ b/tests-clar/reset/default.c @@ -133,7 +133,7 @@ void test_reset_default__resetting_filepaths_replaces_their_corresponding_index_ */ void test_reset_default__resetting_filepaths_clears_previous_conflicts(void) { - git_index_entry *conflict_entry[3]; + const git_index_entry *conflict_entry[3]; git_strarray after; char *paths[] = { "conflicts-one.txt" }; From 660d59caa9cd6260fbc980e7da15f806d6d53083 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 17 May 2013 16:40:00 -0700 Subject: [PATCH 252/384] Add git_oid_nfmt - a flexible OID formatter I frequently want to the the first N digits of an OID formatted as a string and I'd like it to be efficient. This function makes that easy and I could rewrite the OID formatters in terms of it. --- include/git2/oid.h | 21 ++++++++++++--- src/oid.c | 48 +++++++++++++++++++-------------- tests-clar/object/raw/convert.c | 38 ++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 24 deletions(-) diff --git a/include/git2/oid.h b/include/git2/oid.h index b20bb221a..018cead4a 100644 --- a/include/git2/oid.h +++ b/include/git2/oid.h @@ -89,6 +89,17 @@ GIT_EXTERN(void) git_oid_fromraw(git_oid *out, const unsigned char *raw); */ GIT_EXTERN(void) git_oid_fmt(char *out, const git_oid *id); +/** + * Format a git_oid into a partial hex string. + * + * @param out output hex string; you say how many bytes to write. + * If the number of bytes is > GIT_OID_HEXSZ, extra bytes + * will be zeroed; if not, a '\0' terminator is NOT added. + * @param n number of characters to write into out string + * @param oid oid structure to format. + */ +GIT_EXTERN(void) git_oid_nfmt(char *out, size_t n, const git_oid *id); + /** * Format a git_oid into a loose-object path string. * @@ -117,10 +128,12 @@ GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *id); * Format a git_oid into a buffer as a hex format c-string. * * If the buffer is smaller than GIT_OID_HEXSZ+1, then the resulting - * oid c-string will be truncated to n-1 characters. If there are - * any input parameter errors (out == NULL, n == 0, oid == NULL), - * then a pointer to an empty string is returned, so that the return - * value can always be printed. + * oid c-string will be truncated to n-1 characters (but will still be + * NUL-byte terminated). + * + * If there are any input parameter errors (out == NULL, n == 0, oid == + * NULL), then a pointer to an empty string is returned, so that the + * return value can always be printed. * * @param out the buffer into which the oid string is output. * @param n the size of the out buffer. diff --git a/src/oid.c b/src/oid.c index e74640c57..a64bd3b0a 100644 --- a/src/oid.c +++ b/src/oid.c @@ -68,12 +68,31 @@ GIT_INLINE(char) *fmt_one(char *str, unsigned int val) return str; } +void git_oid_nfmt(char *str, size_t n, const git_oid *oid) +{ + size_t i, max_i; + + if (!oid) { + memset(str, 0, n); + return; + } + if (n > GIT_OID_HEXSZ) { + memset(&str[GIT_OID_HEXSZ], 0, n - GIT_OID_HEXSZ); + n = GIT_OID_HEXSZ; + } + + max_i = n / 2; + + for (i = 0; i < max_i; i++) + str = fmt_one(str, oid->id[i]); + + if (n & 1) + *str++ = to_hex[oid->id[i] >> 4]; +} + void git_oid_fmt(char *str, const git_oid *oid) { - size_t i; - - for (i = 0; i < sizeof(oid->id); i++) - str = fmt_one(str, oid->id[i]); + git_oid_nfmt(str, GIT_OID_HEXSZ, oid); } void git_oid_pathfmt(char *str, const git_oid *oid) @@ -91,31 +110,20 @@ char *git_oid_allocfmt(const git_oid *oid) char *str = git__malloc(GIT_OID_HEXSZ + 1); if (!str) return NULL; - git_oid_fmt(str, oid); - str[GIT_OID_HEXSZ] = '\0'; + git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid); return str; } char *git_oid_tostr(char *out, size_t n, const git_oid *oid) { - char str[GIT_OID_HEXSZ]; - if (!out || n == 0) return ""; - n--; /* allow room for terminating NUL */ + if (n > GIT_OID_HEXSZ + 1) + n = GIT_OID_HEXSZ + 1; - if (oid == NULL) - n = 0; - - if (n > 0) { - git_oid_fmt(str, oid); - if (n > GIT_OID_HEXSZ) - n = GIT_OID_HEXSZ; - memcpy(out, str, n); - } - - out[n] = '\0'; + git_oid_nfmt(out, n - 1, oid); /* allow room for terminating NUL */ + out[n - 1] = '\0'; return out; } diff --git a/tests-clar/object/raw/convert.c b/tests-clar/object/raw/convert.c index 74442c153..86f0d74a9 100644 --- a/tests-clar/object/raw/convert.c +++ b/tests-clar/object/raw/convert.c @@ -73,3 +73,41 @@ void test_object_raw_convert__succeed_on_oid_to_string_conversion_big(void) cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+2) == 'Y'); cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+3) == 'Z'); } + +static void check_partial_oid( + char *buffer, size_t count, const git_oid *oid, const char *expected) +{ + git_oid_nfmt(buffer, count, oid); + buffer[count] = '\0'; + cl_assert_equal_s(expected, buffer); +} + +void test_object_raw_convert__convert_oid_partially(void) +{ + const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0"; + git_oid in; + char big[GIT_OID_HEXSZ + 1 + 3]; /* note + 4 => big buffer */ + char *str; + + cl_git_pass(git_oid_fromstr(&in, exp)); + + git_oid_nfmt(big, sizeof(big), &in); + cl_assert_equal_s(exp, big); + + git_oid_nfmt(big, GIT_OID_HEXSZ + 1, &in); + cl_assert_equal_s(exp, big); + + check_partial_oid(big, 1, &in, "1"); + check_partial_oid(big, 2, &in, "16"); + check_partial_oid(big, 3, &in, "16a"); + check_partial_oid(big, 4, &in, "16a0"); + check_partial_oid(big, 5, &in, "16a01"); + + check_partial_oid(big, GIT_OID_HEXSZ, &in, exp); + check_partial_oid( + big, GIT_OID_HEXSZ - 1, &in, "16a0123456789abcdef4b775213c23a8bd74f5e"); + check_partial_oid( + big, GIT_OID_HEXSZ - 2, &in, "16a0123456789abcdef4b775213c23a8bd74f5"); + check_partial_oid( + big, GIT_OID_HEXSZ - 3, &in, "16a0123456789abcdef4b775213c23a8bd74f"); +} From aadfa85b0a8f9a8fec4dedd149017a769d54d930 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 17 May 2013 16:41:15 -0700 Subject: [PATCH 253/384] Add git_diff_print_raw printing helper Makes it easier to emulate the --raw option --- include/git2/diff.h | 16 +++++++++++ src/diff_output.c | 68 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 1feddd7a2..54966f14a 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -688,6 +688,22 @@ GIT_EXTERN(int) git_diff_print_compact( git_diff_data_cb print_cb, void *payload); +/** + * Iterate over a diff generating text output like "git diff --raw". + * + * Returning a non-zero value from the callbacks will terminate the + * iteration and cause this return `GIT_EUSER`. + * + * @param diff A git_diff_list generated by one of the above functions. + * @param print_cb Callback to make per line of diff text. + * @param payload Reference pointer that will be passed to your callback. + * @return 0 on success, GIT_EUSER on non-zero callback, or error code + */ +GIT_EXTERN(int) git_diff_print_raw( + git_diff_list *diff, + git_diff_data_cb print_cb, + void *payload); + /** * Look up the single character abbreviation for a delta status code. * diff --git a/src/diff_output.c b/src/diff_output.c index be1ff56e7..4ea9e4baf 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -1104,12 +1104,7 @@ int git_diff_print_compact( { int error; git_buf buf = GIT_BUF_INIT; - diff_print_info pi; - - pi.diff = diff; - pi.print_cb = print_cb; - pi.payload = payload; - pi.buf = &buf; + diff_print_info pi = { diff, print_cb, payload, &buf }; error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi); @@ -1118,6 +1113,67 @@ int git_diff_print_compact( return error; } +static int print_raw( + const git_diff_delta *delta, float progress, void *data) +{ + diff_print_info *pi = data; + char code = git_diff_status_char(delta->status); + char ooid[8], noid[8]; + + GIT_UNUSED(progress); + + if (code == ' ') + return 0; + + git_buf_clear(pi->buf); + + git_oid_nfmt(ooid, sizeof(ooid), &delta->old_file.oid); + ooid[7] = '\0'; + + git_oid_nfmt(noid, sizeof(noid), &delta->new_file.oid); + noid[7] = '\0'; + + git_buf_printf( + pi->buf, ":%06o %06o %s... %s... %c", + delta->old_file.mode, delta->new_file.mode, ooid, noid, code); + + if (delta->similarity > 0) + git_buf_printf(pi->buf, "%03u", delta->similarity); + + if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED) + git_buf_printf( + pi->buf, "\t%s %s\n", delta->old_file.path, delta->new_file.path); + else + git_buf_printf( + pi->buf, "\t%s\n", delta->old_file.path ? + delta->old_file.path : delta->new_file.path); + + if (git_buf_oom(pi->buf)) + return -1; + + if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, + git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) + return callback_error(); + + return 0; +} + +int git_diff_print_raw( + git_diff_list *diff, + git_diff_data_cb print_cb, + void *payload) +{ + int error; + git_buf buf = GIT_BUF_INIT; + diff_print_info pi = { diff, print_cb, payload, &buf }; + + error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi); + + git_buf_free(&buf); + + return error; +} + static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta) { int abbrevlen; From 0293450e217c1cb6966655bb00cc11fdf626083d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 17 May 2013 16:57:49 -0700 Subject: [PATCH 254/384] Fix delta compare to use correct pathname The delta cmp function needs to choose the correct path for ordering when a delta is ADDED, RENAMED, or COPIED. --- src/diff.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index d93506984..d2389f103 100644 --- a/src/diff.c +++ b/src/diff.c @@ -231,10 +231,23 @@ static char *diff_strdup_prefix(git_pool *pool, const char *prefix) return git_pool_strndup(pool, prefix, len + 1); } +GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta) +{ + const char *str = delta->old_file.path; + + if (!str || + delta->status == GIT_DELTA_ADDED || + delta->status == GIT_DELTA_RENAMED || + delta->status == GIT_DELTA_COPIED) + str = delta->new_file.path; + + return str; +} + int git_diff_delta__cmp(const void *a, const void *b) { const git_diff_delta *da = a, *db = b; - int val = strcmp(da->old_file.path, db->old_file.path); + int val = strcmp(diff_delta__path(da), diff_delta__path(db)); return val ? val : ((int)da->status - (int)db->status); } From d958e37a48c693a1907bede563bd835935d23499 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 17 May 2013 17:21:45 -0700 Subject: [PATCH 255/384] Fix issues with git_diff_find_similar There are a number of bugs in the rename code that only were obvious when I started testing it against large old repos with more complex patterns. (The code to do that testing is not ready to merge with libgit2, but I do plan to add more thorough tests.) This contains a significant number of changes and also tweaks the public API slightly to make emulating core git easier. Most notably, this separates the GIT_DIFF_FIND_AND_BREAK_REWRITES flag into FIND_REWRITES (which adds a self-similarity score to every modified file) and BREAK_REWRITES (which splits the modified deltas into add/remove pairs in the diff list). When you do a raw output of core git, rewrites show up as M090 or such, not at A and D output, so I wanted to be able to emulate that. Publicly, this also changes the flags to be uint16_t since we don't need values out of that range. Internally, this contains significant changes from a number of small bug fixes (like using the wrong side of the diff to decide if the object could be found in the ODB vs the workdir) to larger issues about which files can and should be compared and how the various edge cases of similarity scores should be treated. Honestly, I don't think this is the last update that will have to be made to this code, but I think this moves us closer to correct behavior and I tried to document the code so it would be easier to follow.. --- include/git2/diff.h | 43 ++++++-- src/diff_tform.c | 233 +++++++++++++++++++++++++++------------ src/util.h | 7 ++ tests-clar/diff/rename.c | 3 +- 4 files changed, 202 insertions(+), 84 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 54966f14a..172aa118b 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -243,6 +243,19 @@ typedef struct { * `NOT_BINARY` flag set to avoid examining file contents if you do not pass * in hunk and/or line callbacks to the diff foreach iteration function. It * will just use the git attributes for those files. + * + * The similarity score is zero unless you call `git_diff_find_similar()` + * which does a similarity analysis of files in the diff. Use that + * function to do rename and copy detection, and to split heavily modified + * files in add/delete pairs. After that call, deltas with a status of + * GIT_DELTA_RENAMED or GIT_DELTA_COPIED will have a similarity score + * between 0 and 100 indicating how similar the old and new sides are. + * + * If you ask `git_diff_find_similar` to find heavily modified files to + * break, but to not *actually* break the records, then GIT_DELTA_MODIFIED + * records may have a non-zero similarity score if the self-similarity is + * below the split threshold. To display this value like core Git, invert + * the score (a la `printf("M%03d", 100 - delta->similarity)`). */ typedef struct { git_diff_file old_file; @@ -408,18 +421,26 @@ typedef enum { /** consider unmodified as copy sources? (`--find-copies-harder`) */ GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1 << 3), - /** split large rewrites into delete/add pairs (`--break-rewrites=/M`) */ - GIT_DIFF_FIND_AND_BREAK_REWRITES = (1 << 4), + /** mark large rewrites for split (`--break-rewrites=/M`) */ + GIT_DIFF_FIND_REWRITES = (1 << 4), + /** actually split large rewrites into delete/add pairs */ + GIT_DIFF_BREAK_REWRITES = (1 << 5), + /** mark rewrites for split and break into delete/add pairs */ + GIT_DIFF_FIND_AND_BREAK_REWRITES = + (GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES), + + /** consider untracked files as rename/copy targets */ + GIT_DIFF_FIND_FROM_UNTRACKED = (1 << 6), /** turn on all finding features */ - GIT_DIFF_FIND_ALL = (0x1f), + GIT_DIFF_FIND_ALL = (0x0ff), /** measure similarity ignoring leading whitespace (default) */ GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0, /** measure similarity ignoring all whitespace */ - GIT_DIFF_FIND_IGNORE_WHITESPACE = (1 << 6), + GIT_DIFF_FIND_IGNORE_WHITESPACE = (1 << 12), /** measure similarity including all data */ - GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1 << 7), + GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1 << 13), } git_diff_find_t; /** @@ -446,7 +467,7 @@ typedef struct { * - `copy_threshold` is the same as the -C option with a value * - `rename_from_rewrite_threshold` matches the top of the -B option * - `break_rewrite_threshold` matches the bottom of the -B option - * - `target_limit` matches the -l option + * - `target_limit` matches the -l option (approximately) * * The `metric` option allows you to plug in a custom similarity metric. * Set it to NULL for the default internal metric which is based on sampling @@ -461,18 +482,18 @@ typedef struct { unsigned int flags; /** Similarity to consider a file renamed (default 50) */ - unsigned int rename_threshold; + uint16_t rename_threshold; /** Similarity of modified to be eligible rename source (default 50) */ - unsigned int rename_from_rewrite_threshold; + uint16_t rename_from_rewrite_threshold; /** Similarity to consider a file a copy (default 50) */ - unsigned int copy_threshold; + uint16_t copy_threshold; /** Similarity to split modify into delete/add pair (default 60) */ - unsigned int break_rewrite_threshold; + uint16_t break_rewrite_threshold; /** Maximum similarity sources to examine (a la diff's `-l` option or * the `diff.renameLimit` config) (default 200) */ - unsigned int target_limit; + size_t target_limit; /** Pluggable similarity metric; pass NULL to use internal metric */ git_diff_similarity_metric *metric; diff --git a/src/diff_tform.c b/src/diff_tform.c index 84650a37b..33268e403 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -19,11 +19,13 @@ static git_diff_delta *diff_delta__dup( memcpy(delta, d, sizeof(git_diff_delta)); - delta->old_file.path = git_pool_strdup(pool, d->old_file.path); - if (delta->old_file.path == NULL) - goto fail; + if (d->old_file.path != NULL) { + delta->old_file.path = git_pool_strdup(pool, d->old_file.path); + if (delta->old_file.path == NULL) + goto fail; + } - if (d->new_file.path != d->old_file.path) { + if (d->new_file.path != d->old_file.path && d->new_file.path != NULL) { delta->new_file.path = git_pool_strdup(pool, d->new_file.path); if (delta->new_file.path == NULL) goto fail; @@ -259,6 +261,9 @@ static int normalize_find_opts( if (opts->flags & GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED) opts->flags |= GIT_DIFF_FIND_COPIES; + if (opts->flags & GIT_DIFF_BREAK_REWRITES) + opts->flags |= GIT_DIFF_FIND_REWRITES; + #define USE_DEFAULT(X) ((X) == 0 || (X) > 100) if (USE_DEFAULT(opts->rename_threshold)) @@ -307,11 +312,33 @@ static int normalize_find_opts( return 0; } -static int apply_splits_and_deletes(git_diff_list *diff, size_t expected_size) +static void validate_delta(git_diff_delta *delta) +{ + assert(delta); + return; +/* + switch (delta->status) { + case GIT_DELTA_ADDED: + case GIT_DELTA_UNTRACKED: + case GIT_DELTA_IGNORED: + assert(delta->new_file.path); + break; + case GIT_DELTA_DELETED: + assert(delta->old_file.path); + break; + default: + assert(delta->old_file.path && delta->new_file.path); + break; + } +*/ +} + +static int apply_splits_and_deletes( + git_diff_list *diff, size_t expected_size, bool actually_split) { git_vector onto = GIT_VECTOR_INIT; size_t i; - git_diff_delta *delta; + git_diff_delta *delta, *deleted; if (git_vector_init(&onto, expected_size, git_diff_delta__cmp) < 0) return -1; @@ -322,14 +349,26 @@ static int apply_splits_and_deletes(git_diff_list *diff, size_t expected_size) continue; if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { - git_diff_delta *deleted = diff_delta__dup(delta, &diff->pool); - if (!deleted) + + /* just leave delta flagged with score if not actually splitting */ + if (!actually_split) { + delta->flags = (delta->flags & ~GIT_DIFF_FLAG__TO_SPLIT); + if (delta->status != GIT_DELTA_MODIFIED) + delta->similarity = 0; + continue; + } + + delta->similarity = 0; + + /* make new record for DELETED side of split */ + if (!(deleted = diff_delta__dup(delta, &diff->pool))) goto on_error; deleted->status = GIT_DELTA_DELETED; memset(&deleted->new_file, 0, sizeof(deleted->new_file)); deleted->new_file.path = deleted->old_file.path; deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; + validate_delta(deleted); if (git_vector_insert(&onto, deleted) < 0) goto on_error; @@ -338,6 +377,7 @@ static int apply_splits_and_deletes(git_diff_list *diff, size_t expected_size) memset(&delta->old_file, 0, sizeof(delta->old_file)); delta->old_file.path = delta->new_file.path; delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; + validate_delta(delta); } if (git_vector_insert(&onto, delta) < 0) @@ -350,7 +390,6 @@ static int apply_splits_and_deletes(git_diff_list *diff, size_t expected_size) git__free(delta); /* swap new delta list into place */ - git_vector_sort(&onto); git_vector_swap(&diff->deltas, &onto); git_vector_free(&onto); @@ -359,7 +398,6 @@ static int apply_splits_and_deletes(git_diff_list *diff, size_t expected_size) on_error: git_vector_foreach(&onto, i, delta) git__free(delta); - git_vector_free(&onto); return -1; @@ -379,7 +417,7 @@ static int similarity_calc( { int error = 0; git_diff_file *file = similarity_get_file(diff, file_idx); - git_iterator_type_t src = (file_idx & 1) ? diff->old_src : diff->new_src; + git_iterator_type_t src = (file_idx & 1) ? diff->new_src : diff->old_src; if (src == GIT_ITERATOR_TYPE_WORKDIR) { /* compute hashsig from file */ git_buf path = GIT_BUF_INIT; @@ -455,8 +493,8 @@ static int similarity_measure( return -1; /* clip score */ - if (score < 0) - score = 0; + if (score < 1) + score = 1; /* zero means uncomparable, so use 1 for least similar */ else if (score > 100) score = 100; @@ -465,36 +503,50 @@ static int similarity_measure( #define FLAG_SET(opts,flag_name) ((opts.flags & flag_name) != 0) +typedef struct { + uint32_t idx; + uint32_t similarity; +} diff_find_match; + int git_diff_find_similar( git_diff_list *diff, git_diff_find_options *given_opts) { - size_t i, j, cache_size, *matches; + size_t i, j, cache_size; int error = 0, similarity; git_diff_delta *from, *to; git_diff_find_options opts; - size_t tried_targets, num_rewrites = 0; - void **cache; + size_t num_rewrites = 0, num_updates = 0; + void **cache; /* cache of similarity metric file signatures */ + diff_find_match *matches; /* cache of best matches */ if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0) return error; /* TODO: maybe abort if deltas.length > target_limit ??? */ + if (!git__is_uint32(diff->deltas.length)) + return 0; cache_size = diff->deltas.length * 2; /* must store b/c length may change */ cache = git__calloc(cache_size, sizeof(void *)); GITERR_CHECK_ALLOC(cache); - matches = git__calloc(diff->deltas.length, sizeof(size_t)); + matches = git__calloc(diff->deltas.length, sizeof(diff_find_match)); GITERR_CHECK_ALLOC(matches); - /* first break MODIFIED records that are too different (if requested) */ + /* first mark MODIFIED deltas to split if too different (if requested) */ - if (FLAG_SET(opts, GIT_DIFF_FIND_AND_BREAK_REWRITES)) { + if (FLAG_SET(opts, GIT_DIFF_FIND_REWRITES)) { git_vector_foreach(&diff->deltas, i, from) { if (from->status != GIT_DELTA_MODIFIED) continue; + /* skip things that aren't plain blobs */ + if (GIT_MODE_TYPE(from->old_file.mode) != + GIT_MODE_TYPE(GIT_FILEMODE_BLOB)) + continue; + + /* measure similarity from old_file to new_file */ similarity = similarity_measure( diff, &opts, cache, 2 * i, 2 * i + 1); @@ -503,7 +555,9 @@ int git_diff_find_similar( goto cleanup; } - if ((unsigned int)similarity < opts.break_rewrite_threshold) { + if (similarity > 0 && + similarity < (int)opts.break_rewrite_threshold) { + from->similarity = (uint32_t)similarity; from->flags |= GIT_DIFF_FLAG__TO_SPLIT; num_rewrites++; } @@ -513,9 +567,12 @@ int git_diff_find_similar( /* next find the most similar delta for each rename / copy candidate */ git_vector_foreach(&diff->deltas, i, from) { - tried_targets = 0; + size_t tried_targets = 0; - /* skip things that aren't blobs */ + matches[i].idx = i; + matches[i].similarity = 0; + + /* skip things that aren't plain blobs */ if (GIT_MODE_TYPE(from->old_file.mode) != GIT_MODE_TYPE(GIT_FILEMODE_BLOB)) continue; @@ -525,7 +582,13 @@ int git_diff_find_similar( !FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)) continue; - /* skip all but DELETED files unless copy detection is on */ + /* don't check UNTRACKED files as source unless given option */ + if ((from->status == GIT_DELTA_UNTRACKED || + from->status == GIT_DELTA_IGNORED) && + !FLAG_SET(opts, GIT_DIFF_FIND_FROM_UNTRACKED)) + continue; + + /* only use DELETED (or split MODIFIED) unless copy detection on */ if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES) && from->status != GIT_DELTA_DELETED && (from->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0) @@ -540,9 +603,11 @@ int git_diff_find_similar( GIT_MODE_TYPE(GIT_FILEMODE_BLOB)) continue; + /* only consider ADDED, RENAMED, COPIED, and split MODIFIED as + * targets; maybe include UNTRACKED and IGNORED if requested. + */ switch (to->status) { case GIT_DELTA_ADDED: - case GIT_DELTA_UNTRACKED: case GIT_DELTA_RENAMED: case GIT_DELTA_COPIED: break; @@ -550,18 +615,21 @@ int git_diff_find_similar( if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0) continue; break; + case GIT_DELTA_UNTRACKED: + case GIT_DELTA_IGNORED: + if (!FLAG_SET(opts, GIT_DIFF_FIND_FROM_UNTRACKED)) + continue; + break; default: - /* only the above status values should be checked */ + /* all other status values will be skipped */ continue; } - /* cap on maximum files we'll examine (per "from" file) */ + /* cap on maximum targets we'll examine (per "from" file) */ if (++tried_targets > opts.target_limit) break; - /* calculate similarity and see if this pair beats the - * similarity score of the current best pair. - */ + /* calculate similarity for this pair and find best match */ similarity = similarity_measure( diff, &opts, cache, 2 * i, 2 * j + 1); @@ -570,112 +638,133 @@ int git_diff_find_similar( goto cleanup; } - if (to->similarity < (unsigned int)similarity) { - to->similarity = (unsigned int)similarity; - matches[j] = i + 1; + if (matches[i].similarity < (uint32_t)similarity) { + matches[i].similarity = (uint32_t)similarity; + matches[i].idx = j; } } } /* next rewrite the diffs with renames / copies */ - git_vector_foreach(&diff->deltas, j, to) { - if (!matches[j]) { - assert(to->similarity == 0); + git_vector_foreach(&diff->deltas, i, from) { + if (!matches[i].similarity) continue; - } - i = matches[j] - 1; - from = GIT_VECTOR_GET(&diff->deltas, i); - assert(from); + to = GIT_VECTOR_GET(&diff->deltas, matches[i].idx); + assert(to); - /* four possible outcomes here: - * 1. old DELETED and if over rename threshold, - * new becomes RENAMED and old goes away - * 2. old SPLIT and if over rename threshold, - * new becomes RENAMED and old becomes ADDED (clear SPLIT) - * 3. old was MODIFIED but FIND_RENAMES_FROM_REWRITES is on and - * old is more similar to new than it is to itself, in which - * case, new becomes RENAMED and old becomed ADDED - * 4. otherwise if over copy threshold, new becomes COPIED + similarity = (int)matches[i].similarity; + + /* + * Four possible outcomes here: */ + /* 1. DELETED "from" with match over rename threshold becomes + * RENAMED "from" record (and "to" record goes away) + */ if (from->status == GIT_DELTA_DELETED) { - if (to->similarity < opts.rename_threshold) { - to->similarity = 0; + if (similarity < (int)opts.rename_threshold) continue; - } - to->status = GIT_DELTA_RENAMED; - memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); + to->flags |= GIT_DIFF_FLAG__TO_DELETE; + + from->status = GIT_DELTA_RENAMED; + from->similarity = (uint32_t)similarity; + memcpy(&from->new_file, &to->new_file, sizeof(to->new_file)); + validate_delta(from); - from->flags |= GIT_DIFF_FLAG__TO_DELETE; num_rewrites++; - continue; } + /* 2. SPLIT MODIFIED "from" with match over rename threshold becomes + * ADDED "from" record (with no SPLIT) and RENAMED "to" record + */ if (from->status == GIT_DELTA_MODIFIED && - (from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) - { - if (to->similarity < opts.rename_threshold) { - to->similarity = 0; + (from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { + + if (similarity < (int)opts.rename_threshold) continue; - } to->status = GIT_DELTA_RENAMED; + to->similarity = (uint32_t)similarity; memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); + validate_delta(to); from->status = GIT_DELTA_ADDED; from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; + from->similarity = 0; /* reset self-similarity */ memset(&from->old_file, 0, sizeof(from->old_file)); - num_rewrites--; + from->old_file.path = from->new_file.path; + validate_delta(from); + num_rewrites--; + num_updates++; continue; } + /* 3. MODIFIED "from" with FIND_RENAMES_FROM_REWRITES with similar + * "to" and self-similarity below rename_from_rewrite_threshold + * becomes newly ADDED "from" and RENAMED "to". + */ if (from->status == GIT_DELTA_MODIFIED && FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) && - to->similarity > opts.rename_threshold) + similarity > (int)opts.rename_threshold) { - similarity = similarity_measure( + int self_similarity = similarity_measure( diff, &opts, cache, 2 * i, 2 * i + 1); - - if (similarity < 0) { - error = similarity; + if (self_similarity < 0) { + error = self_similarity; goto cleanup; } - if ((unsigned int)similarity < opts.rename_from_rewrite_threshold) { + if (self_similarity < (int)opts.rename_from_rewrite_threshold) { to->status = GIT_DELTA_RENAMED; + to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; /* ensure no split */ + to->similarity = (uint32_t)similarity; memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); + validate_delta(to); from->status = GIT_DELTA_ADDED; + from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; /* ensure no split */ + from->similarity = 0; memset(&from->old_file, 0, sizeof(from->old_file)); - from->old_file.path = to->old_file.path; + from->old_file.path = from->new_file.path; from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; + validate_delta(from); + num_updates++; continue; } } - if (to->similarity < opts.copy_threshold) { - to->similarity = 0; + /* 4. if "from" -> "to" over copy threshold, "to" becomes COPIED */ + if (similarity < (int)opts.copy_threshold) continue; - } /* convert "to" to a COPIED record */ to->status = GIT_DELTA_COPIED; + to->similarity = (uint32_t)similarity; memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); + validate_delta(to); + + validate_delta(from); + + num_updates++; } if (num_rewrites > 0) { assert(num_rewrites < diff->deltas.length); error = apply_splits_and_deletes( - diff, diff->deltas.length - num_rewrites); + diff, diff->deltas.length - num_rewrites, + FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES)); } + if (num_rewrites > 0 || num_updates > 0) + git_vector_sort(&diff->deltas); + cleanup: git__free(matches); diff --git a/src/util.h b/src/util.h index 6f876d012..5ae87ac10 100644 --- a/src/util.h +++ b/src/util.h @@ -109,6 +109,13 @@ GIT_INLINE(int) git__is_sizet(git_off_t p) return p == (git_off_t)r; } +/** @return true if p fits into the range of a uint32_t */ +GIT_INLINE(int) git__is_uint32(size_t p) +{ + uint32_t r = (uint32_t)p; + return p == (size_t)r; +} + /* 32-bit cross-platform rotl */ #ifdef _MSC_VER /* use built-in method in MSVC */ # define git__rotl(v, s) (uint32_t)_rotl(v, s) diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 1349d4013..01f65abfd 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -377,7 +377,8 @@ void test_diff_rename__handles_small_files(void) */ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); - opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES | GIT_DIFF_FIND_AND_BREAK_REWRITES; + opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES | + GIT_DIFF_FIND_AND_BREAK_REWRITES; cl_git_pass(git_diff_find_similar(diff, &opts)); git_diff_list_free(diff); From 5c8f37a397609eba92c6a906be467a7d7373e4f3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 17 May 2013 17:33:03 -0700 Subject: [PATCH 256/384] Extend diff example Add --raw output format and (some) options to invoke rename/copy detection on the diff. --- examples/diff.c | 50 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/examples/diff.c b/examples/diff.c index bb4f0ec21..11efa21ec 100644 --- a/examples/diff.c +++ b/examples/diff.c @@ -84,6 +84,10 @@ static int check_uint16_param(const char *arg, const char *pattern, uint16_t *va char *endptr = NULL; if (strncmp(arg, pattern, len)) return 0; + if (arg[len] == '\0' && pattern[len - 1] != '=') + return 1; + if (arg[len] == '=') + len++; strval = strtoul(arg + len, &endptr, 0); if (endptr == arg) return 0; @@ -110,13 +114,20 @@ static void usage(const char *message, const char *arg) exit(1); } +enum { + FORMAT_PATCH = 0, + FORMAT_COMPACT = 1, + FORMAT_RAW = 2 +}; + int main(int argc, char *argv[]) { git_repository *repo = NULL; git_tree *t1 = NULL, *t2 = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; git_diff_list *diff; - int i, color = -1, compact = 0, cached = 0; + int i, color = -1, format = FORMAT_PATCH, cached = 0; char *a, *treeish1 = NULL, *treeish2 = NULL; const char *dir = "."; @@ -137,11 +148,13 @@ int main(int argc, char *argv[]) } else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch")) - compact = 0; + format = FORMAT_PATCH; else if (!strcmp(a, "--cached")) cached = 1; else if (!strcmp(a, "--name-status")) - compact = 1; + format = FORMAT_COMPACT; + else if (!strcmp(a, "--raw")) + format = FORMAT_RAW; else if (!strcmp(a, "--color")) color = 0; else if (!strcmp(a, "--no-color")) @@ -160,6 +173,20 @@ int main(int argc, char *argv[]) opts.flags |= GIT_DIFF_INCLUDE_IGNORED; else if (!strcmp(a, "--untracked")) opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; + else if (check_uint16_param(a, "-M", &findopts.rename_threshold) || + check_uint16_param(a, "--find-renames", + &findopts.rename_threshold)) + findopts.flags |= GIT_DIFF_FIND_RENAMES; + else if (check_uint16_param(a, "-C", &findopts.copy_threshold) || + check_uint16_param(a, "--find-copies", + &findopts.copy_threshold)) + findopts.flags |= GIT_DIFF_FIND_COPIES; + else if (!strcmp(a, "--find-copies-harder")) + findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED; + else if (!strncmp(a, "-B", 2) || !strncmp(a, "--break-rewrites", 16)) { + /* TODO: parse thresholds */ + findopts.flags |= GIT_DIFF_FIND_REWRITES; + } else if (!check_uint16_param(a, "-U", &opts.context_lines) && !check_uint16_param(a, "--unified=", &opts.context_lines) && !check_uint16_param(a, "--inter-hunk-context=", @@ -204,13 +231,24 @@ int main(int argc, char *argv[]) else check(git_diff_index_to_workdir(&diff, repo, NULL, &opts), "Diff"); + if ((findopts.flags & GIT_DIFF_FIND_ALL) != 0) + check(git_diff_find_similar(diff, &findopts), + "finding renames and copies "); + if (color >= 0) fputs(colors[0], stdout); - if (compact) - check(git_diff_print_compact(diff, printer, &color), "Displaying diff"); - else + switch (format) { + case FORMAT_PATCH: check(git_diff_print_patch(diff, printer, &color), "Displaying diff"); + break; + case FORMAT_COMPACT: + check(git_diff_print_compact(diff, printer, &color), "Displaying diff"); + break; + case FORMAT_RAW: + check(git_diff_print_raw(diff, printer, &color), "Displaying diff"); + break; + } if (color >= 0) fputs(colors[0], stdout); From 038c1654d9bb17070272d0ba6f4489eada267bd0 Mon Sep 17 00:00:00 2001 From: Eitan Adler Date: Sun, 19 May 2013 01:41:00 -0400 Subject: [PATCH 257/384] Initialize loose_deleted before it is used for a check later on. --- src/refdb_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index f964c4182..7de56a1a0 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -976,7 +976,7 @@ static int refdb_fs_backend__delete( struct packref *pack_ref; khiter_t pack_ref_pos; int error = 0, pack_error; - bool loose_deleted; + bool loose_deleted = 0; assert(_backend); assert(ref); From 9be5be47fb1d9bc08e25b30c05dbf48739710062 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 20 May 2013 13:37:21 -0700 Subject: [PATCH 258/384] More git_diff_find_similar improvements - Add new GIT_DIFF_FIND_EXACT_MATCH_ONLY flag to do similarity matching without using the similarity metric (i.e. only compare the SHA). - Clean up the similarity measurement code to more rigorously distinguish between files that are not similar and files that are not comparable (previously, a 0 could either mean that the files could not be compared or that they were totally different) - When splitting a MODIFIED file into a DELETE/ADD pair, actually make a DELETED/UNTRACKED pair if the right side of the diff is from the working directory. This prevents an odd mix of ADDED and UNTRACKED files on workdir diffs. --- include/git2/diff.h | 2 + src/diff_tform.c | 169 +++++++++++++++++++++++++------------------- src/fileops.h | 1 + 3 files changed, 101 insertions(+), 71 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 172aa118b..31f6e0591 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -441,6 +441,8 @@ typedef enum { GIT_DIFF_FIND_IGNORE_WHITESPACE = (1 << 12), /** measure similarity including all data */ GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1 << 13), + /** measure similarity only by comparing SHAs (fast and cheap) */ + GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1 << 14), } git_diff_find_t; /** diff --git a/src/diff_tform.c b/src/diff_tform.c index 33268e403..d5e56ac60 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -255,6 +255,16 @@ static int normalize_find_opts( /* some flags imply others */ + if (opts->flags & GIT_DIFF_FIND_EXACT_MATCH_ONLY) { + /* if we are only looking for exact matches, then don't turn + * MODIFIED items into ADD/DELETE pairs because it's too picky + */ + opts->flags &= ~(GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES); + + /* similarly, don't look for self-rewrites to split */ + opts->flags &= ~GIT_DIFF_FIND_RENAMES_FROM_REWRITES; + } + if (opts->flags & GIT_DIFF_FIND_RENAMES_FROM_REWRITES) opts->flags |= GIT_DIFF_FIND_RENAMES; @@ -373,7 +383,10 @@ static int apply_splits_and_deletes( if (git_vector_insert(&onto, deleted) < 0) goto on_error; - delta->status = GIT_DELTA_ADDED; + if (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) + delta->status = GIT_DELTA_UNTRACKED; + else + delta->status = GIT_DELTA_ADDED; memset(&delta->old_file, 0, sizeof(delta->old_file)); delta->old_file.path = delta->new_file.path; delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; @@ -460,22 +473,56 @@ static int similarity_calc( return error; } +#define FLAG_SET(opts,flag_name) (((opts).flags & flag_name) != 0) + +/* - score < 0 means files cannot be compared + * - score >= 100 means files are exact match + * - score == 0 means files are completely different + */ static int similarity_measure( + int *score, git_diff_list *diff, git_diff_find_options *opts, void **cache, size_t a_idx, size_t b_idx) { - int score = 0; git_diff_file *a_file = similarity_get_file(diff, a_idx); git_diff_file *b_file = similarity_get_file(diff, b_idx); + bool exact_match = FLAG_SET(*opts, GIT_DIFF_FIND_EXACT_MATCH_ONLY); + *score = -1; + + /* don't try to compare files of different types */ if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode)) return 0; - if (git_oid__cmp(&a_file->oid, &b_file->oid) == 0) - return 100; + /* if exact match is requested, force calculation of missing OIDs */ + if (exact_match) { + if (git_oid_iszero(&a_file->oid) && + diff->old_src == GIT_ITERATOR_TYPE_WORKDIR && + !git_diff__oid_for_file(diff->repo, a_file->path, + a_file->mode, a_file->size, &a_file->oid)) + a_file->flags |= GIT_DIFF_FLAG_VALID_OID; + + if (git_oid_iszero(&b_file->oid) && + diff->new_src == GIT_ITERATOR_TYPE_WORKDIR && + !git_diff__oid_for_file(diff->repo, b_file->path, + b_file->mode, b_file->size, &b_file->oid)) + b_file->flags |= GIT_DIFF_FLAG_VALID_OID; + } + + /* check OID match as a quick test */ + if (git_oid__cmp(&a_file->oid, &b_file->oid) == 0) { + *score = 100; + return 0; + } + + /* don't calculate signatures if we are doing exact match */ + if (exact_match) { + *score = 0; + return 0; + } /* update signature cache if needed */ if (!cache[a_idx] && similarity_calc(diff, opts, a_idx, cache) < 0) @@ -488,20 +535,33 @@ static int similarity_measure( return 0; /* compare signatures */ - if (opts->metric->similarity( - &score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0) - return -1; - - /* clip score */ - if (score < 1) - score = 1; /* zero means uncomparable, so use 1 for least similar */ - else if (score > 100) - score = 100; - - return score; + return opts->metric->similarity( + score, cache[a_idx], cache[b_idx], opts->metric->payload); } -#define FLAG_SET(opts,flag_name) ((opts.flags & flag_name) != 0) +static void convert_to_rename_and_add( + git_diff_list *diff, + git_diff_delta *from, + git_diff_delta *to, + int similarity) +{ + to->status = GIT_DELTA_RENAMED; + to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; /* ensure no split */ + to->similarity = (uint32_t)similarity; + memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); + validate_delta(to); + + if (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) + from->status = GIT_DELTA_UNTRACKED; + else + from->status = GIT_DELTA_ADDED; + from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; /* ensure no split */ + from->similarity = 0; + memset(&from->old_file, 0, sizeof(from->old_file)); + from->old_file.path = from->new_file.path; + from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; + validate_delta(from); +} typedef struct { uint32_t idx; @@ -542,21 +602,17 @@ int git_diff_find_similar( continue; /* skip things that aren't plain blobs */ - if (GIT_MODE_TYPE(from->old_file.mode) != - GIT_MODE_TYPE(GIT_FILEMODE_BLOB)) + if (!GIT_MODE_ISBLOB(from->old_file.mode)) continue; /* measure similarity from old_file to new_file */ - similarity = similarity_measure( - diff, &opts, cache, 2 * i, 2 * i + 1); - - if (similarity < 0) { - error = similarity; + if ((error = similarity_measure( + &similarity, diff, &opts, cache, 2 * i, 2 * i + 1)) < 0) goto cleanup; - } - if (similarity > 0 && - similarity < (int)opts.break_rewrite_threshold) { + if (similarity < 0) + continue; + if (similarity < (int)opts.break_rewrite_threshold) { from->similarity = (uint32_t)similarity; from->flags |= GIT_DIFF_FLAG__TO_SPLIT; num_rewrites++; @@ -573,8 +629,7 @@ int git_diff_find_similar( matches[i].similarity = 0; /* skip things that aren't plain blobs */ - if (GIT_MODE_TYPE(from->old_file.mode) != - GIT_MODE_TYPE(GIT_FILEMODE_BLOB)) + if (!GIT_MODE_ISBLOB(from->old_file.mode)) continue; /* don't check UNMODIFIED files as source unless given option */ @@ -599,8 +654,7 @@ int git_diff_find_similar( continue; /* skip things that aren't blobs */ - if (GIT_MODE_TYPE(to->new_file.mode) != - GIT_MODE_TYPE(GIT_FILEMODE_BLOB)) + if (!GIT_MODE_ISBLOB(to->new_file.mode)) continue; /* only consider ADDED, RENAMED, COPIED, and split MODIFIED as @@ -630,14 +684,13 @@ int git_diff_find_similar( break; /* calculate similarity for this pair and find best match */ - similarity = similarity_measure( - diff, &opts, cache, 2 * i, 2 * j + 1); - - if (similarity < 0) { - error = similarity; + if ((error = similarity_measure( + &similarity, diff, &opts, cache, 2 * i, 2 * j + 1)) < 0) goto cleanup; + if (similarity < 0) { + --tried_targets; + continue; } - if (matches[i].similarity < (uint32_t)similarity) { matches[i].similarity = (uint32_t)similarity; matches[i].idx = j; @@ -687,18 +740,7 @@ int git_diff_find_similar( if (similarity < (int)opts.rename_threshold) continue; - to->status = GIT_DELTA_RENAMED; - to->similarity = (uint32_t)similarity; - memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); - validate_delta(to); - - from->status = GIT_DELTA_ADDED; - from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; - from->similarity = 0; /* reset self-similarity */ - memset(&from->old_file, 0, sizeof(from->old_file)); - from->old_file.path = from->new_file.path; - validate_delta(from); - + convert_to_rename_and_add(diff, from, to, similarity); num_rewrites--; num_updates++; continue; @@ -712,28 +754,16 @@ int git_diff_find_similar( FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) && similarity > (int)opts.rename_threshold) { - int self_similarity = similarity_measure( - diff, &opts, cache, 2 * i, 2 * i + 1); - if (self_similarity < 0) { - error = self_similarity; + int self_similarity; + + if ((error = similarity_measure(&self_similarity, + diff, &opts, cache, 2 * i, 2 * i + 1)) < 0) goto cleanup; - } - if (self_similarity < (int)opts.rename_from_rewrite_threshold) { - to->status = GIT_DELTA_RENAMED; - to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; /* ensure no split */ - to->similarity = (uint32_t)similarity; - memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); - validate_delta(to); - - from->status = GIT_DELTA_ADDED; - from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; /* ensure no split */ - from->similarity = 0; - memset(&from->old_file, 0, sizeof(from->old_file)); - from->old_file.path = from->new_file.path; - from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; - validate_delta(from); + if (self_similarity >= 0 && + self_similarity < (int)opts.rename_from_rewrite_threshold) { + convert_to_rename_and_add(diff, from, to, similarity); num_updates++; continue; } @@ -754,13 +784,10 @@ int git_diff_find_similar( num_updates++; } - if (num_rewrites > 0) { - assert(num_rewrites < diff->deltas.length); - + if (num_rewrites > 0) error = apply_splits_and_deletes( diff, diff->deltas.length - num_rewrites, FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES)); - } if (num_rewrites > 0 || num_updates > 0) git_vector_sort(&diff->deltas); diff --git a/src/fileops.h b/src/fileops.h index 627a6923d..3e214aab1 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -223,6 +223,7 @@ extern git_off_t git_futils_filesize(git_file fd); #define GIT_MODE_PERMS_MASK 0777 #define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644) #define GIT_MODE_TYPE(MODE) ((MODE) & ~GIT_MODE_PERMS_MASK) +#define GIT_MODE_ISBLOB(MODE) (GIT_MODE_TYPE(MODE) == GIT_MODE_TYPE(GIT_FILEMODE_BLOB)) /** * Convert a mode_t from the OS to a legal git mode_t value. From 4742148d54334629495eeaf0382e6c9da8786f17 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 20 May 2013 13:41:39 -0700 Subject: [PATCH 259/384] Add more diff rename detection tests This adds a bunch more rename detection tests including checks vs the working directory, the new exact match options, some more whitespace variants, etc. This also adds a git_futils_writebuffer helper function and uses it in checkout. This is mainly added because I wanted an easy way to write out a git_buf to disk inside my test code. --- src/checkout.c | 31 ++++---- src/fileops.c | 26 +++++++ src/fileops.h | 3 + tests-clar/diff/rename.c | 148 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 187 insertions(+), 21 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 5820f626a..c28fcdee0 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -676,33 +676,26 @@ static int buffer_to_file( int file_open_flags, mode_t file_mode) { - int fd, error; + int error; if ((error = git_futils_mkpath2file(path, dir_mode)) < 0) return error; - if ((fd = p_open(path, file_open_flags, file_mode)) < 0) { - giterr_set(GITERR_OS, "Could not open '%s' for writing", path); - return fd; + if ((error = git_futils_writebuffer( + buffer, path, file_open_flags, file_mode)) < 0) + return error; + + if (st != NULL && (error = p_stat(path, st)) < 0) { + giterr_set(GITERR_OS, "Error while statting '%s'", path); + return error; } - if ((error = p_write(fd, git_buf_cstr(buffer), git_buf_len(buffer))) < 0) { - giterr_set(GITERR_OS, "Could not write to '%s'", path); - (void)p_close(fd); - } else { - if ((error = p_close(fd)) < 0) - giterr_set(GITERR_OS, "Error while closing '%s'", path); - - if ((error = p_stat(path, st)) < 0) - giterr_set(GITERR_OS, "Error while statting '%s'", path); - } - - if (!error && - (file_mode & 0100) != 0 && - (error = p_chmod(path, file_mode)) < 0) + if ((file_mode & 0100) != 0 && (error = p_chmod(path, file_mode)) < 0) { giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path); + return error; + } - return error; + return 0; } static int blob_content_to_file( diff --git a/src/fileops.c b/src/fileops.c index 98ab8efe3..a3e43214f 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -202,6 +202,32 @@ int git_futils_readbuffer(git_buf *buf, const char *path) return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL); } +int git_futils_writebuffer( + const git_buf *buf, const char *path, int flags, mode_t mode) +{ + int fd, error = 0; + + if (flags <= 0) + flags = O_CREAT | O_TRUNC | O_WRONLY; + if (!mode) + mode = GIT_FILEMODE_BLOB; + + if ((fd = p_open(path, flags, mode)) < 0) { + giterr_set(GITERR_OS, "Could not open '%s' for writing", path); + return fd; + } + + if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) { + giterr_set(GITERR_OS, "Could not write to '%s'", path); + (void)p_close(fd); + } + + if ((error = p_close(fd)) < 0) + giterr_set(GITERR_OS, "Error while closing '%s'", path); + + return error; +} + int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode) { if (git_futils_mkpath2file(to, dirmode) < 0) diff --git a/src/fileops.h b/src/fileops.h index 3e214aab1..f4e059c83 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -22,6 +22,9 @@ extern int git_futils_readbuffer_updated( git_buf *obj, const char *path, time_t *mtime, size_t *size, int *updated); extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len); +extern int git_futils_writebuffer( + const git_buf *buf, const char *path, int open_flags, mode_t mode); + /** * File utils * diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 01f65abfd..a78e33939 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "diff_helpers.h" +#include "buf_text.h" static git_repository *g_repo = NULL; @@ -388,9 +389,152 @@ void test_diff_rename__handles_small_files(void) void test_diff_rename__working_directory_changes(void) { - /* let's rewrite some files in the working directory on demand */ + const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a"; + const char *blobsha = "66311f5cfbe7836c27510a3ba2f43e282e2c8bba"; + git_oid id; + git_tree *tree; + git_blob *blob; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + git_buf old_content = GIT_BUF_INIT, content = GIT_BUF_INIT;; - /* and with / without CRLF changes */ + tree = resolve_commit_oid_to_tree(g_repo, sha0); + diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED | GIT_DIFF_INCLUDE_UNTRACKED; + + /* + $ git cat-file -p 2bc7f351d20b53f1c72c16c4b036e491c478c49a^{tree} + + 100644 blob 66311f5cfbe7836c27510a3ba2f43e282e2c8bba sevencities.txt + 100644 blob ad0a8e55a104ac54a8a29ed4b84b49e76837a113 sixserving.txt + 100644 blob 66311f5cfbe7836c27510a3ba2f43e282e2c8bba songofseven.txt + + $ for f in *.txt; do + echo `git hash-object -t blob $f` $f + done + + eaf4a3e3bfe68585e90cada20736ace491cd100b ikeepsix.txt + f90d4fc20ecddf21eebe6a37e9225d244339d2b5 sixserving.txt + 4210ffd5c390b21dd5483375e75288dea9ede512 songof7cities.txt + 9a69d960ae94b060f56c2a8702545e2bb1abb935 untimely.txt + */ + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts)); + + /* git diff --no-renames 2bc7f351d20b53f1c72c16c4b036e491c478c49a */ + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(6, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]); + + /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a */ + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(5, exp.files); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + + /* rewrite files in the working directory with / without CRLF changes */ + + cl_git_pass( + git_futils_readbuffer(&old_content, "renames/songof7cities.txt")); + cl_git_pass( + git_buf_text_lf_to_crlf(&content, &old_content)); + cl_git_pass( + git_futils_writebuffer(&content, "renames/songof7cities.txt", 0, 0)); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts)); + + /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a */ + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(5, exp.files); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + + /* try a different whitespace option */ + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts)); + + opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(6, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + + /* try a different matching option */ + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts)); + + opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_EXACT_MATCH_ONLY; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(6, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]); + + git_diff_list_free(diff); + + /* again with exact match blob */ + + cl_git_pass(git_oid_fromstr(&id, blobsha)); + cl_git_pass(git_blob_lookup(&blob, g_repo, &id)); + cl_git_pass(git_buf_set( + &content, git_blob_rawcontent(blob), git_blob_rawsize(blob))); + cl_git_rewritefile("renames/songof7cities.txt", content.ptr); + git_blob_free(blob); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts)); + + opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_EXACT_MATCH_ONLY; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(5, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + + git_tree_free(tree); + git_buf_free(&content); + git_buf_free(&old_content); } void test_diff_rename__patch(void) From e069478edc1711e92c25eb26b82370f37775142b Mon Sep 17 00:00:00 2001 From: Li Xuanji Date: Tue, 21 May 2013 21:35:58 +0800 Subject: [PATCH 260/384] define "long name" in git_reference_name_to_id --- include/git2/refs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/git2/refs.h b/include/git2/refs.h index 754bda785..510000c37 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -48,7 +48,7 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, * * @param out Pointer to oid to be filled in * @param repo The repository in which to look up the reference - * @param name The long name for the reference + * @param name The long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...) * @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code. */ GIT_EXTERN(int) git_reference_name_to_id( From fc74343ff45187528e674712f7e858b95718edd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 21 May 2013 16:51:50 +0200 Subject: [PATCH 261/384] refs: export the glob iterator --- include/git2/refs.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/git2/refs.h b/include/git2/refs.h index 754bda785..fa9958369 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -353,6 +353,17 @@ GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2); */ GIT_EXTERN(int) git_reference_iterator_new(git_reference_iterator **out, git_repository *repo); +/** + * Create an iterator for the repo's references that match the + * specified glob + * + * @param out pointer in which to store the iterator + * @param repo the repository + * @param glob the glob to match against the reference names + * @return 0 or an error code + */ +GIT_EXTERN(int) git_reference_iterator_glob_new(git_reference_iterator **out, git_repository *repo, const char *glob); + /** * Get the next reference name * From b81cc1d63bb42dde993979cda49550d2c224d9ed Mon Sep 17 00:00:00 2001 From: nulltoken Date: Sat, 18 May 2013 16:52:16 +0200 Subject: [PATCH 262/384] tag: Introduce git_tag_annotation_create() --- include/git2/tag.h | 31 ++++++++++++++++++++++++++++ src/tag.c | 13 ++++++++++++ tests-clar/object/tag/write.c | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/include/git2/tag.h b/include/git2/tag.h index 469b1d72b..c822cee7c 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -177,6 +177,37 @@ GIT_EXTERN(int) git_tag_create( const char *message, int force); +/** + * Create a new tag in the object database pointing to a git_object + * + * The message will not be cleaned up. This can be achieved + * through `git_message_prettify()`. + * + * @param oid Pointer where to store the OID of the + * newly created tag + * + * @param repo Repository where to store the tag + * + * @param tag_name Name for the tag + * + * @param target Object to which this tag points. This object + * must belong to the given `repo`. + * + * @param tagger Signature of the tagger for this tag, and + * of the tagging time + * + * @param message Full message for this tag + * + * @return 0 on success or an error code + */ +GIT_EXTERN(int) git_tag_annotation_create( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message); + /** * Create a new tag in the repository from a buffer * diff --git a/src/tag.c b/src/tag.c index f81956de7..ecf3876cb 100644 --- a/src/tag.c +++ b/src/tag.c @@ -291,6 +291,19 @@ int git_tag_create( return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1); } +int git_tag_annotation_create( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message) +{ + assert(oid && repo && tag_name && target && tagger && message); + + return write_tag_annotation(oid, repo, tag_name, target, tagger, message); +} + int git_tag_create_lightweight( git_oid *oid, git_repository *repo, diff --git a/tests-clar/object/tag/write.c b/tests-clar/object/tag/write.c index cd69bea89..68e4b6c61 100644 --- a/tests-clar/object/tag/write.c +++ b/tests-clar/object/tag/write.c @@ -220,3 +220,41 @@ void test_object_tag_write__deleting_with_an_invalid_name_returns_EINVALIDSPEC(v { cl_assert_equal_i(GIT_EINVALIDSPEC, git_tag_delete(g_repo, "Inv@{id")); } + +void create_annotation(git_oid *tag_id, const char *name) +{ + git_object *target; + git_oid target_id; + git_signature *tagger; + + cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60)); + + git_oid_fromstr(&target_id, tagged_commit); + cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJ_COMMIT)); + + cl_git_pass(git_tag_annotation_create(tag_id, g_repo, name, target, tagger, "boom!")); + git_object_free(target); + git_signature_free(tagger); +} + +void test_object_tag_write__creating_an_annotation_stores_the_new_object_in_the_odb(void) +{ + git_oid tag_id; + git_tag *tag; + + create_annotation(&tag_id, "new_tag"); + + cl_git_pass(git_tag_lookup(&tag, g_repo, &tag_id)); + cl_assert_equal_s("new_tag", git_tag_name(tag)); + + git_tag_free(tag); +} + +void test_object_tag_write__creating_an_annotation_does_not_create_a_reference(void) +{ + git_oid tag_id; + git_reference *tag_ref; + + create_annotation(&tag_id, "new_tag"); + cl_git_fail_with(git_reference_lookup(&tag_ref, g_repo, "refs/tags/new_tag"), GIT_ENOTFOUND); +} From ccf1a2ba5fbd2a7d71be3e9bb895e3f2ac45e9b5 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Tue, 21 May 2013 11:37:13 +0200 Subject: [PATCH 263/384] cmake: Fix indentation --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3aa3770b8..0499dc90a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,8 +106,8 @@ ENDIF() # Specify sha1 implementation IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin") - ADD_DEFINITIONS(-DWIN32_SHA1) - FILE(GLOB SRC_SHA1 src/hash/hash_win32.c) + ADD_DEFINITIONS(-DWIN32_SHA1) + FILE(GLOB SRC_SHA1 src/hash/hash_win32.c) ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin") ADD_DEFINITIONS(-DOPENSSL_SHA1) ELSE() @@ -155,7 +155,7 @@ ENDIF() # Platform specific compilation flags IF (MSVC) - STRING(REPLACE "/Zm1000" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + STRING(REPLACE "/Zm1000" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") # /GF - String pooling # /MP - Parallel build @@ -170,7 +170,7 @@ IF (MSVC) SET(CRT_FLAG_DEBUG "/MTd") SET(CRT_FLAG_RELEASE "/MT") ELSE() - SET(CRT_FLAG_DEBUG "/MDd") + SET(CRT_FLAG_DEBUG "/MDd") SET(CRT_FLAG_RELEASE "/MD") ENDIF() From 095bfd748766966f5515bdfe64867d6a09287123 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Tue, 21 May 2013 11:38:24 +0200 Subject: [PATCH 264/384] cmake: Update Windows resources to reflect the optional vendor string Make InternalName and OriginalFilename resources reflect the name of the compiled binary. --- CMakeLists.txt | 11 +++++++++++ src/win32/{git2.rc => git2.rc.cmake} | 6 +----- 2 files changed, 12 insertions(+), 5 deletions(-) rename src/win32/{git2.rc => git2.rc.cmake} (91%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0499dc90a..016d77ad1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -321,8 +321,19 @@ IF (SONAME) SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME "git2-${SONAME_APPEND}") ENDIF() ENDIF() + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY) +IF (NOT BUILD_SHARED_LIBS) + SET(LIBGIT2_NAME_PREFIX "lib") +ENDIF() + +IF (SONAME_APPEND) + SET(LIBGIT2_NAME_SUFFIX "-${SONAME_APPEND}") +ENDIF() + +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/src/win32/git2.rc.cmake ${WIN_RC} @ONLY) + IF (MSVC_IDE) # Precompiled headers SET_TARGET_PROPERTIES(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h") diff --git a/src/win32/git2.rc b/src/win32/git2.rc.cmake similarity index 91% rename from src/win32/git2.rc rename to src/win32/git2.rc.cmake index 436913228..dc9b3e6eb 100644 --- a/src/win32/git2.rc +++ b/src/win32/git2.rc.cmake @@ -1,11 +1,7 @@ #include #include "../../include/git2/version.h" -#ifndef INCLUDE_LIB -#define LIBGIT2_FILENAME "git2.dll" -#else -#define LIBGIT2_FILENAME "libgit2.dll" -#endif +#define LIBGIT2_FILENAME "@LIBGIT2_NAME_PREFIX@git2@LIBGIT2_NAME_SUFFIX@.dll" VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE FILEVERSION LIBGIT2_VER_MAJOR,LIBGIT2_VER_MINOR,LIBGIT2_VER_REVISION,0 From 52f537e9c540fe5492ee6008153ab4db50c4091e Mon Sep 17 00:00:00 2001 From: Axel Wagner Date: Wed, 22 May 2013 02:04:12 +0200 Subject: [PATCH 265/384] Bugfix: Return NULL in push_leaf, when trie is full os->full was set 1, but the overflowed idx_leaf was still used to index into os->nodes a little later. Returning NULL fixes that. --- src/oid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/oid.c b/src/oid.c index e74640c57..0b023ddd0 100644 --- a/src/oid.c +++ b/src/oid.c @@ -269,8 +269,10 @@ static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, co idx_leaf = (node_index)os->node_count++; - if (os->node_count == SHRT_MAX) + if (os->node_count == SHRT_MAX) { os->full = 1; + return NULL; + } node = &os->nodes[idx]; node->children[push_at] = -idx_leaf; From a21cbb12db62426ca789045d5ac5c96ca069f0ea Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 22 May 2013 10:37:12 -0700 Subject: [PATCH 266/384] Significant rename detection rewrite This flips rename detection around so instead of creating a forward mapping from deltas to possible rename targets, instead it creates a reverse mapping, looking at possible targets and trying to find a source that they could have been renamed or copied from. This is important because each output can only have a single source, but a given source could map to multiple outputs (in the form of COPIED records). Additionally, this makes a couple of tweaks to the public rename detection APIs, mostly renaming a couple of options that control the behavior to make more sense and to be more like core Git. I walked through the tests looking at the exact results and updated the expectations based on what I saw. The new code is different from the old because it cannot give some nonsense results (like A was renamed to both B and C) which were part of the outputs previously. --- include/git2/diff.h | 15 +- src/diff.h | 10 +- src/diff_tform.c | 477 ++++++++++++++++++-------------- tests-clar/diff/rename.c | 34 ++- tests-clar/object/raw/convert.c | 1 - 5 files changed, 318 insertions(+), 219 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 31f6e0591..6939f6a2e 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -429,8 +429,8 @@ typedef enum { GIT_DIFF_FIND_AND_BREAK_REWRITES = (GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES), - /** consider untracked files as rename/copy targets */ - GIT_DIFF_FIND_FROM_UNTRACKED = (1 << 6), + /** find renames/copies for untracked items in working directory */ + GIT_DIFF_FIND_FOR_UNTRACKED = (1 << 6), /** turn on all finding features */ GIT_DIFF_FIND_ALL = (0x0ff), @@ -469,7 +469,10 @@ typedef struct { * - `copy_threshold` is the same as the -C option with a value * - `rename_from_rewrite_threshold` matches the top of the -B option * - `break_rewrite_threshold` matches the bottom of the -B option - * - `target_limit` matches the -l option (approximately) + * - `rename_limit` is the maximum number of matches to consider for + * a particular file. This is a little different from the `-l` option + * to regular Git because we will still process up to this many matches + * before abandoning the search. * * The `metric` option allows you to plug in a custom similarity metric. * Set it to NULL for the default internal metric which is based on sampling @@ -492,10 +495,10 @@ typedef struct { /** Similarity to split modify into delete/add pair (default 60) */ uint16_t break_rewrite_threshold; - /** Maximum similarity sources to examine (a la diff's `-l` option or - * the `diff.renameLimit` config) (default 200) + /** Maximum similarity sources to examine for a file (somewhat like + * git-diff's `-l` option or `diff.renameLimit` config) (default 200) */ - size_t target_limit; + size_t rename_limit; /** Pluggable similarity metric; pass NULL to use internal metric */ git_diff_similarity_metric *metric; diff --git a/src/diff.h b/src/diff.h index 16df431ed..a9a543ecd 100644 --- a/src/diff.h +++ b/src/diff.h @@ -34,10 +34,16 @@ enum { GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */ GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */ GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */ - GIT_DIFF_FLAG__TO_DELETE = (1 << 11), /* delete entry during rename det. */ - GIT_DIFF_FLAG__TO_SPLIT = (1 << 12), /* split entry during rename det. */ + + GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */ + GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */ + GIT_DIFF_FLAG__IS_RENAME_TARGET = (1 << 18), + GIT_DIFF_FLAG__IS_RENAME_SOURCE = (1 << 19), + GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20), }; +#define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF) + struct git_diff_list { git_refcount rc; git_repository *repo; diff --git a/src/diff_tform.c b/src/diff_tform.c index d5e56ac60..a3afe0d7a 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -222,7 +222,7 @@ int git_diff_find_similar__calc_similarity( #define DEFAULT_THRESHOLD 50 #define DEFAULT_BREAK_REWRITE_THRESHOLD 60 -#define DEFAULT_TARGET_LIMIT 200 +#define DEFAULT_RENAME_LIMIT 200 static int normalize_find_opts( git_diff_list *diff, @@ -290,15 +290,15 @@ static int normalize_find_opts( #undef USE_DEFAULT - if (!opts->target_limit) { + if (!opts->rename_limit) { int32_t limit = 0; - opts->target_limit = DEFAULT_TARGET_LIMIT; + opts->rename_limit = DEFAULT_RENAME_LIMIT; if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0) giterr_clear(); else if (limit > 0) - opts->target_limit = limit; + opts->rename_limit = limit; } /* assign the internal metric with whitespace flag as payload */ @@ -322,27 +322,6 @@ static int normalize_find_opts( return 0; } -static void validate_delta(git_diff_delta *delta) -{ - assert(delta); - return; -/* - switch (delta->status) { - case GIT_DELTA_ADDED: - case GIT_DELTA_UNTRACKED: - case GIT_DELTA_IGNORED: - assert(delta->new_file.path); - break; - case GIT_DELTA_DELETED: - assert(delta->old_file.path); - break; - default: - assert(delta->old_file.path && delta->new_file.path); - break; - } -*/ -} - static int apply_splits_and_deletes( git_diff_list *diff, size_t expected_size, bool actually_split) { @@ -358,16 +337,7 @@ static int apply_splits_and_deletes( if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0) continue; - if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { - - /* just leave delta flagged with score if not actually splitting */ - if (!actually_split) { - delta->flags = (delta->flags & ~GIT_DIFF_FLAG__TO_SPLIT); - if (delta->status != GIT_DELTA_MODIFIED) - delta->similarity = 0; - continue; - } - + if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0 && actually_split) { delta->similarity = 0; /* make new record for DELETED side of split */ @@ -378,7 +348,6 @@ static int apply_splits_and_deletes( memset(&deleted->new_file, 0, sizeof(deleted->new_file)); deleted->new_file.path = deleted->old_file.path; deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; - validate_delta(deleted); if (git_vector_insert(&onto, deleted) < 0) goto on_error; @@ -390,7 +359,6 @@ static int apply_splits_and_deletes( memset(&delta->old_file, 0, sizeof(delta->old_file)); delta->old_file.path = delta->new_file.path; delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; - validate_delta(delta); } if (git_vector_insert(&onto, delta) < 0) @@ -398,13 +366,22 @@ static int apply_splits_and_deletes( } /* cannot return an error past this point */ - git_vector_foreach(&diff->deltas, i, delta) + git_vector_foreach(&diff->deltas, i, delta) { if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0) git__free(delta); + GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags); + + if (delta->status != GIT_DELTA_COPIED && + delta->status != GIT_DELTA_RENAMED && + (delta->status != GIT_DELTA_MODIFIED || actually_split)) + delta->similarity = 0; + } + /* swap new delta list into place */ git_vector_swap(&diff->deltas, &onto); git_vector_free(&onto); + git_vector_sort(&diff->deltas); return 0; @@ -424,7 +401,7 @@ GIT_INLINE(git_diff_file *) similarity_get_file(git_diff_list *diff, size_t idx) static int similarity_calc( git_diff_list *diff, - git_diff_find_options *opts, + const git_diff_find_options *opts, size_t file_idx, void **cache) { @@ -473,7 +450,7 @@ static int similarity_calc( return error; } -#define FLAG_SET(opts,flag_name) (((opts).flags & flag_name) != 0) +#define FLAG_SET(opts,flag_name) (((opts)->flags & flag_name) != 0) /* - score < 0 means files cannot be compared * - score >= 100 means files are exact match @@ -482,14 +459,14 @@ static int similarity_calc( static int similarity_measure( int *score, git_diff_list *diff, - git_diff_find_options *opts, + const git_diff_find_options *opts, void **cache, size_t a_idx, size_t b_idx) { git_diff_file *a_file = similarity_get_file(diff, a_idx); git_diff_file *b_file = similarity_get_file(diff, b_idx); - bool exact_match = FLAG_SET(*opts, GIT_DIFF_FIND_EXACT_MATCH_ONLY); + bool exact_match = FLAG_SET(opts, GIT_DIFF_FIND_EXACT_MATCH_ONLY); *score = -1; @@ -539,28 +516,152 @@ static int similarity_measure( score, cache[a_idx], cache[b_idx], opts->metric->payload); } -static void convert_to_rename_and_add( +static int calc_self_similarity( git_diff_list *diff, - git_diff_delta *from, - git_diff_delta *to, - int similarity) + const git_diff_find_options *opts, + size_t delta_idx, + void **cache) { - to->status = GIT_DELTA_RENAMED; - to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; /* ensure no split */ - to->similarity = (uint32_t)similarity; - memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); - validate_delta(to); + int error, similarity = -1; + git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx); - if (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) - from->status = GIT_DELTA_UNTRACKED; - else - from->status = GIT_DELTA_ADDED; - from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; /* ensure no split */ - from->similarity = 0; - memset(&from->old_file, 0, sizeof(from->old_file)); - from->old_file.path = from->new_file.path; - from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; - validate_delta(from); + if ((delta->flags & GIT_DIFF_FLAG__HAS_SELF_SIMILARITY) != 0) + return 0; + + error = similarity_measure( + &similarity, diff, opts, cache, 2 * delta_idx, 2 * delta_idx + 1); + if (error < 0) + return error; + + if (similarity >= 0) { + delta->similarity = (uint32_t)similarity; + delta->flags |= GIT_DIFF_FLAG__HAS_SELF_SIMILARITY; + } + + return 0; +} + +static bool is_rename_target( + git_diff_list *diff, + const git_diff_find_options *opts, + size_t delta_idx, + void **cache) +{ + git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx); + + /* skip things that aren't plain blobs */ + if (!GIT_MODE_ISBLOB(delta->new_file.mode)) + return false; + + /* only consider ADDED, RENAMED, COPIED, and split MODIFIED as + * targets; maybe include UNTRACKED and IGNORED if requested. + */ + switch (delta->status) { + case GIT_DELTA_UNMODIFIED: + case GIT_DELTA_DELETED: + return false; + + case GIT_DELTA_MODIFIED: + if (!FLAG_SET(opts, GIT_DIFF_FIND_REWRITES) && + !FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES)) + return false; + + if (calc_self_similarity(diff, opts, delta_idx, cache) < 0) + return false; + + if (FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES) && + delta->similarity < opts->break_rewrite_threshold) { + delta->flags |= GIT_DIFF_FLAG__TO_SPLIT; + break; + } + if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) && + delta->similarity < opts->rename_from_rewrite_threshold) + break; + + return false; + + case GIT_DELTA_UNTRACKED: + case GIT_DELTA_IGNORED: + if (!FLAG_SET(opts, GIT_DIFF_FIND_FOR_UNTRACKED)) + return false; + break; + + default: /* all other status values should be checked */ + break; + } + + delta->flags |= GIT_DIFF_FLAG__IS_RENAME_TARGET; + return true; +} + +static bool is_rename_source( + git_diff_list *diff, + const git_diff_find_options *opts, + size_t delta_idx, + void **cache) +{ + git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx); + + /* skip things that aren't blobs */ + if (!GIT_MODE_ISBLOB(delta->old_file.mode)) + return false; + + switch (delta->status) { + case GIT_DELTA_ADDED: + case GIT_DELTA_UNTRACKED: + case GIT_DELTA_IGNORED: + return false; + + case GIT_DELTA_DELETED: + case GIT_DELTA_TYPECHANGE: + break; + + case GIT_DELTA_UNMODIFIED: + if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)) + return false; + break; + + default: /* MODIFIED, RENAMED, COPIED */ + /* if we're finding copies, this could be a source */ + if (FLAG_SET(opts, GIT_DIFF_FIND_COPIES)) + break; + + /* otherwise, this is only a source if we can split it */ + if (!FLAG_SET(opts, GIT_DIFF_FIND_REWRITES) && + !FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES)) + return false; + + if (calc_self_similarity(diff, opts, delta_idx, cache) < 0) + return false; + + if (FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES) && + delta->similarity < opts->break_rewrite_threshold) { + delta->flags |= GIT_DIFF_FLAG__TO_SPLIT; + break; + } + + if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) && + delta->similarity < opts->rename_from_rewrite_threshold) + break; + + return false; + } + + delta->flags |= GIT_DIFF_FLAG__IS_RENAME_SOURCE; + return true; +} + +GIT_INLINE(bool) delta_is_split(git_diff_delta *delta) +{ + return (delta->status == GIT_DELTA_TYPECHANGE || + (delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0); +} + +GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta) +{ + return (delta->status == GIT_DELTA_ADDED || + delta->status == GIT_DELTA_UNTRACKED || + delta->status == GIT_DELTA_IGNORED); } typedef struct { @@ -583,7 +684,7 @@ int git_diff_find_similar( if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0) return error; - /* TODO: maybe abort if deltas.length > target_limit ??? */ + /* TODO: maybe abort if deltas.length > rename_limit ??? */ if (!git__is_uint32(diff->deltas.length)) return 0; @@ -594,103 +695,40 @@ int git_diff_find_similar( matches = git__calloc(diff->deltas.length, sizeof(diff_find_match)); GITERR_CHECK_ALLOC(matches); - /* first mark MODIFIED deltas to split if too different (if requested) */ - - if (FLAG_SET(opts, GIT_DIFF_FIND_REWRITES)) { - git_vector_foreach(&diff->deltas, i, from) { - if (from->status != GIT_DELTA_MODIFIED) - continue; - - /* skip things that aren't plain blobs */ - if (!GIT_MODE_ISBLOB(from->old_file.mode)) - continue; - - /* measure similarity from old_file to new_file */ - if ((error = similarity_measure( - &similarity, diff, &opts, cache, 2 * i, 2 * i + 1)) < 0) - goto cleanup; - - if (similarity < 0) - continue; - if (similarity < (int)opts.break_rewrite_threshold) { - from->similarity = (uint32_t)similarity; - from->flags |= GIT_DIFF_FLAG__TO_SPLIT; - num_rewrites++; - } - } - } - /* next find the most similar delta for each rename / copy candidate */ - git_vector_foreach(&diff->deltas, i, from) { - size_t tried_targets = 0; + git_vector_foreach(&diff->deltas, i, to) { + size_t tried_sources = 0; matches[i].idx = i; matches[i].similarity = 0; - /* skip things that aren't plain blobs */ - if (!GIT_MODE_ISBLOB(from->old_file.mode)) + /* skip things that are not rename targets */ + if (!is_rename_target(diff, &opts, i, cache)) continue; - /* don't check UNMODIFIED files as source unless given option */ - if (from->status == GIT_DELTA_UNMODIFIED && - !FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)) - continue; - - /* don't check UNTRACKED files as source unless given option */ - if ((from->status == GIT_DELTA_UNTRACKED || - from->status == GIT_DELTA_IGNORED) && - !FLAG_SET(opts, GIT_DIFF_FIND_FROM_UNTRACKED)) - continue; - - /* only use DELETED (or split MODIFIED) unless copy detection on */ - if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES) && - from->status != GIT_DELTA_DELETED && - (from->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0) - continue; - - git_vector_foreach(&diff->deltas, j, to) { + git_vector_foreach(&diff->deltas, j, from) { if (i == j) continue; - /* skip things that aren't blobs */ - if (!GIT_MODE_ISBLOB(to->new_file.mode)) + /* skip things that are not rename sources */ + if (!is_rename_source(diff, &opts, j, cache)) continue; - /* only consider ADDED, RENAMED, COPIED, and split MODIFIED as - * targets; maybe include UNTRACKED and IGNORED if requested. - */ - switch (to->status) { - case GIT_DELTA_ADDED: - case GIT_DELTA_RENAMED: - case GIT_DELTA_COPIED: - break; - case GIT_DELTA_MODIFIED: - if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0) - continue; - break; - case GIT_DELTA_UNTRACKED: - case GIT_DELTA_IGNORED: - if (!FLAG_SET(opts, GIT_DIFF_FIND_FROM_UNTRACKED)) - continue; - break; - default: - /* all other status values will be skipped */ - continue; - } - - /* cap on maximum targets we'll examine (per "from" file) */ - if (++tried_targets > opts.target_limit) + /* cap on maximum targets we'll examine (per "to" file) */ + if (++tried_sources > opts.rename_limit) break; /* calculate similarity for this pair and find best match */ if ((error = similarity_measure( - &similarity, diff, &opts, cache, 2 * i, 2 * j + 1)) < 0) + &similarity, diff, &opts, cache, 2 * j, 2 * i + 1)) < 0) goto cleanup; - if (similarity < 0) { - --tried_targets; + + if (similarity < 0) { /* not actually comparable */ + --tried_sources; continue; } + if (matches[i].similarity < (uint32_t)similarity) { matches[i].similarity = (uint32_t)similarity; matches[i].idx = j; @@ -700,97 +738,128 @@ int git_diff_find_similar( /* next rewrite the diffs with renames / copies */ - git_vector_foreach(&diff->deltas, i, from) { - if (!matches[i].similarity) + git_vector_foreach(&diff->deltas, i, to) { + + /* check if this delta was matched to another one */ + if ((similarity = (int)matches[i].similarity) <= 0) continue; + assert(to && (to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) != 0); - to = GIT_VECTOR_GET(&diff->deltas, matches[i].idx); - assert(to); + from = GIT_VECTOR_GET(&diff->deltas, matches[i].idx); + assert(from && (from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) != 0); - similarity = (int)matches[i].similarity; - - /* - * Four possible outcomes here: + /* possible scenarios: + * 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME + * 2. from DELETE to SPLIT/TYPECHANGE = RENAME + DELETE + * 3. from SPLIT/TYPECHANGE to ADD/UNTRACK/IGNORE = ADD + RENAME + * 4. from SPLIT/TYPECHANGE to SPLIT/TYPECHANGE = RENAME + SPLIT + * 5. from OTHER to ADD/UNTRACK/IGNORE = OTHER + COPY */ - /* 1. DELETED "from" with match over rename threshold becomes - * RENAMED "from" record (and "to" record goes away) - */ if (from->status == GIT_DELTA_DELETED) { - if (similarity < (int)opts.rename_threshold) - continue; - to->flags |= GIT_DIFF_FLAG__TO_DELETE; + if (delta_is_new_only(to)) { - from->status = GIT_DELTA_RENAMED; - from->similarity = (uint32_t)similarity; - memcpy(&from->new_file, &to->new_file, sizeof(to->new_file)); - validate_delta(from); + if (similarity < (int)opts.rename_threshold) + continue; - num_rewrites++; - continue; - } + from->status = GIT_DELTA_RENAMED; + from->similarity = (uint32_t)similarity; + memcpy(&from->new_file, &to->new_file, sizeof(from->new_file)); - /* 2. SPLIT MODIFIED "from" with match over rename threshold becomes - * ADDED "from" record (with no SPLIT) and RENAMED "to" record - */ - if (from->status == GIT_DELTA_MODIFIED && - (from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { + to->flags |= GIT_DIFF_FLAG__TO_DELETE; - if (similarity < (int)opts.rename_threshold) - continue; + num_rewrites++; + } else { + assert(delta_is_split(from)); - convert_to_rename_and_add(diff, from, to, similarity); - num_rewrites--; - num_updates++; - continue; - } + if (similarity < (int)opts.rename_from_rewrite_threshold) + continue; - /* 3. MODIFIED "from" with FIND_RENAMES_FROM_REWRITES with similar - * "to" and self-similarity below rename_from_rewrite_threshold - * becomes newly ADDED "from" and RENAMED "to". - */ - if (from->status == GIT_DELTA_MODIFIED && - FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) && - similarity > (int)opts.rename_threshold) - { - int self_similarity; + from->status = GIT_DELTA_RENAMED; + from->similarity = (uint32_t)similarity; + memcpy(&from->new_file, &to->new_file, sizeof(from->new_file)); - if ((error = similarity_measure(&self_similarity, - diff, &opts, cache, 2 * i, 2 * i + 1)) < 0) - goto cleanup; + to->status = GIT_DELTA_DELETED; + memset(&to->new_file, 0, sizeof(to->new_file)); + to->new_file.path = to->old_file.path; + to->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; + if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { + to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; + num_rewrites--; + } - if (self_similarity >= 0 && - self_similarity < (int)opts.rename_from_rewrite_threshold) { - - convert_to_rename_and_add(diff, from, to, similarity); num_updates++; - continue; } } - /* 4. if "from" -> "to" over copy threshold, "to" becomes COPIED */ - if (similarity < (int)opts.copy_threshold) - continue; + else if (delta_is_split(from)) { + git_diff_file swap; - /* convert "to" to a COPIED record */ - to->status = GIT_DELTA_COPIED; - to->similarity = (uint32_t)similarity; - memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); - validate_delta(to); + if (delta_is_new_only(to)) { - validate_delta(from); + if (similarity < (int)opts.rename_threshold) + continue; - num_updates++; + memcpy(&swap, &from->new_file, sizeof(swap)); + + from->status = GIT_DELTA_RENAMED; + from->similarity = (uint32_t)similarity; + memcpy(&from->new_file, &to->new_file, sizeof(from->new_file)); + if ((from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { + from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; + num_rewrites--; + } + + to->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ? + GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED; + memcpy(&to->new_file, &swap, sizeof(to->new_file)); + to->old_file.path = to->new_file.path; + + num_updates++; + } else { + assert(delta_is_split(from)); + + if (similarity < (int)opts.rename_from_rewrite_threshold) + continue; + + memcpy(&swap, &from->new_file, sizeof(swap)); + + from->status = GIT_DELTA_RENAMED; + from->similarity = (uint32_t)similarity; + memcpy(&from->new_file, &to->new_file, sizeof(from->new_file)); + if ((from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { + from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; + num_rewrites--; + } + + memcpy(&to->new_file, &swap, sizeof(to->new_file)); + if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0) { + to->flags |= GIT_DIFF_FLAG__TO_SPLIT; + num_rewrites++; + } + + num_updates++; + } + } + + else if (delta_is_new_only(to)) { + if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES) || + similarity < (int)opts.copy_threshold) + continue; + + to->status = GIT_DELTA_COPIED; + to->similarity = (uint32_t)similarity; + memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); + + num_updates++; + } } - if (num_rewrites > 0) + if (num_rewrites > 0 || num_updates > 0) error = apply_splits_and_deletes( diff, diff->deltas.length - num_rewrites, - FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES)); - - if (num_rewrites > 0 || num_updates > 0) - git_vector_sort(&diff->deltas); + FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES)); cleanup: git__free(matches); diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index a78e33939..b4f9df713 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -14,6 +14,18 @@ void test_diff_rename__cleanup(void) cl_git_sandbox_cleanup(); } +/* +static int debug_print( + const git_diff_delta *delta, const git_diff_range *range, char usage, + const char *line, size_t line_len, void *data) +{ + GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(usage); + GIT_UNUSED(line_len); GIT_UNUSED(data); + fputs(line, stderr); + return 0; +} +*/ + /* * Renames repo has: * @@ -72,8 +84,10 @@ void test_diff_rename__match_oid(void) /* git diff 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a + * don't use NULL opts to avoid config `diff.renames` contamination */ - cl_git_pass(git_diff_find_similar(diff, NULL)); + opts.flags = GIT_DIFF_FIND_RENAMES; + cl_git_pass(git_diff_find_similar(diff, &opts)); memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( @@ -243,8 +257,8 @@ void test_diff_rename__not_exact_match(void) cl_assert_equal_i(5, exp.files); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]); git_diff_list_free(diff); @@ -429,8 +443,8 @@ void test_diff_rename__working_directory_changes(void) cl_assert_equal_i(6, exp.files); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]); /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a */ opts.flags = GIT_DIFF_FIND_ALL; @@ -441,7 +455,8 @@ void test_diff_rename__working_directory_changes(void) diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(5, exp.files); - cl_assert_equal_i(3, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]); git_diff_list_free(diff); @@ -466,7 +481,8 @@ void test_diff_rename__working_directory_changes(void) diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(5, exp.files); - cl_assert_equal_i(3, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]); git_diff_list_free(diff); @@ -521,13 +537,19 @@ void test_diff_rename__working_directory_changes(void) opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_EXACT_MATCH_ONLY; cl_git_pass(git_diff_find_similar(diff, &opts)); + /* + fprintf(stderr, "\n\n"); + cl_git_pass(git_diff_print_raw(diff, debug_print, NULL)); + */ + memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(5, exp.files); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]); git_diff_list_free(diff); diff --git a/tests-clar/object/raw/convert.c b/tests-clar/object/raw/convert.c index 86f0d74a9..88b1380a4 100644 --- a/tests-clar/object/raw/convert.c +++ b/tests-clar/object/raw/convert.c @@ -87,7 +87,6 @@ void test_object_raw_convert__convert_oid_partially(void) const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0"; git_oid in; char big[GIT_OID_HEXSZ + 1 + 3]; /* note + 4 => big buffer */ - char *str; cl_git_pass(git_oid_fromstr(&in, exp)); From 06c070b5bb213e76bff15b881ee77c0f179cdbd1 Mon Sep 17 00:00:00 2001 From: Axel Wagner Date: Thu, 23 May 2013 09:43:56 +0200 Subject: [PATCH 267/384] Add testcase for #1600 --- tests-clar/object/raw/short.c | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests-clar/object/raw/short.c b/tests-clar/object/raw/short.c index 93c79b6a5..b4019936a 100644 --- a/tests-clar/object/raw/short.c +++ b/tests-clar/object/raw/short.c @@ -92,3 +92,42 @@ void test_object_raw_short__oid_shortener_stresstest_git_oid_shorten(void) #undef MAX_OIDS } + +void test_object_raw_short__oid_shortener_too_much_oids(void) { + /* The magic number of oids at which an oid_shortener will fail. + * This was experimentally established. */ +#define MAX_OIDS 24556 + + git_oid_shorten *os; + char number_buffer[16]; + git_oid oid; + size_t i; + + int min_len = 0; + + os = git_oid_shorten_new(0); + cl_assert(os != NULL); + + for (i = 0; i < MAX_OIDS; ++i) { + char *oid_text; + + p_snprintf(number_buffer, 16, "%u", (unsigned int)i); + git_hash_buf(&oid, number_buffer, strlen(number_buffer)); + + oid_text = git__malloc(GIT_OID_HEXSZ + 1); + git_oid_fmt(oid_text, &oid); + oid_text[GIT_OID_HEXSZ] = 0; + + min_len = git_oid_shorten_add(os, oid_text); + /* All but the last oid should give a non-negative min_len. At the + * last oid, git_oid_shorten_add should fail, returning a negative + * value */ + if (i < MAX_OIDS - 1) + cl_assert(min_len >= 0); + else + cl_assert(min_len < 0); + } + + git_oid_shorten_free(os); + +} From c68b09dc7ab782eeec9a9c59d66d8be31aed67f1 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 23 May 2013 11:52:34 -0700 Subject: [PATCH 268/384] Fix dereference of freed delta I was accidentally using a value that I had just freed. This moves the clearing of the delta internal flags into a better place. --- src/diff_tform.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/diff_tform.c b/src/diff_tform.c index a3afe0d7a..b481e64ce 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -18,6 +18,7 @@ static git_diff_delta *diff_delta__dup( return NULL; memcpy(delta, d, sizeof(git_diff_delta)); + GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags); if (d->old_file.path != NULL) { delta->old_file.path = git_pool_strdup(pool, d->old_file.path); @@ -361,21 +362,25 @@ static int apply_splits_and_deletes( delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; } - if (git_vector_insert(&onto, delta) < 0) - goto on_error; - } - - /* cannot return an error past this point */ - git_vector_foreach(&diff->deltas, i, delta) { - if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0) - git__free(delta); - + /* clean up delta before inserting into new list */ GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags); if (delta->status != GIT_DELTA_COPIED && delta->status != GIT_DELTA_RENAMED && (delta->status != GIT_DELTA_MODIFIED || actually_split)) delta->similarity = 0; + + /* insert into new list */ + if (git_vector_insert(&onto, delta) < 0) + goto on_error; + } + + /* cannot return an error past this point */ + + /* free deltas from old list that didn't make it to the new one */ + git_vector_foreach(&diff->deltas, i, delta) { + if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0) + git__free(delta); } /* swap new delta list into place */ From 67db583dabf6e154037302a24b7f79028f403454 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 23 May 2013 15:06:07 -0700 Subject: [PATCH 269/384] More diff rename tests; better split swap handling This adds a couple more tests of different rename scenarios. Also, this fixes a problem with the case where you have two "split" deltas and the left half of one matches the right half of the other. That case was already being handled, but in the wrong order in a way that could result in bad output. Also, if the swap also happened to put the other two halves into the correct place (i.e. two files exchanged places with each other), then the second delta was left with the SPLIT flag set when it really should be cleared. --- include/git2/diff.h | 2 +- src/diff.h | 2 + src/diff_tform.c | 33 ++++++--- tests-clar/diff/diff_helpers.c | 5 ++ tests-clar/diff/diff_helpers.h | 2 +- tests-clar/diff/rename.c | 119 +++++++++++++++++++++++++++++---- 6 files changed, 139 insertions(+), 24 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 6939f6a2e..0d4875b43 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -484,7 +484,7 @@ typedef struct { unsigned int version; /** Combination of git_diff_find_t values (default FIND_RENAMES) */ - unsigned int flags; + uint32_t flags; /** Similarity to consider a file renamed (default 50) */ uint16_t rename_threshold; diff --git a/src/diff.h b/src/diff.h index a9a543ecd..ac8ab2aed 100644 --- a/src/diff.h +++ b/src/diff.h @@ -44,6 +44,8 @@ enum { #define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF) +#define GIT_DIFF__VERBOSE (1 << 30) + struct git_diff_list { git_refcount rc; git_repository *repo; diff --git a/src/diff_tform.c b/src/diff_tform.c index b481e64ce..0f4ecc7b5 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -828,22 +828,37 @@ int git_diff_find_similar( if (similarity < (int)opts.rename_from_rewrite_threshold) continue; - memcpy(&swap, &from->new_file, sizeof(swap)); + memcpy(&swap, &to->new_file, sizeof(swap)); - from->status = GIT_DELTA_RENAMED; - from->similarity = (uint32_t)similarity; - memcpy(&from->new_file, &to->new_file, sizeof(from->new_file)); - if ((from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { - from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; + to->status = GIT_DELTA_RENAMED; + to->similarity = (uint32_t)similarity; + memcpy(&to->new_file, &from->new_file, sizeof(to->new_file)); + if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { + to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; num_rewrites--; } - memcpy(&to->new_file, &swap, sizeof(to->new_file)); - if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0) { - to->flags |= GIT_DIFF_FLAG__TO_SPLIT; + memcpy(&from->new_file, &swap, sizeof(from->new_file)); + if ((from->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0) { + from->flags |= GIT_DIFF_FLAG__TO_SPLIT; num_rewrites++; } + /* in the off chance that we've just swapped the new + * element into the correct place, clear the SPLIT flag + */ + if (matches[matches[i].idx].idx == i && + matches[matches[i].idx].similarity > + opts.rename_from_rewrite_threshold) { + + from->status = GIT_DELTA_RENAMED; + from->similarity = + (uint32_t)matches[matches[i].idx].similarity; + matches[matches[i].idx].similarity = 0; + from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; + num_rewrites--; + } + num_updates++; } } diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c index 75eda0520..4e23792a6 100644 --- a/tests-clar/diff/diff_helpers.c +++ b/tests-clar/diff/diff_helpers.c @@ -213,3 +213,8 @@ void diff_print(FILE *fp, git_diff_list *diff) { cl_git_pass(git_diff_print_patch(diff, diff_print_cb, fp ? fp : stderr)); } + +void diff_print_raw(FILE *fp, git_diff_list *diff) +{ + cl_git_pass(git_diff_print_raw(diff, diff_print_cb, fp ? fp : stderr)); +} diff --git a/tests-clar/diff/diff_helpers.h b/tests-clar/diff/diff_helpers.h index b39a69d1d..bb76d0076 100644 --- a/tests-clar/diff/diff_helpers.h +++ b/tests-clar/diff/diff_helpers.h @@ -65,4 +65,4 @@ extern int diff_foreach_via_iterator( void *data); extern void diff_print(FILE *fp, git_diff_list *diff); - +extern void diff_print_raw(FILE *fp, git_diff_list *diff); diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index b4f9df713..1edf66e95 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -14,18 +14,6 @@ void test_diff_rename__cleanup(void) cl_git_sandbox_cleanup(); } -/* -static int debug_print( - const git_diff_delta *delta, const git_diff_range *range, char usage, - const char *line, size_t line_len, void *data) -{ - GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(usage); - GIT_UNUSED(line_len); GIT_UNUSED(data); - fputs(line, stderr); - return 0; -} -*/ - /* * Renames repo has: * @@ -539,7 +527,7 @@ void test_diff_rename__working_directory_changes(void) /* fprintf(stderr, "\n\n"); - cl_git_pass(git_diff_print_raw(diff, debug_print, NULL)); + diff_print_raw(stderr, diff); */ memset(&exp, 0, sizeof(exp)); @@ -613,3 +601,108 @@ void test_diff_rename__patch(void) git_tree_free(old_tree); git_tree_free(new_tree); } + +void test_diff_rename__file_exchange(void) +{ + git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT; + git_index *index; + git_tree *tree; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + + cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt")); + cl_git_pass(git_futils_readbuffer(&c2, "renames/songof7cities.txt")); + cl_git_pass(git_futils_writebuffer(&c1, "renames/songof7cities.txt", 0, 0)); + cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_read_tree(index, tree)); + cl_git_pass(git_index_add_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_add_bypath(index, "untimely.txt")); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]); + + git_diff_list_free(diff); + git_tree_free(tree); + git_index_free(index); + + git_buf_free(&c1); + git_buf_free(&c2); +} + +void test_diff_rename__file_split(void) +{ + git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT; + git_index *index; + git_tree *tree; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + + /* put the first 2/3 of file into one new place + * and the second 2/3 of file into another new place + */ + cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt")); + cl_git_pass(git_buf_set(&c2, c1.ptr, c1.size)); + git_buf_truncate(&c1, c1.size * 2 / 3); + git_buf_consume(&c2, ((char *)c2.ptr) + (c2.size / 3)); + cl_git_pass(git_futils_writebuffer(&c1, "renames/song_a.txt", 0, 0)); + cl_git_pass(git_futils_writebuffer(&c2, "renames/song_b.txt", 0, 0)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_read_tree(index, tree)); + cl_git_pass(git_index_add_bypath(index, "song_a.txt")); + cl_git_pass(git_index_add_bypath(index, "song_b.txt")); + + diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED; + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(6, exp.files); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNMODIFIED]); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(6, exp.files); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_COPIED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNMODIFIED]); + + git_diff_list_free(diff); + git_tree_free(tree); + git_index_free(index); + + git_buf_free(&c1); + git_buf_free(&c2); +} From f38cea97c3793dd6841fadbd1b9d26d25445375b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 23 May 2013 15:21:43 -0700 Subject: [PATCH 270/384] Move core.abbrev lookup out of diff print loop This moves the GIT_CVAR_ABBREV lookup out of the loop. Also, this fixes git_diff_print_raw to actually use that constant instead of hardcoding 7 characters. --- src/diff_output.c | 91 +++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/src/diff_output.c b/src/diff_output.c index 4ea9e4baf..8dd110cbf 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -236,9 +236,8 @@ static int get_blob_content( char oidstr[GIT_OID_HEXSZ+1]; git_buf content = GIT_BUF_INIT; - git_oid_fmt(oidstr, &file->oid); - oidstr[GIT_OID_HEXSZ] = 0; - git_buf_printf(&content, "Subproject commit %s\n", oidstr ); + git_oid_tostr(oidstr, sizeof(oidstr), &file->oid); + git_buf_printf(&content, "Subproject commit %s\n", oidstr); map->data = git_buf_detach(&content); map->len = strlen(map->data); @@ -318,14 +317,13 @@ static int get_workdir_sm_content( } } - git_oid_fmt(oidstr, &file->oid); - oidstr[GIT_OID_HEXSZ] = '\0'; + git_oid_tostr(oidstr, sizeof(oidstr), &file->oid); if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) sm_status_text = "-dirty"; - git_buf_printf(&content, "Subproject commit %s%s\n", - oidstr, sm_status_text); + git_buf_printf( + &content, "Subproject commit %s%s\n", oidstr, sm_status_text); map->data = git_buf_detach(&content); map->len = strlen(map->data); @@ -1021,8 +1019,33 @@ typedef struct { git_diff_data_cb print_cb; void *payload; git_buf *buf; + int oid_strlen; } diff_print_info; +static int diff_print_info_init( + diff_print_info *pi, + git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload) +{ + assert(diff && diff->repo); + + pi->diff = diff; + pi->print_cb = cb; + pi->payload = payload; + pi->buf = out; + + if (git_repository__cvar(&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0) + return -1; + + pi->oid_strlen += 1; /* for NUL byte */ + + if (pi->oid_strlen < 2) + pi->oid_strlen = 2; + else if (pi->oid_strlen > GIT_OID_HEXSZ + 1) + pi->oid_strlen = GIT_OID_HEXSZ + 1; + + return 0; +} + static char pick_suffix(int mode) { if (S_ISDIR(mode)) @@ -1104,9 +1127,10 @@ int git_diff_print_compact( { int error; git_buf buf = GIT_BUF_INIT; - diff_print_info pi = { diff, print_cb, payload, &buf }; + diff_print_info pi; - error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi); + if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) + error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi); git_buf_free(&buf); @@ -1118,7 +1142,7 @@ static int print_raw( { diff_print_info *pi = data; char code = git_diff_status_char(delta->status); - char ooid[8], noid[8]; + char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; GIT_UNUSED(progress); @@ -1127,15 +1151,12 @@ static int print_raw( git_buf_clear(pi->buf); - git_oid_nfmt(ooid, sizeof(ooid), &delta->old_file.oid); - ooid[7] = '\0'; - - git_oid_nfmt(noid, sizeof(noid), &delta->new_file.oid); - noid[7] = '\0'; + git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid); + git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid); git_buf_printf( pi->buf, ":%06o %06o %s... %s... %c", - delta->old_file.mode, delta->new_file.mode, ooid, noid, code); + delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); if (delta->similarity > 0) git_buf_printf(pi->buf, "%03u", delta->similarity); @@ -1165,9 +1186,10 @@ int git_diff_print_raw( { int error; git_buf buf = GIT_BUF_INIT; - diff_print_info pi = { diff, print_cb, payload, &buf }; + diff_print_info pi; - error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi); + if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) + error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi); git_buf_free(&buf); @@ -1176,20 +1198,10 @@ int git_diff_print_raw( static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta) { - int abbrevlen; char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; - if (git_repository__cvar(&abbrevlen, pi->diff->repo, GIT_CVAR_ABBREV) < 0) - return -1; - - abbrevlen += 1; /* for NUL byte */ - if (abbrevlen < 2) - abbrevlen = 2; - else if (abbrevlen > (int)sizeof(start_oid)) - abbrevlen = (int)sizeof(start_oid); - - git_oid_tostr(start_oid, abbrevlen, &delta->old_file.oid); - git_oid_tostr(end_oid, abbrevlen, &delta->new_file.oid); + git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid); + git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid); /* TODO: Match git diff more closely */ if (delta->old_file.mode == delta->new_file.mode) { @@ -1345,13 +1357,9 @@ int git_diff_print_patch( git_buf buf = GIT_BUF_INIT; diff_print_info pi; - pi.diff = diff; - pi.print_cb = print_cb; - pi.payload = payload; - pi.buf = &buf; - - error = git_diff_foreach( - diff, print_patch_file, print_patch_hunk, print_patch_line, &pi); + if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) + error = git_diff_foreach( + diff, print_patch_file, print_patch_hunk, print_patch_line, &pi); git_buf_free(&buf); @@ -1792,12 +1800,9 @@ int git_diff_patch_print( assert(patch && print_cb); - pi.diff = patch->diff; - pi.print_cb = print_cb; - pi.payload = payload; - pi.buf = &temp; - - error = print_patch_file(patch->delta, 0, &pi); + if (!(error = diff_print_info_init( + &pi, &temp, patch->diff, print_cb, payload))) + error = print_patch_file(patch->delta, 0, &pi); for (h = 0; h < patch->hunks_size && !error; ++h) { diff_patch_hunk *hunk = &patch->hunks[h]; From 93d8f77fed9407421ba1c90141d997b2dea7e0e7 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Thu, 23 May 2013 15:11:53 -0700 Subject: [PATCH 271/384] Improve test failure output --- include/git2/repository.h | 9 +++++ src/repository.c | 12 +++++++ tests-clar/repo/shallow.c | 33 ++++++++++++++++++ tests-clar/resources/shallow.git/HEAD | 1 + tests-clar/resources/shallow.git/config | 8 +++++ ...6e49b161700946489570d96153e5be4dc31ad4.idx | Bin 0 -> 1324 bytes ...e49b161700946489570d96153e5be4dc31ad4.pack | Bin 0 -> 791 bytes tests-clar/resources/shallow.git/packed-refs | 2 ++ .../resources/shallow.git/refs/.gitkeep | 0 tests-clar/resources/shallow.git/shallow | 1 + 10 files changed, 66 insertions(+) create mode 100644 tests-clar/repo/shallow.c create mode 100644 tests-clar/resources/shallow.git/HEAD create mode 100644 tests-clar/resources/shallow.git/config create mode 100644 tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx create mode 100644 tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack create mode 100644 tests-clar/resources/shallow.git/packed-refs create mode 100644 tests-clar/resources/shallow.git/refs/.gitkeep create mode 100644 tests-clar/resources/shallow.git/shallow diff --git a/include/git2/repository.h b/include/git2/repository.h index bb2b3db83..4fbd913b1 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -657,6 +657,15 @@ GIT_EXTERN(int) git_repository_set_namespace(git_repository *repo, const char *n */ GIT_EXTERN(const char *) git_repository_get_namespace(git_repository *repo); + +/** + * Determine if the repository was a shallow clone + * + * @param repo The repository + * @return 1 if shallow, zero if not + */ +GIT_EXTERN(int) git_repository_is_shallow(git_repository *repo); + /** @} */ GIT_END_DECL #endif diff --git a/src/repository.c b/src/repository.c index 9957f32b7..b0359a58f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1822,3 +1822,15 @@ int git_repository_state(git_repository *repo) git_buf_free(&repo_path); return state; } + +int git_repository_is_shallow(git_repository *repo) +{ + git_buf path = GIT_BUF_INIT; + struct stat st; + + git_buf_joinpath(&path, repo->path_repository, "shallow"); + + if (git_path_lstat(path.ptr, &st) == GIT_ENOTFOUND) + return 0; + return st.st_size == 0 ? 0 : 1; +} diff --git a/tests-clar/repo/shallow.c b/tests-clar/repo/shallow.c new file mode 100644 index 000000000..1cc66ae40 --- /dev/null +++ b/tests-clar/repo/shallow.c @@ -0,0 +1,33 @@ +#include "clar_libgit2.h" +#include "fileops.h" + +static git_repository *g_repo; + +void test_repo_shallow__initialize(void) +{ +} + +void test_repo_shallow__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_repo_shallow__no_shallow_file(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_assert_equal_i(0, git_repository_is_shallow(g_repo)); +} + +void test_repo_shallow__empty_shallow_file(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_git_mkfile("testrepo.git/shallow", ""); + cl_assert_equal_i(0, git_repository_is_shallow(g_repo)); +} + +void test_repo_shallow__shallow_repo(void) +{ + g_repo = cl_git_sandbox_init("shallow.git"); + cl_assert_equal_i(1, git_repository_is_shallow(g_repo)); +} + diff --git a/tests-clar/resources/shallow.git/HEAD b/tests-clar/resources/shallow.git/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests-clar/resources/shallow.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests-clar/resources/shallow.git/config b/tests-clar/resources/shallow.git/config new file mode 100644 index 000000000..a88b74b69 --- /dev/null +++ b/tests-clar/resources/shallow.git/config @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true + ignorecase = true + precomposeunicode = false +[remote "origin"] + url = file://testrepo.git diff --git a/tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx b/tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx new file mode 100644 index 0000000000000000000000000000000000000000..bfc7d24ff4178cdde4ff06b36ace5dc71e6d7edf GIT binary patch literal 1324 zcmexg;-AdGz`z8=v<8eo3kCv%{6;Qj2I?KDm<8x}N-!%>KQ%BL&>VIk=AaSg1e!bI zv4p_=uf1JqtU_0s^-B0NHCl{&&YDfX=Ka!fsjb(GZ;J|_JpL87LFtU^&gOk)+ilo8 z?#$Vj<~#U=IsG@>yF%Ge;oI7R z{ubLdnMd1|`i?xlcWj?&@;dv`_DnUy^Ijt;fofp8vS~tls#F zoaLp=h1)FO31v*P;+vQv&7)at4b1w${L6S3NS6R{1CSjFjBg$w{SJsv0oilKH*)Qo rI*((~)L*l@&xOAhtS?G@u-*99hsWiQkG`n5FZS{O$3sUJO4b7af1;4_ literal 0 HcmV?d00001 diff --git a/tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack b/tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack new file mode 100644 index 0000000000000000000000000000000000000000..ccc6932fc403443b47b4bb7f4e1ea1245ce7193e GIT binary patch literal 791 zcmWG=boORoU|<4b&Z)c=bLO7D$jM~L)9~=S-$d3a+dPB0m70_-9A|m&Wj=o@@_V?< z!R_<;|81PTiY*jJm(E%f$cc{+v7Y>Avgx<|!m}v`$byd@lX{-SF$F zJinipzGF6Mwf{I>5a^&@-z;WB9=Gpyt!0r*J^oGRQ5D(rZk76n6GdtlW@`3T|8H~B zzi{{XW7~a}XBt{Ie4V$%^Q@%sPchYs#~rrf`a;eESD)>BwV~GOWpDG061|zCk+Wu* zE^Jav2tOHkCTLy#J~uI!)cM6zZksOv3j+uj2V}A=z)oy3l^v zIQd`J_wu6m*6(|)1EW!q5nH}N$0@b z3)m~>7@HUv7#jw16qS~IEKQAAn6u(TZmwf}ckb*ni{EL^nFWnZWzfV`I=F^Yx8*EFB1C~~*8-Y=WpKqu)NCIA|x{|%QE6* z(Zy}e-{Wfnf55t;MI!@vnxpqyR$FXSYuUXya!e0y47bQLb0F@qk-v9sr literal 0 HcmV?d00001 diff --git a/tests-clar/resources/shallow.git/packed-refs b/tests-clar/resources/shallow.git/packed-refs new file mode 100644 index 000000000..97eed743b --- /dev/null +++ b/tests-clar/resources/shallow.git/packed-refs @@ -0,0 +1,2 @@ +# pack-refs with: peeled +a65fedf39aefe402d3bb6e24df4d4f5fe4547750 refs/heads/master diff --git a/tests-clar/resources/shallow.git/refs/.gitkeep b/tests-clar/resources/shallow.git/refs/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests-clar/resources/shallow.git/shallow b/tests-clar/resources/shallow.git/shallow new file mode 100644 index 000000000..9536ad89c --- /dev/null +++ b/tests-clar/resources/shallow.git/shallow @@ -0,0 +1 @@ +be3563ae3f795b2b4353bcce3a527ad0a4f7f644 From 49f70f2c370e649c3a0970b6aadd6f5cdf4f2701 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 23 May 2013 15:48:06 -0700 Subject: [PATCH 272/384] Fill out diff rename test coverage This extends the rename tests to make sure that every rename scenario in the inner loop of git_diff_find_similar is actually exercised. Also, fixes an incorrect assert that was in one of the clauses that was not previously being exercised. --- src/diff_tform.c | 2 +- tests-clar/diff/rename.c | 105 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/diff_tform.c b/src/diff_tform.c index 0f4ecc7b5..bc3acae1d 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -776,7 +776,7 @@ int git_diff_find_similar( num_rewrites++; } else { - assert(delta_is_split(from)); + assert(delta_is_split(to)); if (similarity < (int)opts.rename_from_rewrite_threshold) continue; diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 1edf66e95..8bff96cf2 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -650,6 +650,58 @@ void test_diff_rename__file_exchange(void) git_buf_free(&c2); } +void test_diff_rename__file_partial_exchange(void) +{ + git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT; + git_index *index; + git_tree *tree; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + int i; + + cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt")); + cl_git_pass(git_futils_writebuffer(&c1, "renames/songof7cities.txt", 0, 0)); + for (i = 0; i < 100; ++i) + cl_git_pass(git_buf_puts(&c2, "this is not the content you are looking for\n")); + cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_read_tree(index, tree)); + cl_git_pass(git_index_add_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_add_bypath(index, "untimely.txt")); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + + git_diff_list_free(diff); + git_tree_free(tree); + git_index_free(index); + + git_buf_free(&c1); + git_buf_free(&c2); +} + void test_diff_rename__file_split(void) { git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT; @@ -706,3 +758,56 @@ void test_diff_rename__file_split(void) git_buf_free(&c1); git_buf_free(&c2); } + +void test_diff_rename__from_deleted_to_split(void) +{ + git_buf c1 = GIT_BUF_INIT; + git_index *index; + git_tree *tree; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + + /* old file is missing, new file is actually old file renamed */ + + cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt")); + cl_git_pass(git_futils_writebuffer(&c1, "renames/untimely.txt", 0, 0)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_read_tree(index, tree)); + cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_add_bypath(index, "untimely.txt")); + + diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED; + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNMODIFIED]); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNMODIFIED]); + + git_diff_list_free(diff); + git_tree_free(tree); + git_index_free(index); + + git_buf_free(&c1); +} From 3b32b6d3cc649a7808151dd67d1dd3e45e202f08 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 23 May 2013 09:19:42 -0700 Subject: [PATCH 273/384] More tests of config with various absent files Plus a bit of extra paranoia to ensure config object has valid contents. --- src/config.c | 5 +- tests-clar/repo/config.c | 129 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 131 insertions(+), 3 deletions(-) diff --git a/src/config.c b/src/config.c index dc8c7024e..49b91237d 100644 --- a/src/config.c +++ b/src/config.c @@ -465,10 +465,13 @@ static int get_string(const char **out, const git_config *cfg, const char *name) { file_internal *internal; unsigned int i; + int res; git_vector_foreach(&cfg->files, i, internal) { - int res = get_string_at_file(out, internal->file, name); + if (!internal || !internal->file || !internal->file->get) + continue; + res = get_string_at_file(out, internal->file, name); if (res != GIT_ENOTFOUND) return res; } diff --git a/tests-clar/repo/config.c b/tests-clar/repo/config.c index 086fb5e4f..b8971bb6b 100644 --- a/tests-clar/repo/config.c +++ b/tests-clar/repo/config.c @@ -13,14 +13,15 @@ void test_repo_config__initialize(void) cl_must_pass(p_mkdir("alternate", 0777)); cl_git_pass(git_path_prettify(&path, "alternate", NULL)); - } void test_repo_config__cleanup(void) { + cl_git_pass(git_path_prettify(&path, "alternate", NULL)); cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)); - git_buf_free(&path); + cl_assert(!git_path_isdir("alternate")); + cl_fixture_cleanup("empty_standard_repo"); } @@ -73,3 +74,127 @@ void test_repo_config__open_missing_global_with_separators(void) git_config_free(config); git_repository_free(repo); } + +#include "repository.h" + +void test_repo_config__read_no_configs(void) +{ + git_repository *repo; + int val; + + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); + + /* with none */ + + cl_must_pass(p_unlink("empty_standard_repo/.git/config")); + cl_assert(!git_path_isfile("empty_standard_repo/.git/config")); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__cvar_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); + cl_assert_equal_i(GIT_ABBREV_DEFAULT, val); + git_repository_free(repo); + + /* with just system */ + + cl_must_pass(p_mkdir("alternate/1", 0777)); + cl_git_pass(git_buf_joinpath(&path, path.ptr, "1")); + cl_git_rewritefile("alternate/1/gitconfig", "[core]\n\tabbrev = 10\n"); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__cvar_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); + cl_assert_equal_i(10, val); + git_repository_free(repo); + + /* with xdg + system */ + + cl_must_pass(p_mkdir("alternate/2", 0777)); + path.ptr[path.size - 1] = '2'; + cl_git_rewritefile("alternate/2/config", "[core]\n\tabbrev = 20\n"); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__cvar_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); + cl_assert_equal_i(20, val); + git_repository_free(repo); + + /* with global + xdg + system */ + + cl_must_pass(p_mkdir("alternate/3", 0777)); + path.ptr[path.size - 1] = '3'; + cl_git_rewritefile("alternate/3/.gitconfig", "[core]\n\tabbrev = 30\n"); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__cvar_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); + cl_assert_equal_i(30, val); + git_repository_free(repo); + + /* with all configs */ + + cl_git_rewritefile("empty_standard_repo/.git/config", "[core]\n\tabbrev = 40\n"); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__cvar_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); + cl_assert_equal_i(40, val); + git_repository_free(repo); + + /* with all configs but delete the files ? */ + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__cvar_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); + cl_assert_equal_i(40, val); + + cl_must_pass(p_unlink("empty_standard_repo/.git/config")); + cl_assert(!git_path_isfile("empty_standard_repo/.git/config")); + + cl_must_pass(p_unlink("alternate/1/gitconfig")); + cl_assert(!git_path_isfile("alternate/1/gitconfig")); + + cl_must_pass(p_unlink("alternate/2/config")); + cl_assert(!git_path_isfile("alternate/2/config")); + + cl_must_pass(p_unlink("alternate/3/.gitconfig")); + cl_assert(!git_path_isfile("alternate/3/.gitconfig")); + + git_repository__cvar_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); + cl_assert_equal_i(40, val); + git_repository_free(repo); + + /* reopen */ + + cl_assert(!git_path_isfile("empty_standard_repo/.git/config")); + cl_assert(!git_path_isfile("alternate/3/.gitconfig")); + + cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); + git_repository__cvar_cache_clear(repo); + val = -1; + cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); + cl_assert_equal_i(7, val); + git_repository_free(repo); + + cl_assert(!git_path_exists("empty_standard_repo/.git/config")); + cl_assert(!git_path_exists("alternate/3/.gitconfig")); +} From 0700ca1a74b58b73c4fb9ececffeaa80046c2f58 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 23 May 2013 16:11:53 -0700 Subject: [PATCH 274/384] More config code checks and cleanups --- src/config.c | 57 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/src/config.c b/src/config.c index 49b91237d..e436a31ad 100644 --- a/src/config.c +++ b/src/config.c @@ -339,17 +339,6 @@ int git_config_foreach_match( return ret; } -int git_config_delete_entry(git_config *cfg, const char *name) -{ - git_config_backend *file; - file_internal *internal; - - internal = git_vector_get(&cfg->files, 0); - file = internal->file; - - return file->del(file, name); -} - /************** * Setters **************/ @@ -361,6 +350,19 @@ static int config_error_nofiles(const char *name) return GIT_ENOTFOUND; } +int git_config_delete_entry(git_config *cfg, const char *name) +{ + git_config_backend *file; + file_internal *internal; + + internal = git_vector_get(&cfg->files, 0); + if (!internal || !internal->file) + return config_error_nofiles(name); + file = internal->file; + + return file->del(file, name); +} + int git_config_set_int64(git_config *cfg, const char *name, int64_t value) { char str_value[32]; /* All numbers should fit in here */ @@ -390,7 +392,7 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value) } internal = git_vector_get(&cfg->files, 0); - if (!internal) + if (!internal || !internal->file) return config_error_nofiles(name); file = internal->file; @@ -468,7 +470,7 @@ static int get_string(const char **out, const git_config *cfg, const char *name) int res; git_vector_foreach(&cfg->files, i, internal) { - if (!internal || !internal->file || !internal->file->get) + if (!internal || !internal->file) continue; res = get_string_at_file(out, internal->file, name); @@ -506,12 +508,17 @@ int git_config_get_entry(const git_config_entry **out, const git_config *cfg, co { file_internal *internal; unsigned int i; + git_config_backend *file; + int ret; *out = NULL; git_vector_foreach(&cfg->files, i, internal) { - git_config_backend *file = internal->file; - int ret = file->get(file, name, out); + if (!internal || !internal->file) + continue; + file = internal->file; + + ret = file->get(file, name, out); if (ret != GIT_ENOTFOUND) return ret; } @@ -519,8 +526,9 @@ int git_config_get_entry(const git_config_entry **out, const git_config *cfg, co return config_error_notfound(name); } -int git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp, - git_config_foreach_cb cb, void *payload) +int git_config_get_multivar( + const git_config *cfg, const char *name, const char *regexp, + git_config_foreach_cb cb, void *payload) { file_internal *internal; git_config_backend *file; @@ -533,7 +541,10 @@ int git_config_get_multivar(const git_config *cfg, const char *name, const char */ for (i = cfg->files.length; i > 0; --i) { internal = git_vector_get(&cfg->files, i - 1); + if (!internal || !internal->file) + continue; file = internal->file; + ret = file->get_multivar(file, name, regexp, cb, payload); if (ret < 0 && ret != GIT_ENOTFOUND) return ret; @@ -548,7 +559,7 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex file_internal *internal; internal = git_vector_get(&cfg->files, 0); - if (!internal) + if (!internal || !internal->file) return config_error_nofiles(name); file = internal->file; @@ -643,13 +654,12 @@ int git_config_open_default(git_config **out) git_config *cfg = NULL; git_buf buf = GIT_BUF_INIT; - error = git_config_new(&cfg); + if ((error = git_config_new(&cfg)) < 0) + return error; - if (!error && (!git_config_find_global_r(&buf) || - !git_config__global_location(&buf))) { + if (!git_config_find_global_r(&buf) || !git_config__global_location(&buf)) { error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_GLOBAL, 0); - } else { } if (!error && !git_config_find_xdg_r(&buf)) @@ -662,7 +672,7 @@ int git_config_open_default(git_config **out) git_buf_free(&buf); - if (error && cfg) { + if (error) { git_config_free(cfg); cfg = NULL; } @@ -675,6 +685,7 @@ int git_config_open_default(git_config **out) /*********** * Parsers ***********/ + int git_config_lookup_map_value( int *out, const git_cvar_map *maps, From 6f0b8142e65b43f2224027a7abc67116ab6ad1a7 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Thu, 23 May 2013 17:28:52 -0700 Subject: [PATCH 275/384] Stop leaking memory --- src/repository.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/repository.c b/src/repository.c index b0359a58f..28505e822 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1827,10 +1827,15 @@ int git_repository_is_shallow(git_repository *repo) { git_buf path = GIT_BUF_INIT; struct stat st; + int error; git_buf_joinpath(&path, repo->path_repository, "shallow"); + error = git_path_lstat(path.ptr, &st); + git_buf_free(&path); - if (git_path_lstat(path.ptr, &st) == GIT_ENOTFOUND) + if (error == GIT_ENOTFOUND) return 0; + if (error < 0) + return -1; return st.st_size == 0 ? 0 : 1; } From 25a899ec8431cf4738ee7eb6ca79f60cc27141de Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 24 May 2013 10:30:32 +0000 Subject: [PATCH 276/384] qsort_r is broken on HURD, avoid --- src/util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util.c b/src/util.c index 49530f8c8..a6f87a0d7 100644 --- a/src/util.c +++ b/src/util.c @@ -685,7 +685,8 @@ static int GIT_STDLIB_CALL git__qsort_r_glue_cmp( void git__qsort_r( void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) { -#if defined(__MINGW32__) || defined(__OpenBSD__) || defined(AMIGA) +#if defined(__MINGW32__) || defined(__OpenBSD__) || defined(AMIGA) || \ + defined(__gnu_hurd__) git__insertsort_r(els, nel, elsize, NULL, cmp, payload); #elif defined(GIT_WIN32) git__qsort_r_glue glue = { cmp, payload }; From 43efc4493d72b4666d8fe31b89c3aad97c3ec1a3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 16 May 2013 11:03:55 -0700 Subject: [PATCH 277/384] Ensure reuc vector is always valid In theory, if there was a problem reading the REUC data, the read_reuc() routine could have left uninitialized and invalid data in the git_index vector. This moves the line that inserts a new entry into the vector down to the bottom of the routine so we know all the content is already valid. Also, per @linquize, this uses calloc to ensure no uninitialized data. --- src/index.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/index.c b/src/index.c index ec45a5c0e..d5d9aef48 100644 --- a/src/index.c +++ b/src/index.c @@ -1373,8 +1373,9 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) size_t len; int i; - /* This gets called multiple times, the vector might already be initialized */ - if (index->reuc._alloc_size == 0 && git_vector_init(&index->reuc, 16, reuc_cmp) < 0) + /* If called multiple times, the vector might already be initialized */ + if (index->reuc._alloc_size == 0 && + git_vector_init(&index->reuc, 16, reuc_cmp) < 0) return -1; while (size) { @@ -1384,12 +1385,9 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) if (size <= len) return index_error_invalid("reading reuc entries"); - lost = git__malloc(sizeof(git_index_reuc_entry)); + lost = git__calloc(1, sizeof(git_index_reuc_entry)); GITERR_CHECK_ALLOC(lost); - if (git_vector_insert(&index->reuc, lost) < 0) - return -1; - /* read NUL-terminated pathname for entry */ lost->path = git__strdup(buffer); GITERR_CHECK_ALLOC(lost->path); @@ -1427,6 +1425,10 @@ static int read_reuc(git_index *index, const char *buffer, size_t size) size -= 20; buffer += 20; } + + /* entry was read successfully - insert into reuc vector */ + if (git_vector_insert(&index->reuc, lost) < 0) + return -1; } /* entries are guaranteed to be sorted on-disk */ From 0f1f9833cf13ac8714ec21fcf29d895ff8cbfe24 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 24 May 2013 10:32:07 -0700 Subject: [PATCH 278/384] Add typedefs on some public enums Apparently this makes things easier to bind in some languages. --- include/git2/common.h | 13 +++++++------ include/git2/errors.h | 4 ++-- include/git2/odb_backend.h | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index f9e9929ea..1b0e0cb4f 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -103,10 +103,10 @@ GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev); /** * Combinations of these values describe the capabilities of libgit2. */ -enum { +typedef enum { GIT_CAP_THREADS = ( 1 << 0 ), GIT_CAP_HTTPS = ( 1 << 1 ) -}; +} git_cap_t; /** * Query compile time options for libgit2. @@ -114,12 +114,13 @@ enum { * @return A combination of GIT_CAP_* values. * * - GIT_CAP_THREADS - * Libgit2 was compiled with thread support. Note that thread support is still to be seen as a - * 'work in progress'. + * Libgit2 was compiled with thread support. Note that thread support is + * still to be seen as a 'work in progress' - basic object lookups are + * believed to be threadsafe, but other operations may not be. * * - GIT_CAP_HTTPS - * Libgit2 supports the https:// protocol. This requires the open ssl library to be - * found when compiling libgit2. + * Libgit2 supports the https:// protocol. This requires the openssl + * library to be found when compiling libgit2. */ GIT_EXTERN(int) git_libgit2_capabilities(void); diff --git a/include/git2/errors.h b/include/git2/errors.h index 917f0699c..caf9e62b8 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -18,7 +18,7 @@ GIT_BEGIN_DECL /** Generic return codes */ -enum { +typedef enum { GIT_OK = 0, GIT_ERROR = -1, GIT_ENOTFOUND = -3, @@ -35,7 +35,7 @@ enum { GIT_PASSTHROUGH = -30, GIT_ITEROVER = -31, -}; +} git_error_code; typedef struct { char *message; diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h index 4c2493a25..af1e3e5b9 100644 --- a/include/git2/odb_backend.h +++ b/include/git2/odb_backend.h @@ -59,11 +59,11 @@ GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file); /** Streaming mode */ -enum { +typedef enum { GIT_STREAM_RDONLY = (1 << 1), GIT_STREAM_WRONLY = (1 << 2), GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY), -}; +} git_odb_stream_t; /** A stream to read/write from a backend */ struct git_odb_stream { From 2e62e7c23ce69d89d495a2c42458c7e185a3120a Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 24 May 2013 10:33:41 -0700 Subject: [PATCH 279/384] Docs for git_libgit2_opts and cache disable tweak This adds docs for the cache control options to git_libgit2_opts and also tweaks the cache code so that if the cache is disabled, then the next time we attempt to insert something into the cache in question, we will actually clear any old cached objects. --- include/git2/common.h | 33 +++++++++++++++++++++++++++++++-- src/cache.c | 5 +++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/include/git2/common.h b/include/git2/common.h index 1b0e0cb4f..b52e13918 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -125,7 +125,7 @@ typedef enum { GIT_EXTERN(int) git_libgit2_capabilities(void); -enum { +typedef enum { GIT_OPT_GET_MWINDOW_SIZE, GIT_OPT_SET_MWINDOW_SIZE, GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, @@ -136,7 +136,7 @@ enum { GIT_OPT_SET_CACHE_MAX_SIZE, GIT_OPT_ENABLE_CACHING, GIT_OPT_GET_CACHED_MEMORY -}; +} git_libgit2_opt_t; /** * Set or query a library global option @@ -180,6 +180,35 @@ enum { * > - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL, * > or GIT_CONFIG_LEVEL_XDG. * + * * opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, git_otype type, size_t size) + * + * > Set the maximum data size for the given type of object to be + * > considered eligible for caching in memory. Setting to value to + * > zero means that that type of object will not be cached. + * > Defaults to 0 for GIT_OBJ_BLOB (i.e. won't cache blobs) and 4k + * > for GIT_OBJ_COMMIT, GIT_OBJ_TREE, and GIT_OBJ_TAG. + * + * * opts(GIT_OPT_SET_CACHE_MAX_SIZE, ssize_t max_storage_bytes) + * + * > Set the maximum total data size that will be cached in memory + * > across all repositories before libgit2 starts evicting objects + * > from the cache. This is a soft limit, in that the library might + * > briefly exceed it, but will start aggressively evicting objects + * > from cache when that happens. The default cache size is 256Mb. + * + * * opts(GIT_OPT_ENABLE_CACHING, int enabled) + * + * > Enable or disable caching completely. + * > + * > Because caches are repository-specific, disabling the cache + * > cannot immediately clear all cached objects, but each cache will + * > be cleared on the next attempt to update anything in it. + * + * * opts(GIT_OPT_GET_CACHED_MEMORY, ssize_t *current, ssize_t *allowed) + * + * > Get the current bytes in cache and the maximum that would be + * > allowed in the cache. + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure diff --git a/src/cache.c b/src/cache.c index 1360cc976..dc3af063a 100644 --- a/src/cache.c +++ b/src/cache.c @@ -174,6 +174,11 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry) git_cached_obj_incref(entry); + if (!git_cache__enabled && cache->used_memory > 0) { + git_cache_clear(cache); + return entry; + } + if (!cache_should_store(entry->type, entry->size)) return entry; From 16adc9fade52b49e2bc13cb52407cc0025a93c8b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 24 May 2013 10:35:58 -0700 Subject: [PATCH 280/384] Typedef git_config_level_t and use it everywhere The GIT_CONFIG_LEVEL constants actually work well as an enum because they are mutually exclusive, so this adds a typedef to the enum and uses that everywhere that one of these constants are expected, instead of the old code that typically used an unsigned int. --- include/git2/config.h | 37 ++++++++++++++++++++++++++----------- include/git2/sys/config.h | 4 ++-- src/config.c | 24 ++++++++++++------------ src/config_file.c | 8 ++++---- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/include/git2/config.h b/include/git2/config.h index 8d1a1a5a6..518dcaf16 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -27,18 +27,33 @@ GIT_BEGIN_DECL * git_config_open_default() and git_repository_config() honor those * priority levels as well. */ -enum { - GIT_CONFIG_LEVEL_SYSTEM = 1, /**< System-wide configuration file. */ - GIT_CONFIG_LEVEL_XDG = 2, /**< XDG compatible configuration file (.config/git/config). */ - GIT_CONFIG_LEVEL_GLOBAL = 3, /**< User-specific configuration file, also called Global configuration file. */ - GIT_CONFIG_LEVEL_LOCAL = 4, /**< Repository specific configuration file. */ - GIT_CONFIG_HIGHEST_LEVEL = -1, /**< Represents the highest level of a config file. */ -}; +typedef enum { + /** System-wide configuration file; /etc/gitconfig on Linux systems */ + GIT_CONFIG_LEVEL_SYSTEM = 1, + + /** XDG compatible configuration file; typically ~/.config/git/config */ + GIT_CONFIG_LEVEL_XDG = 2, + + /** User-specific configuration file (also called Global configuration + * file); typically ~/.gitconfig + */ + GIT_CONFIG_LEVEL_GLOBAL = 3, + + /** Repository specific configuration file; $WORK_DIR/.git/config on + * non-bare repos + */ + GIT_CONFIG_LEVEL_LOCAL = 4, + + /** Represents the highest level available config file (i.e. the most + * specific config file available that actually is loaded) + */ + GIT_CONFIG_HIGHEST_LEVEL = -1, +} git_config_level_t; typedef struct { const char *name; const char *value; - unsigned int level; + git_config_level_t level; } git_config_entry; typedef int (*git_config_foreach_cb)(const git_config_entry *, void *); @@ -155,7 +170,7 @@ GIT_EXTERN(int) git_config_new(git_config **out); GIT_EXTERN(int) git_config_add_file_ondisk( git_config *cfg, const char *path, - unsigned int level, + git_config_level_t level, int force); /** @@ -192,7 +207,7 @@ GIT_EXTERN(int) git_config_open_ondisk(git_config **out, const char *path); GIT_EXTERN(int) git_config_open_level( git_config **out, const git_config *parent, - unsigned int level); + git_config_level_t level); /** * Open the global/XDG configuration file according to git's rules @@ -241,7 +256,7 @@ GIT_EXTERN(void) git_config_free(git_config *cfg); * @return 0 or an error code */ GIT_EXTERN(int) git_config_get_entry( - const git_config_entry **out, + const git_config_entry **out, const git_config *cfg, const char *name); diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h index 1c9deba7c..11e59cf03 100644 --- a/include/git2/sys/config.h +++ b/include/git2/sys/config.h @@ -29,7 +29,7 @@ struct git_config_backend { struct git_config *cfg; /* Open means open the file/database and parse if necessary */ - int (*open)(struct git_config_backend *, unsigned int level); + int (*open)(struct git_config_backend *, git_config_level_t level); int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry); int (*get_multivar)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload); int (*set)(struct git_config_backend *, const char *key, const char *value); @@ -63,7 +63,7 @@ struct git_config_backend { GIT_EXTERN(int) git_config_add_backend( git_config *cfg, git_config_backend *file, - unsigned int level, + git_config_level_t level, int force); /** @} */ diff --git a/src/config.c b/src/config.c index e436a31ad..9491d267a 100644 --- a/src/config.c +++ b/src/config.c @@ -23,7 +23,7 @@ typedef struct { git_refcount rc; git_config_backend *file; - unsigned int level; + git_config_level_t level; } file_internal; static void file_internal_free(file_internal *internal) @@ -87,7 +87,7 @@ int git_config_new(git_config **out) int git_config_add_file_ondisk( git_config *cfg, const char *path, - unsigned int level, + git_config_level_t level, int force) { git_config_backend *file = NULL; @@ -138,11 +138,11 @@ int git_config_open_ondisk(git_config **out, const char *path) static int find_internal_file_by_level( file_internal **internal_out, const git_config *cfg, - int level) + git_config_level_t level) { int pos = -1; file_internal *internal; - unsigned int i; + size_t i; /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file * which has the highest level. As config files are stored in a vector @@ -153,14 +153,14 @@ static int find_internal_file_by_level( pos = 0; } else { git_vector_foreach(&cfg->files, i, internal) { - if (internal->level == (unsigned int)level) + if (internal->level == level) pos = i; } } if (pos == -1) { giterr_set(GITERR_CONFIG, - "No config file exists for the given level '%i'", level); + "No config file exists for the given level '%i'", (int)level); return GIT_ENOTFOUND; } @@ -175,17 +175,17 @@ static int duplicate_level(void **old_raw, void *new_raw) GIT_UNUSED(new_raw); - giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (*old)->level); + giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (int)(*old)->level); return GIT_EEXISTS; } static void try_remove_existing_file_internal( git_config *cfg, - unsigned int level) + git_config_level_t level) { int pos = -1; file_internal *internal; - unsigned int i; + size_t i; git_vector_foreach(&cfg->files, i, internal) { if (internal->level == level) @@ -206,7 +206,7 @@ static void try_remove_existing_file_internal( static int git_config__add_internal( git_config *cfg, file_internal *internal, - unsigned int level, + git_config_level_t level, int force) { int result; @@ -238,7 +238,7 @@ int git_config_open_global(git_config **cfg_out, git_config *cfg) int git_config_open_level( git_config **cfg_out, const git_config *cfg_parent, - unsigned int level) + git_config_level_t level) { git_config *cfg; file_internal *internal; @@ -263,7 +263,7 @@ int git_config_open_level( int git_config_add_backend( git_config *cfg, git_config_backend *file, - unsigned int level, + git_config_level_t level, int force) { file_internal *internal; diff --git a/src/config_file.c b/src/config_file.c index e57cd1e53..dec952115 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -81,10 +81,10 @@ typedef struct { time_t file_mtime; size_t file_size; - unsigned int level; + git_config_level_t level; } diskfile_backend; -static int config_parse(diskfile_backend *cfg_file, unsigned int level); +static int config_parse(diskfile_backend *cfg_file, git_config_level_t level); static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value); static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value); static char *escape_value(const char *ptr); @@ -181,7 +181,7 @@ static void free_vars(git_strmap *values) git_strmap_free(values); } -static int config_open(git_config_backend *cfg, unsigned int level) +static int config_open(git_config_backend *cfg, git_config_level_t level) { int res; diskfile_backend *b = (diskfile_backend *)cfg; @@ -965,7 +965,7 @@ static int strip_comments(char *line, int in_quotes) return quote_count; } -static int config_parse(diskfile_backend *cfg_file, unsigned int level) +static int config_parse(diskfile_backend *cfg_file, git_config_level_t level) { int c; char *current_section = NULL; From d20b044961352348855ee82dcc77615f605ac832 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 24 May 2013 10:37:40 -0700 Subject: [PATCH 281/384] Clarify GIT_DIFF_INCLUDE_UNTRACKED_CONTENT option This improves the docs for GIT_DIFF_INCLUDE_UNTRACKED_CONTENT as well as the other flags related to UNTRACKED items in diff, plus it makes that flag now automatically turn on GIT_DIFF_INCLUDE_UNTRACKED which seems like a reasonable dwim type of change. --- include/git2/diff.h | 15 ++++++++++----- src/diff.c | 4 ++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 0d4875b43..d26456cb0 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -89,9 +89,10 @@ typedef enum { /** Include unmodified files in the diff list */ GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9), - /** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked directory - * will be marked with only a single entry in the diff list; this flag - * adds all files under the directory as UNTRACKED entries, too. + /** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked + * directory will be marked with only a single entry in the diff list + * (a la what core Git does in `git status`); this flag adds *all* + * files under untracked directories as UNTRACKED entries, too. */ GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10), @@ -103,7 +104,11 @@ typedef enum { /** Use case insensitive filename comparisons */ GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12), - /** When generating patch text, include the content of untracked files */ + /** When generating patch text, include the content of untracked + * files. This automatically turns on GIT_DIFF_INCLUDE_UNTRACKED but + * it does not turn on GIT_DIFF_RECURSE_UNTRACKED_DIRS. Add that + * flag if you want the content of every single UNTRACKED file. + */ GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13), /** Disable updating of the `binary` flag in delta records. This is @@ -139,7 +144,7 @@ typedef enum { * consider UNTRACKED only if it has an actual untracked file in it. * This scan is extra work for a case you often don't care about. This * flag makes libgit2 immediately label an untracked directory as - * UNTRACKED without looking insde it (which differs from core Git). + * UNTRACKED without looking inside it (which differs from core Git). * Of course, ignore rules are still checked for the directory itself. */ GIT_DIFF_FAST_UNTRACKED_DIRS = (1 << 19), diff --git a/src/diff.c b/src/diff.c index d2389f103..b96ff4705 100644 --- a/src/diff.c +++ b/src/diff.c @@ -383,6 +383,10 @@ static int diff_list_apply_options( if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES)) diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE; + /* flag INCLUDE_UNTRACKED_CONTENT implies INCLUDE_UNTRACKED */ + if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED_CONTENT)) + diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; + /* load config values that affect diff behavior */ if (git_repository_config__weakptr(&cfg, repo) < 0) return -1; From 7a5ee3dc923caf2b3b9b5e9b2408340f6ae32d7d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 24 May 2013 11:09:04 -0700 Subject: [PATCH 282/384] Add ~ expansion to global attributes and excludes This adds ~/ prefix expansion for the value of core.attributesfile and core.excludesfile, plus it fixes the fact that the attributes cache was holding on to the string data from the config for a long time (instead of making its own strdup) which could have caused a problem if the config was refreshed. Adds a test for the new expansion capability. --- src/attr.c | 29 +++++++++++++++++++++-------- src/attrcache.h | 8 ++++---- tests-clar/attr/ignore.c | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/src/attr.c b/src/attr.c index 9fe4471f6..6cdff29f9 100644 --- a/src/attr.c +++ b/src/attr.c @@ -596,26 +596,33 @@ static int collect_attr_files( } static int attr_cache__lookup_path( - const char **out, git_config *cfg, const char *key, const char *fallback) + char **out, git_config *cfg, const char *key, const char *fallback) { git_buf buf = GIT_BUF_INIT; int error; + const char *cfgval = NULL; - if (!(error = git_config_get_string(out, cfg, key))) - return 0; + *out = NULL; - if (error == GIT_ENOTFOUND) { + if (!(error = git_config_get_string(&cfgval, cfg, key))) { + + /* expand leading ~/ as needed */ + if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' && + !git_futils_find_global_file(&buf, &cfgval[2])) + *out = git_buf_detach(&buf); + else if (cfgval) + *out = git__strdup(cfgval); + + } else if (error == GIT_ENOTFOUND) { giterr_clear(); error = 0; if (!git_futils_find_xdg_file(&buf, fallback)) *out = git_buf_detach(&buf); - else - *out = NULL; - - git_buf_free(&buf); } + git_buf_free(&buf); + return error; } @@ -696,6 +703,12 @@ void git_attr_cache_flush( git_pool_clear(&cache->pool); + git__free(cache->cfg_attr_file); + cache->cfg_attr_file = NULL; + + git__free(cache->cfg_excl_file); + cache->cfg_excl_file = NULL; + cache->initialized = 0; } diff --git a/src/attrcache.h b/src/attrcache.h index 12cec4bfb..077633b87 100644 --- a/src/attrcache.h +++ b/src/attrcache.h @@ -13,10 +13,10 @@ typedef struct { int initialized; git_pool pool; - git_strmap *files; /* hash path to git_attr_file of rules */ - git_strmap *macros; /* hash name to vector */ - const char *cfg_attr_file; /* cached value of core.attributesfile */ - const char *cfg_excl_file; /* cached value of core.excludesfile */ + git_strmap *files; /* hash path to git_attr_file of rules */ + git_strmap *macros; /* hash name to vector */ + char *cfg_attr_file; /* cached value of core.attributesfile */ + char *cfg_excl_file; /* cached value of core.excludesfile */ } git_attr_cache; extern int git_attr_cache__init(git_repository *repo); diff --git a/tests-clar/attr/ignore.c b/tests-clar/attr/ignore.c index aa81e9249..8df0eb9de 100644 --- a/tests-clar/attr/ignore.c +++ b/tests-clar/attr/ignore.c @@ -1,6 +1,7 @@ #include "clar_libgit2.h" #include "posix.h" #include "path.h" +#include "fileops.h" static git_repository *g_repo = NULL; @@ -20,7 +21,7 @@ void assert_is_ignored(bool expected, const char *filepath) int is_ignored; cl_git_pass(git_ignore_path_is_ignored(&is_ignored, g_repo, filepath)); - cl_assert_equal_i(expected, is_ignored == 1); + cl_assert_equal_b(expected, is_ignored); } void test_attr_ignore__honor_temporary_rules(void) @@ -46,3 +47,35 @@ void test_attr_ignore__skip_gitignore_directory(void) assert_is_ignored(true, "NewFolder/NewFolder"); assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); } + +void test_attr_ignore__expand_tilde_to_homedir(void) +{ + git_buf path = GIT_BUF_INIT; + git_config *cfg; + + assert_is_ignored(false, "example.global_with_tilde"); + + /* construct fake home with fake global excludes */ + + cl_must_pass(p_mkdir("home", 0777)); + cl_git_pass(git_path_prettify(&path, "home", NULL)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + + cl_git_mkfile("home/globalexcludes", "# found me\n*.global_with_tilde\n"); + + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexcludes")); + git_config_free(cfg); + + git_attr_cache_flush(g_repo); /* must reset to pick up change */ + + assert_is_ignored(true, "example.global_with_tilde"); + + cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES)); + + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL)); + + git_buf_free(&path); +} From c37fb41ae02f7bd99237720181d0736ab1de9bc0 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 25 May 2013 12:35:55 -0400 Subject: [PATCH 283/384] qsort_r appeared in glibc 2.8 --- src/util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util.c b/src/util.c index a6f87a0d7..da15a039d 100644 --- a/src/util.c +++ b/src/util.c @@ -686,7 +686,8 @@ void git__qsort_r( void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) { #if defined(__MINGW32__) || defined(__OpenBSD__) || defined(AMIGA) || \ - defined(__gnu_hurd__) + defined(__gnu_hurd__) || \ + (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) git__insertsort_r(els, nel, elsize, NULL, cmp, payload); #elif defined(GIT_WIN32) git__qsort_r_glue glue = { cmp, payload }; From f8bd730cd90a1e6219447cc9c05f41295d42bbcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Sun, 26 May 2013 17:29:00 +0200 Subject: [PATCH 284/384] Fix documentation of git_branch_delete. The reference should be freed by the user, not the library. --- include/git2/branch.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/git2/branch.h b/include/git2/branch.h index d1838a63a..de414e9b0 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -58,7 +58,8 @@ GIT_EXTERN(int) git_branch_create( * Delete an existing branch reference. * * If the branch is successfully deleted, the passed reference - * object will be freed and invalidated. + * object will be invalidated. The reference must be freed manually + * by the user. * * @param branch A valid reference representing a branch * @return 0 on success, or an error code. From 0582ae6fde94912c4ac7566f151bd0e3b786bb43 Mon Sep 17 00:00:00 2001 From: Michael Schubert Date: Tue, 21 May 2013 13:56:40 +0200 Subject: [PATCH 285/384] tests: don't verify SSH unsupported with GIT_SSH --- tests-clar/network/remote/remotes.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests-clar/network/remote/remotes.c b/tests-clar/network/remote/remotes.c index 21f27bcc6..34f1c055c 100644 --- a/tests-clar/network/remote/remotes.c +++ b/tests-clar/network/remote/remotes.c @@ -100,7 +100,9 @@ void test_network_remote_remotes__supported_transport_methods_are_supported(void void test_network_remote_remotes__unsupported_transport_methods_are_unsupported(void) { +#ifndef GIT_SSH cl_assert( !git_remote_supported_url("git@github.com:libgit2/libgit2.git") ); +#endif } void test_network_remote_remotes__refspec_parsing(void) From 563c19a9ce556301e642a508e53598faf3c8935a Mon Sep 17 00:00:00 2001 From: Michael Schubert Date: Sat, 11 May 2013 11:36:29 +0200 Subject: [PATCH 286/384] packbuilder: also write index in git_packbuilder_write git_packbuilder_write() used to write a packfile to the passed file path. Instead, ask for a destination directory and create both the packfile and an index, as most users probably do expect. --- include/git2/pack.h | 12 +++++++--- src/pack-objects.c | 58 ++++++++++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/include/git2/pack.h b/include/git2/pack.h index 5e431e62d..242bddd25 100644 --- a/include/git2/pack.h +++ b/include/git2/pack.h @@ -107,14 +107,20 @@ GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid * GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *id); /** - * Write the new pack and the corresponding index to path + * Write the new pack and corresponding index file to path. * * @param pb The packbuilder - * @param path Directory to store the new pack and index + * @param path to the directory where the packfile and index should be stored + * @param progress_cb function to call with progress information from the indexer (optional) + * @param progress_payload payload for the progress callback (optional) * * @return 0 or an error code */ -GIT_EXTERN(int) git_packbuilder_write(git_packbuilder *pb, const char *file); +GIT_EXTERN(int) git_packbuilder_write( + git_packbuilder *pb, + const char *path, + git_transfer_progress_callback progress_cb, + void *progress_cb_payload); typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload); /** diff --git a/src/pack-objects.c b/src/pack-objects.c index 1329a9bc1..3d382026e 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -33,6 +33,11 @@ struct tree_walk_context { git_buf buf; }; +struct pack_write_context { + git_indexer_stream *indexer; + git_transfer_progress *stats; +}; + #ifdef GIT_THREADS #define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) do { \ @@ -620,26 +625,6 @@ static int write_pack_buf(void *buf, size_t size, void *data) return git_buf_put(b, buf, size); } -static int write_pack_to_file(void *buf, size_t size, void *data) -{ - git_filebuf *file = (git_filebuf *)data; - return git_filebuf_write(file, buf, size); -} - -static int write_pack_file(git_packbuilder *pb, const char *path) -{ - git_filebuf file = GIT_FILEBUF_INIT; - - if (git_filebuf_open(&file, path, 0) < 0 || - write_pack(pb, &write_pack_to_file, &file) < 0 || - git_filebuf_commit(&file, GIT_PACK_FILE_MODE) < 0) { - git_filebuf_cleanup(&file); - return -1; - } - - return 0; -} - static int type_size_sort(const void *_a, const void *_b) { const git_pobject *a = (git_pobject *)_a; @@ -1259,10 +1244,39 @@ int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb) return write_pack(pb, &write_pack_buf, buf); } -int git_packbuilder_write(git_packbuilder *pb, const char *path) +static int write_cb(void *buf, size_t len, void *payload) { + struct pack_write_context *ctx = payload; + return git_indexer_stream_add(ctx->indexer, buf, len, ctx->stats); +} + +int git_packbuilder_write( + git_packbuilder *pb, + const char *path, + git_transfer_progress_callback progress_cb, + void *progress_cb_payload) +{ + git_indexer_stream *indexer; + git_transfer_progress stats; + struct pack_write_context ctx; + PREPARE_PACK; - return write_pack_file(pb, path); + + if (git_indexer_stream_new( + &indexer, path, progress_cb, progress_cb_payload) < 0) + return -1; + + ctx.indexer = indexer; + ctx.stats = &stats; + + if (git_packbuilder_foreach(pb, write_cb, &ctx) < 0 || + git_indexer_stream_finalize(indexer, &stats) < 0) { + git_indexer_stream_free(indexer); + return -1; + } + + git_indexer_stream_free(indexer); + return 0; } #undef PREPARE_PACK From 9007c53faecf51be7f166172682008d16f7d617a Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 27 May 2013 16:45:22 -0700 Subject: [PATCH 287/384] Fixing unwrapped calloc --- src/delta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/delta.c b/src/delta.c index 3252dbf14..b3435ba87 100644 --- a/src/delta.c +++ b/src/delta.c @@ -168,7 +168,7 @@ git_delta_create_index(const void *buf, unsigned long bufsize) memset(hash, 0, hsize * sizeof(*hash)); /* allocate an array to count hash entries */ - hash_count = calloc(hsize, sizeof(*hash_count)); + hash_count = git__calloc(hsize, sizeof(*hash_count)); if (!hash_count) { git__free(index); return NULL; From 2638a03affdf57c989f573d48afca3b849cb4c1f Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 28 May 2013 17:50:47 +0200 Subject: [PATCH 288/384] This refs iterator pleases the gods. --- src/refdb_fs.c | 169 +++++++++++++++++++++++-------------------------- 1 file changed, 80 insertions(+), 89 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 7de56a1a0..97f0b07c4 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -29,7 +29,8 @@ GIT__USE_STRMAP; enum { PACKREF_HAS_PEEL = 1, PACKREF_WAS_LOOSE = 2, - PACKREF_CANNOT_PEEL = 4 + PACKREF_CANNOT_PEEL = 4, + PACKREF_SHADOWED = 8, }; enum { @@ -552,27 +553,16 @@ static int refdb_fs_backend__lookup( return result; } -struct dirent_list_data { - refdb_fs_backend *backend; - size_t repo_path_len; - unsigned int list_type:2; - - git_reference_foreach_cb callback; - void *callback_payload; - int callback_error; -}; - typedef struct { git_reference_iterator parent; - unsigned int loose; - /* packed */ - git_strmap *h; - khiter_t k; - /* loose */ - git_iterator *fsiter; - git_buf buf; + + git_vector loose; + unsigned int loose_pos; + khiter_t packed_pos; } refdb_fs_iter; +static int iter_load_loose_paths(refdb_fs_iter *iter); + static int refdb_fs_backend__iterator(git_reference_iterator **out, git_refdb_backend *_backend) { refdb_fs_iter *iter; @@ -588,103 +578,104 @@ static int refdb_fs_backend__iterator(git_reference_iterator **out, git_refdb_ba GITERR_CHECK_ALLOC(iter); iter->parent.backend = _backend; - iter->h = backend->refcache.packfile; - iter->k = kh_begin(backend->refcache.packfile); + iter_load_loose_paths(iter); *out = (git_reference_iterator *)iter; - return 0; } static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter) { refdb_fs_iter *iter = (refdb_fs_iter *) _iter; + char *loose_path; + size_t i; - git_buf_free(&iter->buf); - git_iterator_free(iter->fsiter); + git_vector_foreach(&iter->loose, i, loose_path) { + free(loose_path); + } + + git_vector_free(&iter->loose); git__free(iter); } -static int iter_packed(const char **out, refdb_fs_iter *iter) -{ - /* Move forward to the next entry */ - while (!kh_exist(iter->h, iter->k)) { - iter->k++; - if (iter->k == kh_end(iter->h)) - return GIT_ITEROVER; - } - - *out = kh_key(iter->h, iter->k); - iter->k++; - - return 0; -} - -static int iter_loose(const char **out, refdb_fs_iter *iter) -{ - const git_index_entry *entry; - int retry; - git_strmap *packfile_refs; - refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend; - - packfile_refs = backend->refcache.packfile; - - do { - khiter_t pos; - if (git_iterator_current(&entry, iter->fsiter) < 0) - return -1; - - git_buf_clear(&iter->buf); - if (!entry) - return GIT_ITEROVER; - - if (git_buf_printf(&iter->buf, "refs/%s", entry->path) < 0) - return -1; - - git_iterator_advance(NULL, iter->fsiter); - - /* Skip this one if we already listed it in packed */ - pos = git_strmap_lookup_index(packfile_refs, git_buf_cstr(&iter->buf)); - retry = 0; - if (git_strmap_valid_index(packfile_refs, pos) || - !git_reference_is_valid_name(git_buf_cstr(&iter->buf))) - retry = 1; - - *out = git_buf_cstr(&iter->buf); - } while (retry); - - return 0; -} - -static int iter_loose_setup(refdb_fs_iter *iter) +static int iter_load_loose_paths(refdb_fs_iter *iter) { refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend; + git_strmap *packfile = backend->refcache.packfile; - git_buf_clear(&iter->buf); - if (git_buf_printf(&iter->buf, "%s/refs", backend->path) < 0) + git_buf path = GIT_BUF_INIT; + git_iterator *fsit; + const git_index_entry *entry = NULL; + + if (git_buf_printf(&path, "%s/refs", backend->path) < 0) return -1; - return git_iterator_for_filesystem(&iter->fsiter, git_buf_cstr(&iter->buf), 0, NULL, NULL); + if (git_iterator_for_filesystem(&fsit, git_buf_cstr(&path), 0, NULL, NULL) < 0) + return -1; + + git_vector_init(&iter->loose, 8, NULL); + git_buf_sets(&path, GIT_REFS_DIR); + + while (!git_iterator_current(&entry, fsit) && entry) { + const char *ref_name; + khiter_t pos; + + git_buf_truncate(&path, strlen(GIT_REFS_DIR)); + git_buf_puts(&path, entry->path); + ref_name = git_buf_cstr(&path); + + if (git__suffixcmp(ref_name, ".lock") == 0) { + git_iterator_advance(NULL, fsit); + continue; + } + + pos = git_strmap_lookup_index(packfile, ref_name); + if (git_strmap_valid_index(packfile, pos)) { + struct packref *ref = git_strmap_value_at(packfile, pos); + ref->flags |= PACKREF_SHADOWED; + } + + git_vector_insert(&iter->loose, git__strdup(ref_name)); + git_iterator_advance(NULL, fsit); + } + + git_iterator_free(fsit); + git_buf_free(&path); + + return 0; } static int refdb_fs_backend__next(const char **out, git_reference_iterator *_iter) { refdb_fs_iter *iter = (refdb_fs_iter *)_iter; + refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend; + git_strmap *packfile = backend->refcache.packfile; - if (iter->loose) - return iter_loose(out, iter); - - if (iter->k != kh_end(iter->h)) { - int error = iter_packed(out, iter); - if (error != GIT_ITEROVER) - return error; + if (iter->loose_pos < iter->loose.length) { + const char *path = git_vector_get(&iter->loose, iter->loose_pos++); + *out = path; + return 0; } - if (iter_loose_setup(iter) < 0) - return -1; - iter->loose = 1; + if (iter->packed_pos < kh_end(packfile)) { + struct packref *ref = NULL; - return iter_loose(out, iter); + do { + while (!kh_exist(packfile, iter->packed_pos)) { + iter->packed_pos++; + if (iter->packed_pos == kh_end(packfile)) + return GIT_ITEROVER; + } + + ref = kh_val(packfile, iter->packed_pos); + iter->packed_pos++; + } while (ref->flags & PACKREF_SHADOWED); + + *out = ref->name; + return 0; + } + + return GIT_ITEROVER; } static int loose_write(refdb_fs_backend *backend, const git_reference *ref) From 56960b8396d3aef0b39f32aa7a9749202f925ada Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 28 May 2013 20:47:55 +0200 Subject: [PATCH 289/384] Liike this --- include/git2/refs.h | 15 ++++++---- include/git2/sys/refdb_backend.h | 2 +- src/branch.c | 17 ++++++----- src/refdb.c | 19 +++++++++---- src/refdb.h | 2 +- src/refdb_fs.c | 18 ++++++++---- src/refs.c | 2 +- src/remote.c | 49 +++++++++----------------------- tests-clar/refdb/testdb.c | 12 ++++++-- tests-clar/refs/iterator.c | 35 ++++++++++++----------- 10 files changed, 90 insertions(+), 81 deletions(-) diff --git a/include/git2/refs.h b/include/git2/refs.h index 468d0f930..4d9ec8344 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -351,7 +351,9 @@ GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2); * @param repo the repository * @return 0 or an error code */ -GIT_EXTERN(int) git_reference_iterator_new(git_reference_iterator **out, git_repository *repo); +GIT_EXTERN(int) git_reference_iterator_new( + git_reference_iterator **out, + git_repository *repo); /** * Create an iterator for the repo's references that match the @@ -362,16 +364,19 @@ GIT_EXTERN(int) git_reference_iterator_new(git_reference_iterator **out, git_rep * @param glob the glob to match against the reference names * @return 0 or an error code */ -GIT_EXTERN(int) git_reference_iterator_glob_new(git_reference_iterator **out, git_repository *repo, const char *glob); +GIT_EXTERN(int) git_reference_iterator_glob_new( + git_reference_iterator **out, + git_repository *repo, + const char *glob); /** - * Get the next reference name + * Get the next reference * - * @param out pointer in which to store the string + * @param out pointer in which to store the reference * @param iter the iterator * @param 0, GIT_ITEROVER if there are no more; or an error code */ -GIT_EXTERN(int) git_reference_next(const char **out, git_reference_iterator *iter); +GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter); /** * Free the iterator and its associated resources diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 548597fbc..0820cd9c5 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -85,7 +85,7 @@ struct git_refdb_backend { * A refdb implementation must provide this function. */ int (*next)( - const char **name, + git_reference **ref, git_reference_iterator *iter); /** diff --git a/src/branch.c b/src/branch.c index dd6dc68f4..84efadae1 100644 --- a/src/branch.c +++ b/src/branch.c @@ -131,28 +131,32 @@ int git_branch_foreach( void *payload) { git_reference_iterator *iter; - const char *name; + git_reference *ref; int error; if (git_reference_iterator_new(&iter, repo) < 0) return -1; - while ((error = git_reference_next(&name, iter)) == 0) { + while ((error = git_reference_next(&ref, iter)) == 0) { if (list_flags & GIT_BRANCH_LOCAL && - git__prefixcmp(name, GIT_REFS_HEADS_DIR) == 0) { - if (callback(name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, payload)) { + git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0) { + if (callback(ref->name + strlen(GIT_REFS_HEADS_DIR), + GIT_BRANCH_LOCAL, payload)) { error = GIT_EUSER; break; } } if (list_flags & GIT_BRANCH_REMOTE && - git__prefixcmp(name, GIT_REFS_REMOTES_DIR) == 0) { - if (callback(name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, payload)) { + git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) == 0) { + if (callback(ref->name + strlen(GIT_REFS_REMOTES_DIR), + GIT_BRANCH_REMOTE, payload)) { error = GIT_EUSER; break; } } + + git_reference_free(ref); } if (error == GIT_ITEROVER) @@ -160,7 +164,6 @@ int git_branch_foreach( git_reference_iterator_free(iter); return error; - } int git_branch_move( diff --git a/src/refdb.c b/src/refdb.c index 9f9037ce7..6cb879288 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -160,17 +160,26 @@ int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const c return 0; } -int git_refdb_next(const char **out, git_reference_iterator *iter) +int git_refdb_next(git_reference **out, git_reference_iterator *iter) { int error; - if (!iter->glob) - return iter->backend->next(out, iter); + if (!iter->glob) { + if ((error = iter->backend->next(out, iter)) < 0) + return error; + + (*out)->db = iter->backend; + return 0; + } /* If the iterator has a glob, we need to filter */ while ((error = iter->backend->next(out, iter)) == 0) { - if (!p_fnmatch(iter->glob, *out, 0)) - break; + if (!p_fnmatch(iter->glob, (*out)->name, 0)) { + (*out)->db = iter->backend; + return 0; + } + + git_reference_free(*out); } return error; diff --git a/src/refdb.h b/src/refdb.h index 2edd05d18..82522e191 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -28,7 +28,7 @@ int git_refdb_lookup( int git_refdb_iterator(git_reference_iterator **out, git_refdb *db); int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob); -int git_refdb_next(const char **out, git_reference_iterator *iter); +int git_refdb_next(git_reference **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); int git_refdb_write(git_refdb *refdb, const git_reference *ref); diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 97f0b07c4..457964570 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -645,16 +645,19 @@ static int iter_load_loose_paths(refdb_fs_iter *iter) return 0; } -static int refdb_fs_backend__next(const char **out, git_reference_iterator *_iter) +static int refdb_fs_backend__next(git_reference **out, git_reference_iterator *_iter) { refdb_fs_iter *iter = (refdb_fs_iter *)_iter; - refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend; + refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.backend; git_strmap *packfile = backend->refcache.packfile; - if (iter->loose_pos < iter->loose.length) { + while (iter->loose_pos < iter->loose.length) { const char *path = git_vector_get(&iter->loose, iter->loose_pos++); - *out = path; - return 0; + + if (loose_lookup(out, backend, path) == 0) + return 0; + + giterr_clear(); } if (iter->packed_pos < kh_end(packfile)) { @@ -671,7 +674,10 @@ static int refdb_fs_backend__next(const char **out, git_reference_iterator *_ite iter->packed_pos++; } while (ref->flags & PACKREF_SHADOWED); - *out = ref->name; + *out = git_reference__alloc(ref->name, &ref->oid, &ref->peel); + if (*out == NULL) + return -1; + return 0; } diff --git a/src/refs.c b/src/refs.c index 9c6c5c623..43231b0cf 100644 --- a/src/refs.c +++ b/src/refs.c @@ -664,7 +664,7 @@ int git_reference_iterator_glob_new(git_reference_iterator **out, git_repository return git_refdb_iterator_glob(out, refdb, glob); } -int git_reference_next(const char **out, git_reference_iterator *iter) +int git_reference_next(git_reference **out, git_reference_iterator *iter) { return git_refdb_next(out, iter); } diff --git a/src/remote.c b/src/remote.c index 7a64622a5..266a3d914 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1239,30 +1239,25 @@ static int update_branch_remote_config_entry( } static int rename_one_remote_reference( - git_repository *repo, - const char *reference_name, + git_reference *reference, const char *old_remote_name, const char *new_remote_name) { int error = -1; git_buf new_name = GIT_BUF_INIT; - git_reference *reference = NULL; git_reference *newref = NULL; if (git_buf_printf( &new_name, GIT_REFS_REMOTES_DIR "%s%s", new_remote_name, - reference_name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0) + reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0) return -1; - if (git_reference_lookup(&reference, repo, reference_name) < 0) - goto cleanup; - + /* TODO: can we make this NULL? */ error = git_reference_rename(&newref, reference, git_buf_cstr(&new_name), 0); git_reference_free(reference); -cleanup: git_reference_free(newref); git_buf_free(&new_name); return error; @@ -1273,46 +1268,28 @@ static int rename_remote_references( const char *old_name, const char *new_name) { - git_vector refnames; int error = -1; - unsigned int i; - char *name; - const char *refname; + git_reference *ref; git_reference_iterator *iter; - if (git_vector_init(&refnames, 8, NULL) < 0) + if (git_reference_iterator_new(&iter, repo) < 0) return -1; - if (git_reference_iterator_new(&iter, repo) < 0) - goto cleanup; - - while ((error = git_reference_next(&refname, iter)) == 0) { - if (git__prefixcmp(refname, GIT_REFS_REMOTES_DIR)) + while ((error = git_reference_next(&ref, iter)) == 0) { + if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) continue; - if ((error = git_vector_insert(&refnames, git__strdup(refname))) < 0) - break; - + if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) { + git_reference_iterator_free(iter); + return error; + } } git_reference_iterator_free(iter); + if (error == GIT_ITEROVER) - error = 0; - else - goto cleanup; + return 0; - git_vector_foreach(&refnames, i, name) { - if ((error = rename_one_remote_reference(repo, name, old_name, new_name)) < 0) - goto cleanup; - } - - error = 0; -cleanup: - git_vector_foreach(&refnames, i, name) { - git__free(name); - } - - git_vector_free(&refnames); return error; } diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index 961e18d44..c89bcce9f 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -134,7 +134,7 @@ static int refdb_test_backend__iterator(git_reference_iterator **out, git_refdb_ return 0; } -static int refdb_test_backend__next(const char **name, git_reference_iterator *_iter) +static int refdb_test_backend__next(git_reference **out, git_reference_iterator *_iter) { refdb_test_entry *entry; refdb_test_backend *backend = (refdb_test_backend *) _iter->backend; @@ -144,9 +144,15 @@ static int refdb_test_backend__next(const char **name, git_reference_iterator *_ if (!entry) return GIT_ITEROVER; - *name = entry->name; - iter->i++; + if (entry->type == GIT_REF_OID) { + *out = git_reference__alloc(entry->name, &entry->target.oid, NULL); + } else if (entry->type == GIT_REF_SYMBOLIC) { + *out = git_reference__alloc_symbolic(entry->name, entry->target.symbolic); + } else { + return -1; + } + iter->i++; return 0; } diff --git a/tests-clar/refs/iterator.c b/tests-clar/refs/iterator.c index d5555c657..7a966892b 100644 --- a/tests-clar/refs/iterator.c +++ b/tests-clar/refs/iterator.c @@ -38,40 +38,43 @@ static const char *refnames[] = { "refs/tags/wrapped_tag", }; +static int refcmp_cb(const void *a, const void *b) +{ + const git_reference *refa = (const git_reference *)a; + const git_reference *refb = (const git_reference *)b; + + return strcmp(refa->name, refb->name); +} + void test_refs_iterator__list(void) { git_reference_iterator *iter; git_vector output; - char *refname; + git_reference *ref; int error; size_t i; - cl_git_pass(git_vector_init(&output, 32, git__strcmp_cb)); + cl_git_pass(git_vector_init(&output, 32, &refcmp_cb)); cl_git_pass(git_reference_iterator_new(&iter, repo)); do { - const char *name; - error = git_reference_next(&name, iter); + error = git_reference_next(&ref, iter); cl_assert(error == 0 || error == GIT_ITEROVER); if (error != GIT_ITEROVER) { - char *dup = git__strdup(name); - cl_assert(dup != NULL); - cl_git_pass(git_vector_insert(&output, dup)); + cl_git_pass(git_vector_insert(&output, ref)); } } while (!error); + git_reference_iterator_free(iter); cl_assert_equal_i(output.length, ARRAY_SIZE(refnames)); git_vector_sort(&output); - git_vector_foreach(&output, i, refname) { - cl_assert_equal_s(refname, refnames[i]); + + git_vector_foreach(&output, i, ref) { + cl_assert_equal_s(ref->name, refnames[i]); + git_reference_free(ref); } - git_reference_iterator_free(iter); - - git_vector_foreach(&output, i, refname) { - git__free(refname); - } git_vector_free(&output); } @@ -79,14 +82,14 @@ void test_refs_iterator__empty(void) { git_reference_iterator *iter; git_odb *odb; - const char *name; + git_reference *ref; git_repository *empty; cl_git_pass(git_odb_new(&odb)); cl_git_pass(git_repository_wrap_odb(&empty, odb)); cl_git_pass(git_reference_iterator_new(&iter, empty)); - cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&name, iter)); + cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&ref, iter)); git_reference_iterator_free(iter); git_odb_free(odb); From 31a6118175a2a458f88211999952770949e3924c Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Wed, 29 May 2013 00:02:26 +0300 Subject: [PATCH 290/384] Fix two typos in the merge docs. --- include/git2/merge.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/git2/merge.h b/include/git2/merge.h index af8f36063..ce3ed8ed2 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -23,8 +23,8 @@ GIT_BEGIN_DECL /** - * Flags for `git_mrege_tree` options. A combination of these flags can be - * passed in via the `flags` vlaue in the `git_merge_tree_opts`. + * Flags for `git_merge_tree` options. A combination of these flags can be + * passed in via the `flags` value in the `git_merge_tree_opts`. */ typedef enum { /** Detect renames */ From ca9b1702abd79486aabfb9dae108c0bf1c26ccfd Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 29 May 2013 09:18:21 -0700 Subject: [PATCH 291/384] Fix memory leak in oid shortener tests --- tests-clar/object/raw/short.c | 110 ++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/tests-clar/object/raw/short.c b/tests-clar/object/raw/short.c index b4019936a..813cd86b6 100644 --- a/tests-clar/object/raw/short.c +++ b/tests-clar/object/raw/short.c @@ -22,17 +22,55 @@ void test_object_raw_short__oid_shortener_no_duplicates(void) git_oid_shorten_free(os); } +static int insert_sequential_oids( + char ***out, git_oid_shorten *os, int n, int fail) +{ + int i, min_len = 0; + char numbuf[16]; + git_oid oid; + char **oids = git__calloc(n, sizeof(char *)); + cl_assert(oids != NULL); + + for (i = 0; i < n; ++i) { + p_snprintf(numbuf, sizeof(numbuf), "%u", (unsigned int)i); + git_hash_buf(&oid, numbuf, strlen(numbuf)); + + oids[i] = git__malloc(GIT_OID_HEXSZ + 1); + cl_assert(oids[i]); + git_oid_nfmt(oids[i], GIT_OID_HEXSZ + 1, &oid); + + min_len = git_oid_shorten_add(os, oids[i]); + + /* After "fail", we expect git_oid_shorten_add to fail */ + if (fail >= 0 && i >= fail) + cl_assert(min_len < 0); + else + cl_assert(min_len >= 0); + } + + *out = oids; + + return min_len; +} + +static void free_oids(int n, char **oids) +{ + int i; + + for (i = 0; i < n; ++i) { + git__free(oids[i]); + } + git__free(oids); +} + void test_object_raw_short__oid_shortener_stresstest_git_oid_shorten(void) { #define MAX_OIDS 1000 git_oid_shorten *os; - char *oids[MAX_OIDS]; - char number_buffer[16]; - git_oid oid; size_t i, j; - int min_len = 0, found_collision; + char **oids; os = git_oid_shorten_new(0); cl_assert(os != NULL); @@ -40,21 +78,8 @@ void test_object_raw_short__oid_shortener_stresstest_git_oid_shorten(void) /* * Insert in the shortener 1000 unique SHA1 ids */ - for (i = 0; i < MAX_OIDS; ++i) { - char *oid_text; - - p_snprintf(number_buffer, 16, "%u", (unsigned int)i); - git_hash_buf(&oid, number_buffer, strlen(number_buffer)); - - oid_text = git__malloc(GIT_OID_HEXSZ + 1); - git_oid_fmt(oid_text, &oid); - oid_text[GIT_OID_HEXSZ] = 0; - - min_len = git_oid_shorten_add(os, oid_text); - cl_assert(min_len >= 0); - - oids[i] = oid_text; - } + min_len = insert_sequential_oids(&oids, os, MAX_OIDS, MAX_OIDS); + cl_assert(min_len > 0); /* * Compare the first `min_char - 1` characters of each @@ -63,12 +88,12 @@ void test_object_raw_short__oid_shortener_stresstest_git_oid_shorten(void) */ found_collision = 0; for (i = 0; i < MAX_OIDS; ++i) { - for (j = 0; j < MAX_OIDS; ++j) { - if (i != j && memcmp(oids[i], oids[j], min_len - 1) == 0) + for (j = i + 1; j < MAX_OIDS; ++j) { + if (memcmp(oids[i], oids[j], min_len - 1) == 0) found_collision = 1; } } - cl_assert(found_collision == 1); + cl_assert_equal_b(true, found_collision); /* * Compare the first `min_char` characters of each @@ -77,57 +102,36 @@ void test_object_raw_short__oid_shortener_stresstest_git_oid_shorten(void) */ found_collision = 0; for (i = 0; i < MAX_OIDS; ++i) { - for (j = 0; j < MAX_OIDS; ++j) { - if (i != j && memcmp(oids[i], oids[j], min_len) == 0) + for (j = i + 1; j < MAX_OIDS; ++j) { + if (memcmp(oids[i], oids[j], min_len) == 0) found_collision = 1; } } - cl_assert(found_collision == 0); + cl_assert_equal_b(false, found_collision); /* cleanup */ - for (i = 0; i < MAX_OIDS; ++i) - git__free(oids[i]); - + free_oids(MAX_OIDS, oids); git_oid_shorten_free(os); #undef MAX_OIDS } -void test_object_raw_short__oid_shortener_too_much_oids(void) { +void test_object_raw_short__oid_shortener_too_much_oids(void) +{ /* The magic number of oids at which an oid_shortener will fail. * This was experimentally established. */ #define MAX_OIDS 24556 git_oid_shorten *os; - char number_buffer[16]; - git_oid oid; - size_t i; - - int min_len = 0; + char **oids; os = git_oid_shorten_new(0); cl_assert(os != NULL); - for (i = 0; i < MAX_OIDS; ++i) { - char *oid_text; - - p_snprintf(number_buffer, 16, "%u", (unsigned int)i); - git_hash_buf(&oid, number_buffer, strlen(number_buffer)); - - oid_text = git__malloc(GIT_OID_HEXSZ + 1); - git_oid_fmt(oid_text, &oid); - oid_text[GIT_OID_HEXSZ] = 0; - - min_len = git_oid_shorten_add(os, oid_text); - /* All but the last oid should give a non-negative min_len. At the - * last oid, git_oid_shorten_add should fail, returning a negative - * value */ - if (i < MAX_OIDS - 1) - cl_assert(min_len >= 0); - else - cl_assert(min_len < 0); - } + cl_assert(insert_sequential_oids(&oids, os, MAX_OIDS, MAX_OIDS - 1) < 0); + free_oids(MAX_OIDS, oids); git_oid_shorten_free(os); +#undef MAX_OIDS } From ec24e542969f9d49e41e4c2cb3eac2259b1818c2 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 29 May 2013 22:47:37 +0200 Subject: [PATCH 292/384] What are the chances, really --- include/git2/refs.h | 12 +- include/git2/sys/refdb_backend.h | 49 +++---- src/clone.c | 2 +- src/refdb.c | 84 ++++-------- src/refdb.h | 7 +- src/refdb_fs.c | 132 +++++++++++++------ src/refs.c | 125 +++++++++++------- src/repository.c | 4 +- src/tag.c | 2 +- tests-clar/refdb/inmemory.c | 217 ------------------------------- tests-clar/refdb/testdb.c | 52 -------- 11 files changed, 236 insertions(+), 450 deletions(-) delete mode 100644 tests-clar/refdb/inmemory.c diff --git a/include/git2/refs.h b/include/git2/refs.h index 4d9ec8344..44b658d5b 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -308,7 +308,8 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref); */ GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo); -typedef int (*git_reference_foreach_cb)(const char *refname, void *payload); +typedef int (*git_reference_foreach_cb)(git_reference *reference, void *payload); +typedef int (*git_reference_foreach_name_cb)(const char *name, void *payload); /** * Perform a callback on each reference in the repository. @@ -328,6 +329,11 @@ GIT_EXTERN(int) git_reference_foreach( git_reference_foreach_cb callback, void *payload); +GIT_EXTERN(int) git_reference_foreach_name( + git_repository *repo, + git_reference_foreach_name_cb callback, + void *payload); + /** * Free the given reference. * @@ -378,6 +384,8 @@ GIT_EXTERN(int) git_reference_iterator_glob_new( */ GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter); +GIT_EXTERN(int) git_reference_next_name(const char **out, git_reference_iterator *iter); + /** * Free the iterator and its associated resources * @@ -406,7 +414,7 @@ GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter); GIT_EXTERN(int) git_reference_foreach_glob( git_repository *repo, const char *glob, - git_reference_foreach_cb callback, + git_reference_foreach_name_cb callback, void *payload); /** diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 0820cd9c5..a78d22658 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -33,8 +33,27 @@ GIT_BEGIN_DECL * and assign `iter->parent.backend` to your `git_refdb_backend`. */ struct git_reference_iterator { - git_refdb_backend *backend; - char *glob; + git_refdb *db; + + /** + * Return the current reference and advance the iterator. + */ + int (*next)( + git_reference **ref, + git_reference_iterator *iter); + + /** + * Return the name of the current reference and advance the iterator + */ + int (*next_name)( + const char **ref_name, + git_reference_iterator *iter); + + /** + * Free the iterator + */ + void (*free)( + git_reference_iterator *iter); }; /** An instance for a custom backend */ @@ -65,36 +84,10 @@ struct git_refdb_backend { * A refdb implementation must provide this function. */ int (*iterator)( - git_reference_iterator **iter, - struct git_refdb_backend *backend); - - /** - * Allocate a glob-filtering iterator object for the backend. - * - * A refdb implementation may provide this function. If it's - * not available, the glob matching will be done by the frontend. - */ - int (*iterator_glob)( git_reference_iterator **iter, struct git_refdb_backend *backend, const char *glob); - /** - * Return the current value and advance the iterator. - * - * A refdb implementation must provide this function. - */ - int (*next)( - git_reference **ref, - git_reference_iterator *iter); - - /** - * Free the iterator - * - * A refdb implementation must provide this function. - */ - void (*iterator_free)( - git_reference_iterator *iter); /* * Writes the given reference to the refdb. A refdb implementation * must provide this function. diff --git a/src/clone.c b/src/clone.c index 7ebdb5765..72906c3ce 100644 --- a/src/clone.c +++ b/src/clone.c @@ -241,7 +241,7 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote) } /* Not master. Check all the other refs. */ - if (git_reference_foreach( + if (git_reference_foreach_name( repo, reference_matches_remote_head, &head_info) < 0) diff --git a/src/refdb.c b/src/refdb.c index 6cb879288..e0701d347 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -114,99 +114,65 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name) assert(db && db->backend && out && ref_name); - if (!(error = db->backend->lookup(&ref, db->backend, ref_name))) { - ref->db = db; - *out = ref; - } else { - *out = NULL; - } + error = db->backend->lookup(&ref, db->backend, ref_name); + if (error < 0) + return error; - return error; + GIT_REFCOUNT_INC(db); + ref->db = db; + + *out = ref; + return 0; } -int git_refdb_iterator(git_reference_iterator **out, git_refdb *db) +int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob) { if (!db->backend || !db->backend->iterator) { giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators"); return -1; } - if (db->backend->iterator(out, db->backend) < 0) + if (db->backend->iterator(out, db->backend, glob) < 0) return -1; + GIT_REFCOUNT_INC(db); + (*out)->db = db; + return 0; } -int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob) -{ - if (!db->backend) { - giterr_set(GITERR_REFERENCE, "There are no backends loaded"); - return -1; - } - - if (db->backend->iterator_glob) - return db->backend->iterator_glob(out, db->backend, glob); - - /* If the backend doesn't support glob-filtering themselves, we have to do it */ - if (db->backend->iterator(out, db->backend) < 0) - return -1; - - (*out)->glob = git__strdup(glob); - if (!(*out)->glob) { - db->backend->iterator_free(*out); - return -1; - } - - return 0; -} - -int git_refdb_next(git_reference **out, git_reference_iterator *iter) +int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter) { int error; - if (!iter->glob) { - if ((error = iter->backend->next(out, iter)) < 0) - return error; + if ((error = iter->next(out, iter)) < 0) + return error; - (*out)->db = iter->backend; - return 0; - } + GIT_REFCOUNT_INC(iter->db); + (*out)->db = iter->db; - /* If the iterator has a glob, we need to filter */ - while ((error = iter->backend->next(out, iter)) == 0) { - if (!p_fnmatch(iter->glob, (*out)->name, 0)) { - (*out)->db = iter->backend; - return 0; - } + return 0; +} - git_reference_free(*out); - } - - return error; +int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter) +{ + return iter->next_name(out, iter); } void git_refdb_iterator_free(git_reference_iterator *iter) { - git__free(iter->glob); - iter->backend->iterator_free(iter); + GIT_REFCOUNT_DEC(iter->db, refdb_free); + iter->free(iter); } -struct glob_cb_data { - const char *glob; - git_reference_foreach_cb callback; - void *payload; -}; - int git_refdb_write(git_refdb *db, const git_reference *ref) { assert(db && db->backend); - return db->backend->write(db->backend, ref); } int git_refdb_delete(struct git_refdb *db, const git_reference *ref) { assert(db && db->backend); - return db->backend->delete(db->backend, ref); } diff --git a/src/refdb.h b/src/refdb.h index 82522e191..1dcd70da8 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -26,13 +26,12 @@ int git_refdb_lookup( git_refdb *refdb, const char *ref_name); -int git_refdb_iterator(git_reference_iterator **out, git_refdb *db); -int git_refdb_iterator_glob(git_reference_iterator **out, git_refdb *db, const char *glob); -int git_refdb_next(git_reference **out, git_reference_iterator *iter); +int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob); +int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter); +int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); int git_refdb_write(git_refdb *refdb, const git_reference *ref); - int git_refdb_delete(git_refdb *refdb, const git_reference *ref); #endif diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 457964570..ecd033de0 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -556,34 +556,12 @@ static int refdb_fs_backend__lookup( typedef struct { git_reference_iterator parent; + char *glob; git_vector loose; unsigned int loose_pos; khiter_t packed_pos; } refdb_fs_iter; -static int iter_load_loose_paths(refdb_fs_iter *iter); - -static int refdb_fs_backend__iterator(git_reference_iterator **out, git_refdb_backend *_backend) -{ - refdb_fs_iter *iter; - refdb_fs_backend *backend; - - assert(_backend); - backend = (refdb_fs_backend *)_backend; - - if (packed_load(backend) < 0) - return -1; - - iter = git__calloc(1, sizeof(refdb_fs_iter)); - GITERR_CHECK_ALLOC(iter); - - iter->parent.backend = _backend; - iter_load_loose_paths(iter); - - *out = (git_reference_iterator *)iter; - return 0; -} - static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter) { refdb_fs_iter *iter = (refdb_fs_iter *) _iter; @@ -595,14 +573,14 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter) } git_vector_free(&iter->loose); + + git__free(iter->glob); git__free(iter); } -static int iter_load_loose_paths(refdb_fs_iter *iter) +static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) { - refdb_fs_backend *backend = (refdb_fs_backend *) iter->parent.backend; git_strmap *packfile = backend->refcache.packfile; - git_buf path = GIT_BUF_INIT; git_iterator *fsit; const git_index_entry *entry = NULL; @@ -624,7 +602,8 @@ static int iter_load_loose_paths(refdb_fs_iter *iter) git_buf_puts(&path, entry->path); ref_name = git_buf_cstr(&path); - if (git__suffixcmp(ref_name, ".lock") == 0) { + if (git__suffixcmp(ref_name, ".lock") == 0 || + (iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0)) { git_iterator_advance(NULL, fsit); continue; } @@ -645,10 +624,11 @@ static int iter_load_loose_paths(refdb_fs_iter *iter) return 0; } -static int refdb_fs_backend__next(git_reference **out, git_reference_iterator *_iter) +static int refdb_fs_backend__iterator_next( + git_reference **out, git_reference_iterator *_iter) { refdb_fs_iter *iter = (refdb_fs_iter *)_iter; - refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.backend; + refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend; git_strmap *packfile = backend->refcache.packfile; while (iter->loose_pos < iter->loose.length) { @@ -660,19 +640,23 @@ static int refdb_fs_backend__next(git_reference **out, git_reference_iterator *_ giterr_clear(); } - if (iter->packed_pos < kh_end(packfile)) { + while (iter->packed_pos < kh_end(packfile)) { struct packref *ref = NULL; - do { - while (!kh_exist(packfile, iter->packed_pos)) { - iter->packed_pos++; - if (iter->packed_pos == kh_end(packfile)) - return GIT_ITEROVER; - } - - ref = kh_val(packfile, iter->packed_pos); + while (!kh_exist(packfile, iter->packed_pos)) { iter->packed_pos++; - } while (ref->flags & PACKREF_SHADOWED); + if (iter->packed_pos == kh_end(packfile)) + return GIT_ITEROVER; + } + + ref = kh_val(packfile, iter->packed_pos); + iter->packed_pos++; + + if (ref->flags & PACKREF_SHADOWED) + continue; + + if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0) + continue; *out = git_reference__alloc(ref->name, &ref->oid, &ref->peel); if (*out == NULL) @@ -684,6 +668,74 @@ static int refdb_fs_backend__next(git_reference **out, git_reference_iterator *_ return GIT_ITEROVER; } +static int refdb_fs_backend__iterator_next_name( + const char **out, git_reference_iterator *_iter) +{ + refdb_fs_iter *iter = (refdb_fs_iter *)_iter; + refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend; + git_strmap *packfile = backend->refcache.packfile; + + while (iter->loose_pos < iter->loose.length) { + const char *path = git_vector_get(&iter->loose, iter->loose_pos++); + + if (git_strmap_exists(packfile, path)) + continue; + + *out = path; + return 0; + } + + while (iter->packed_pos < kh_end(packfile)) { + while (!kh_exist(packfile, iter->packed_pos)) { + iter->packed_pos++; + if (iter->packed_pos == kh_end(packfile)) + return GIT_ITEROVER; + } + + *out = kh_key(packfile, iter->packed_pos); + iter->packed_pos++; + + if (iter->glob && p_fnmatch(iter->glob, *out, 0) != 0) + continue; + + return 0; + } + + return GIT_ITEROVER; +} + +static int refdb_fs_backend__iterator( + git_reference_iterator **out, git_refdb_backend *_backend, const char *glob) +{ + refdb_fs_iter *iter; + refdb_fs_backend *backend; + + assert(_backend); + backend = (refdb_fs_backend *)_backend; + + if (packed_load(backend) < 0) + return -1; + + iter = git__calloc(1, sizeof(refdb_fs_iter)); + GITERR_CHECK_ALLOC(iter); + + if (glob != NULL) + iter->glob = git__strdup(glob); + + iter->parent.next = refdb_fs_backend__iterator_next; + iter->parent.next_name = refdb_fs_backend__iterator_next_name; + iter->parent.free = refdb_fs_backend__iterator_free; + + if (iter_load_loose_paths(backend, iter) < 0) { + refdb_fs_backend__iterator_free((git_reference_iterator *)iter); + return -1; + } + + *out = (git_reference_iterator *)iter; + return 0; +} + + static int loose_write(refdb_fs_backend *backend, const git_reference *ref) { git_filebuf file = GIT_FILEBUF_INIT; @@ -1118,8 +1170,6 @@ int git_refdb_backend_fs( backend->parent.exists = &refdb_fs_backend__exists; backend->parent.lookup = &refdb_fs_backend__lookup; backend->parent.iterator = &refdb_fs_backend__iterator; - backend->parent.next = &refdb_fs_backend__next; - backend->parent.iterator_free = &refdb_fs_backend__iterator_free; backend->parent.write = &refdb_fs_backend__write; backend->parent.delete = &refdb_fs_backend__delete; backend->parent.compress = &refdb_fs_backend__compress; diff --git a/src/refs.c b/src/refs.c index 43231b0cf..a8583de19 100644 --- a/src/refs.c +++ b/src/refs.c @@ -104,20 +104,20 @@ struct reference_available_t { int available; }; -static int _reference_available_cb(const char *ref, void *data) +static int _reference_available_cb(const char *refname, void *data) { struct reference_available_t *d; - assert(ref && data); + assert(refname && data); d = (struct reference_available_t *)data; - if (!d->old_ref || strcmp(d->old_ref, ref)) { - size_t reflen = strlen(ref); + if (!d->old_ref || strcmp(d->old_ref, refname)) { + size_t reflen = strlen(refname); size_t newlen = strlen(d->new_ref); size_t cmplen = reflen < newlen ? reflen : newlen; - const char *lead = reflen < newlen ? d->new_ref : ref; + const char *lead = reflen < newlen ? d->new_ref : refname; - if (!strncmp(d->new_ref, ref, cmplen) && lead[cmplen] == '/') { + if (!strncmp(d->new_ref, refname, cmplen) && lead[cmplen] == '/') { d->available = 0; return -1; } @@ -126,6 +126,9 @@ static int _reference_available_cb(const char *ref, void *data) return 0; } +/** + * TODO: this should be part of the FS backend + */ static int reference_path_available( git_repository *repo, const char *ref, @@ -138,8 +141,7 @@ static int reference_path_available( data.old_ref = old_ref; data.available = 1; - error = git_reference_foreach( - repo, _reference_available_cb, (void *)&data); + error = git_reference_foreach_name(repo, _reference_available_cb, (void *)&data); if (error < 0) return error; @@ -430,6 +432,7 @@ static int reference__create( ref = git_reference__alloc_symbolic(name, symbolic); } + /* TODO: this needs to be written more explicitly */ GITERR_CHECK_ALLOC(ref); ref->db = refdb; @@ -558,6 +561,7 @@ int git_reference_rename( if (result == NULL) return -1; + /* TODO: this is bad */ result->db = ref->db; /* Check if we have to update HEAD. */ @@ -623,14 +627,69 @@ int git_reference_foreach( void *payload) { git_reference_iterator *iter; - const char *name; + git_reference *ref; int error; if (git_reference_iterator_new(&iter, repo) < 0) return -1; - while ((error = git_reference_next(&name, iter)) == 0) { - if (callback(name, payload)) { + while ((error = git_reference_next(&ref, iter)) == 0) { + if (callback(ref, payload)) { + error = GIT_EUSER; + goto out; + } + } + + if (error == GIT_ITEROVER) + error = 0; + +out: + git_reference_iterator_free(iter); + return error; +} + +int git_reference_foreach_name( + git_repository *repo, + git_reference_foreach_name_cb callback, + void *payload) +{ + git_reference_iterator *iter; + const char *refname; + int error; + + if (git_reference_iterator_new(&iter, repo) < 0) + return -1; + + while ((error = git_reference_next_name(&refname, iter)) == 0) { + if (callback(refname, payload)) { + error = GIT_EUSER; + goto out; + } + } + + if (error == GIT_ITEROVER) + error = 0; + +out: + git_reference_iterator_free(iter); + return error; +} + +int git_reference_foreach_glob( + git_repository *repo, + const char *glob, + git_reference_foreach_name_cb callback, + void *payload) +{ + git_reference_iterator *iter; + const char *refname; + int error; + + if (git_reference_iterator_glob_new(&iter, repo, glob) < 0) + return -1; + + while ((error = git_reference_next_name(&refname, iter)) == 0) { + if (callback(refname, payload)) { error = GIT_EUSER; goto out; } @@ -651,22 +710,28 @@ int git_reference_iterator_new(git_reference_iterator **out, git_repository *rep if (git_repository_refdb__weakptr(&refdb, repo) < 0) return -1; - return git_refdb_iterator(out, refdb); + return git_refdb_iterator(out, refdb, NULL); } -int git_reference_iterator_glob_new(git_reference_iterator **out, git_repository *repo, const char *glob) +int git_reference_iterator_glob_new( + git_reference_iterator **out, git_repository *repo, const char *glob) { git_refdb *refdb; if (git_repository_refdb__weakptr(&refdb, repo) < 0) return -1; - return git_refdb_iterator_glob(out, refdb, glob); + return git_refdb_iterator(out, refdb, glob); } int git_reference_next(git_reference **out, git_reference_iterator *iter) { - return git_refdb_next(out, iter); + return git_refdb_iterator_next(out, iter); +} + +int git_reference_next_name(const char **out, git_reference_iterator *iter) +{ + return git_refdb_iterator_next_name(out, iter); } void git_reference_iterator_free(git_reference_iterator *iter) @@ -693,7 +758,7 @@ int git_reference_list( if (git_vector_init(&ref_list, 8, NULL) < 0) return -1; - if (git_reference_foreach( + if (git_reference_foreach_name( repo, &cb__reflist_add, (void *)&ref_list) < 0) { git_vector_free(&ref_list); return -1; @@ -991,34 +1056,6 @@ int git_reference__update_terminal( return reference__update_terminal(repo, ref_name, oid, 0); } -int git_reference_foreach_glob( - git_repository *repo, - const char *glob, - git_reference_foreach_cb callback, - void *payload) -{ - git_reference_iterator *iter; - const char *name; - int error; - - if (git_reference_iterator_glob_new(&iter, repo, glob) < 0) - return -1; - - while ((error = git_reference_next(&name, iter)) == 0) { - if (callback(name, payload)) { - error = GIT_EUSER; - goto out; - } - } - - if (error == GIT_ITEROVER) - error = 0; - -out: - git_reference_iterator_free(iter); - return error; -} - int git_reference_has_log( git_reference *ref) { diff --git a/src/repository.c b/src/repository.c index 28505e822..2e7a334c9 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1473,12 +1473,14 @@ static int at_least_one_cb(const char *refname, void *payload) static int repo_contains_no_reference(git_repository *repo) { - int error = git_reference_foreach(repo, at_least_one_cb, NULL); + int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL); if (error == GIT_EUSER) return 0; + if (!error) return 1; + return error; } diff --git a/src/tag.c b/src/tag.c index ecf3876cb..71f4c1eb1 100644 --- a/src/tag.c +++ b/src/tag.c @@ -440,7 +440,7 @@ int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) data.cb_data = cb_data; data.repo = repo; - return git_reference_foreach(repo, &tags_cb, &data); + return git_reference_foreach_name(repo, &tags_cb, &data); } typedef struct { diff --git a/tests-clar/refdb/inmemory.c b/tests-clar/refdb/inmemory.c deleted file mode 100644 index d2594cd6d..000000000 --- a/tests-clar/refdb/inmemory.c +++ /dev/null @@ -1,217 +0,0 @@ -#include "clar_libgit2.h" - -#include "buffer.h" -#include "posix.h" -#include "path.h" -#include "refs.h" - -#include "testdb.h" - -#define TEST_REPO_PATH "testrepo" - -static git_repository *repo; - -int unlink_ref(void *payload, git_buf *file) -{ - GIT_UNUSED(payload); - return p_unlink(git_buf_cstr(file)); -} - -int empty(void *payload, git_buf *file) -{ - GIT_UNUSED(payload); - GIT_UNUSED(file); - return -1; -} - -int ref_file_foreach(git_repository *repo, int (* cb)(void *payload, git_buf *filename)) -{ - const char *repo_path; - git_buf repo_refs_dir = GIT_BUF_INIT; - int error = 0; - - repo_path = git_repository_path(repo); - - git_buf_joinpath(&repo_refs_dir, repo_path, "HEAD"); - if (git_path_exists(git_buf_cstr(&repo_refs_dir)) && - cb(NULL, &repo_refs_dir) < 0) - return -1; - - git_buf_joinpath(&repo_refs_dir, repo_path, "refs"); - git_buf_joinpath(&repo_refs_dir, git_buf_cstr(&repo_refs_dir), "heads"); - if (git_path_direach(&repo_refs_dir, cb, NULL) != 0) - return -1; - - git_buf_joinpath(&repo_refs_dir, repo_path, "packed-refs"); - if (git_path_exists(git_buf_cstr(&repo_refs_dir)) && - cb(NULL, &repo_refs_dir) < 0) - return -1; - - git_buf_free(&repo_refs_dir); - - return error; -} - -void test_refdb_inmemory__initialize(void) -{ - git_buf repo_refs_dir = GIT_BUF_INIT; - git_refdb *refdb; - git_refdb_backend *refdb_backend; - - repo = cl_git_sandbox_init(TEST_REPO_PATH); - - cl_git_pass(git_repository_refdb(&refdb, repo)); - cl_git_pass(refdb_backend_test(&refdb_backend, repo)); - cl_git_pass(git_refdb_set_backend(refdb, refdb_backend)); - - ref_file_foreach(repo, unlink_ref); - - git_buf_free(&repo_refs_dir); - git_refdb_free(refdb); -} - -void test_refdb_inmemory__cleanup(void) -{ - cl_git_sandbox_cleanup(); -} - -void test_refdb_inmemory__doesnt_write_ref_file(void) -{ - git_reference *ref; - git_oid oid; - - cl_git_pass(git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd")); - cl_git_pass(git_reference_create(&ref, repo, GIT_REFS_HEADS_DIR "test1", &oid, 0)); - - ref_file_foreach(repo, empty); - - git_reference_free(ref); -} - -void test_refdb_inmemory__read(void) -{ - git_reference *write1, *write2, *write3, *read1, *read2, *read3; - git_oid oid1, oid2, oid3; - - cl_git_pass(git_oid_fromstr(&oid1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); - cl_git_pass(git_reference_create(&write1, repo, GIT_REFS_HEADS_DIR "test1", &oid1, 0)); - - cl_git_pass(git_oid_fromstr(&oid2, "e90810b8df3e80c413d903f631643c716887138d")); - cl_git_pass(git_reference_create(&write2, repo, GIT_REFS_HEADS_DIR "test2", &oid2, 0)); - - cl_git_pass(git_oid_fromstr(&oid3, "763d71aadf09a7951596c9746c024e7eece7c7af")); - cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0)); - - - cl_git_pass(git_reference_lookup(&read1, repo, GIT_REFS_HEADS_DIR "test1")); - cl_assert(strcmp(git_reference_name(read1), git_reference_name(write1)) == 0); - cl_assert(git_oid_cmp(git_reference_target(read1), git_reference_target(write1)) == 0); - - cl_git_pass(git_reference_lookup(&read2, repo, GIT_REFS_HEADS_DIR "test2")); - cl_assert(strcmp(git_reference_name(read2), git_reference_name(write2)) == 0); - cl_assert(git_oid_cmp(git_reference_target(read2), git_reference_target(write2)) == 0); - - cl_git_pass(git_reference_lookup(&read3, repo, GIT_REFS_HEADS_DIR "test3")); - cl_assert(strcmp(git_reference_name(read3), git_reference_name(write3)) == 0); - cl_assert(git_oid_cmp(git_reference_target(read3), git_reference_target(write3)) == 0); - - git_reference_free(write1); - git_reference_free(write2); - git_reference_free(write3); - - git_reference_free(read1); - git_reference_free(read2); - git_reference_free(read3); -} - -int foreach_test(const char *ref_name, void *payload) -{ - git_reference *ref; - git_oid expected; - size_t *i = payload; - - cl_git_pass(git_reference_lookup(&ref, repo, ref_name)); - - if (*i == 0) - cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd")); - else if (*i == 1) - cl_git_pass(git_oid_fromstr(&expected, "e90810b8df3e80c413d903f631643c716887138d")); - else if (*i == 2) - cl_git_pass(git_oid_fromstr(&expected, "763d71aadf09a7951596c9746c024e7eece7c7af")); - - cl_assert(git_oid_cmp(&expected, git_reference_target(ref)) == 0); - - ++(*i); - - git_reference_free(ref); - - return 0; -} - -void test_refdb_inmemory__foreach(void) -{ - git_reference *write1, *write2, *write3; - git_oid oid1, oid2, oid3; - size_t i = 0; - - cl_git_pass(git_oid_fromstr(&oid1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); - cl_git_pass(git_reference_create(&write1, repo, GIT_REFS_HEADS_DIR "test1", &oid1, 0)); - - cl_git_pass(git_oid_fromstr(&oid2, "e90810b8df3e80c413d903f631643c716887138d")); - cl_git_pass(git_reference_create(&write2, repo, GIT_REFS_HEADS_DIR "test2", &oid2, 0)); - - cl_git_pass(git_oid_fromstr(&oid3, "763d71aadf09a7951596c9746c024e7eece7c7af")); - cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0)); - - cl_git_pass(git_reference_foreach(repo,foreach_test, &i)); - cl_assert_equal_i(3, (int)i); - - git_reference_free(write1); - git_reference_free(write2); - git_reference_free(write3); -} - -int delete_test(const char *ref_name, void *payload) -{ - git_reference *ref; - git_oid expected; - size_t *i = payload; - - cl_git_pass(git_reference_lookup(&ref, repo, ref_name)); - - cl_git_pass(git_oid_fromstr(&expected, "e90810b8df3e80c413d903f631643c716887138d")); - cl_assert(git_oid_cmp(&expected, git_reference_target(ref)) == 0); - - ++(*i); - - git_reference_free(ref); - - return 0; -} - -void test_refdb_inmemory__delete(void) -{ - git_reference *write1, *write2, *write3; - git_oid oid1, oid2, oid3; - size_t i = 0; - - cl_git_pass(git_oid_fromstr(&oid1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); - cl_git_pass(git_reference_create(&write1, repo, GIT_REFS_HEADS_DIR "test1", &oid1, 0)); - - cl_git_pass(git_oid_fromstr(&oid2, "e90810b8df3e80c413d903f631643c716887138d")); - cl_git_pass(git_reference_create(&write2, repo, GIT_REFS_HEADS_DIR "test2", &oid2, 0)); - - cl_git_pass(git_oid_fromstr(&oid3, "763d71aadf09a7951596c9746c024e7eece7c7af")); - cl_git_pass(git_reference_create(&write3, repo, GIT_REFS_HEADS_DIR "test3", &oid3, 0)); - - git_reference_delete(write1); - git_reference_free(write1); - - git_reference_delete(write3); - git_reference_free(write3); - - cl_git_pass(git_reference_foreach(repo, delete_test, &i)); - cl_assert_equal_i(1, (int)i); - - git_reference_free(write2); -} diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c index c89bcce9f..6509ae9a2 100644 --- a/tests-clar/refdb/testdb.c +++ b/tests-clar/refdb/testdb.c @@ -112,55 +112,6 @@ static int refdb_test_backend__lookup( return GIT_ENOTFOUND; } -typedef struct { - git_reference_iterator parent; - size_t i; -} refdb_test_iter; - -static int refdb_test_backend__iterator(git_reference_iterator **out, git_refdb_backend *_backend) -{ - refdb_test_iter *iter; - - GIT_UNUSED(_backend); - - iter = git__calloc(1, sizeof(refdb_test_iter)); - GITERR_CHECK_ALLOC(iter); - - iter->parent.backend = _backend; - iter->i = 0; - - *out = (git_reference_iterator *) iter; - - return 0; -} - -static int refdb_test_backend__next(git_reference **out, git_reference_iterator *_iter) -{ - refdb_test_entry *entry; - refdb_test_backend *backend = (refdb_test_backend *) _iter->backend; - refdb_test_iter *iter = (refdb_test_iter *) _iter; - - entry = git_vector_get(&backend->refs, iter->i); - if (!entry) - return GIT_ITEROVER; - - if (entry->type == GIT_REF_OID) { - *out = git_reference__alloc(entry->name, &entry->target.oid, NULL); - } else if (entry->type == GIT_REF_SYMBOLIC) { - *out = git_reference__alloc_symbolic(entry->name, entry->target.symbolic); - } else { - return -1; - } - - iter->i++; - return 0; -} - -static void refdb_test_backend__iterator_free(git_reference_iterator *iter) -{ - git__free(iter); -} - static void refdb_test_entry_free(refdb_test_entry *entry) { if (entry->type == GIT_REF_SYMBOLIC) @@ -222,9 +173,6 @@ int refdb_backend_test( backend->parent.exists = &refdb_test_backend__exists; backend->parent.lookup = &refdb_test_backend__lookup; - backend->parent.iterator = &refdb_test_backend__iterator; - backend->parent.next = &refdb_test_backend__next; - backend->parent.iterator_free = &refdb_test_backend__iterator_free; backend->parent.write = &refdb_test_backend__write; backend->parent.delete = &refdb_test_backend__delete; backend->parent.free = &refdb_test_backend__free; From 2d160ef782c812bd7d68413a00a62f46585725d0 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 29 May 2013 16:03:30 -0500 Subject: [PATCH 293/384] allow (ignore) bare slash in gitignore --- src/attr_file.c | 3 ++- src/pool.c | 5 +++++ tests-clar/attr/ignore.c | 21 +++++++++++++++++++++ tests-clar/core/pool.c | 10 ++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/attr_file.c b/src/attr_file.c index 85cd87624..d059cfec7 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -397,7 +397,8 @@ int git_attr_fnmatch__parse( *base = scan; - spec->length = scan - pattern; + if ((spec->length = scan - pattern) == 0) + return GIT_ENOTFOUND; if (pattern[spec->length - 1] == '/') { spec->length--; diff --git a/src/pool.c b/src/pool.c index b3cd49665..d484769e9 100644 --- a/src/pool.c +++ b/src/pool.c @@ -194,6 +194,11 @@ char *git_pool_strndup(git_pool *pool, const char *str, size_t n) assert(pool && str && pool->item_size == sizeof(char)); + if (n + 1 == 0) { + giterr_set_oom(); + return NULL; + } + if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) { memcpy(ptr, str, n); *(((char *)ptr) + n) = '\0'; diff --git a/tests-clar/attr/ignore.c b/tests-clar/attr/ignore.c index 8df0eb9de..0f945ebf6 100644 --- a/tests-clar/attr/ignore.c +++ b/tests-clar/attr/ignore.c @@ -34,6 +34,27 @@ void test_attr_ignore__honor_temporary_rules(void) assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); } +void test_attr_ignore__allow_root(void) +{ + cl_git_rewritefile("attr/.gitignore", "/"); + + assert_is_ignored(false, "File.txt"); + assert_is_ignored(false, "NewFolder"); + assert_is_ignored(false, "NewFolder/NewFolder"); + assert_is_ignored(false, "NewFolder/NewFolder/File.txt"); +} + +void test_attr_ignore__ignore_root(void) +{ + cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder\n/NewFolder/NewFolder"); + + assert_is_ignored(false, "File.txt"); + assert_is_ignored(true, "NewFolder"); + assert_is_ignored(true, "NewFolder/NewFolder"); + assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); +} + + void test_attr_ignore__skip_gitignore_directory(void) { cl_git_rewritefile("attr/.git/info/exclude", "/NewFolder\n/NewFolder/NewFolder"); diff --git a/tests-clar/core/pool.c b/tests-clar/core/pool.c index c42bb6da0..3073c4a45 100644 --- a/tests-clar/core/pool.c +++ b/tests-clar/core/pool.c @@ -133,3 +133,13 @@ void test_core_pool__free_list(void) git_pool_clear(&p); } + +void test_core_pool__strndup_limit(void) +{ + git_pool p; + + cl_git_pass(git_pool_init(&p, 1, 100)); + cl_assert(git_pool_strndup(&p, "foo", -1) == NULL); + git_pool_clear(&p); +} + From 4e6e2ff26f5a04a4628aa0d81e5d5d73acf28ec4 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 30 May 2013 03:47:10 +0200 Subject: [PATCH 294/384] ...Aaaand this works --- include/git2/refs.h | 7 +- include/git2/sys/refdb_backend.h | 9 +- src/branch.c | 13 ++- src/refdb.c | 34 +++++- src/refdb.h | 11 +- src/refdb_fs.c | 136 ++++++++++++++++++---- src/refs.c | 192 ++++--------------------------- src/remote.c | 5 +- tests-clar/refdb/testdb.c | 182 ----------------------------- tests-clar/refdb/testdb.h | 9 -- 10 files changed, 194 insertions(+), 404 deletions(-) delete mode 100644 tests-clar/refdb/testdb.c delete mode 100644 tests-clar/refdb/testdb.h diff --git a/include/git2/refs.h b/include/git2/refs.h index 44b658d5b..cec830653 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -257,11 +257,6 @@ GIT_EXTERN(int) git_reference_set_target( * The new name will be checked for validity. * See `git_reference_create_symbolic()` for rules about valid names. * - * On success, the given git_reference will be deleted from disk and a - * new `git_reference` will be returned. - * - * The reference will be immediately renamed in-memory and on disk. - * * If the `force` flag is not enabled, and there's already * a reference with the given name, the renaming will fail. * @@ -277,7 +272,7 @@ GIT_EXTERN(int) git_reference_set_target( * */ GIT_EXTERN(int) git_reference_rename( - git_reference **out, + git_reference **new_ref, git_reference *ref, const char *new_name, int force); diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index a78d22658..9b457b074 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -92,13 +92,18 @@ struct git_refdb_backend { * Writes the given reference to the refdb. A refdb implementation * must provide this function. */ - int (*write)(git_refdb_backend *backend, const git_reference *ref); + int (*write)(git_refdb_backend *backend, + const git_reference *ref, int force); + + int (*rename)( + git_reference **out, git_refdb_backend *backend, + const char *old_name, const char *new_name, int force); /** * Deletes the given reference from the refdb. A refdb implementation * must provide this function. */ - int (*delete)(git_refdb_backend *backend, const git_reference *ref); + int (*delete)(git_refdb_backend *backend, const char *ref_name); /** * Suggests that the given refdb compress or optimize its references. diff --git a/src/branch.c b/src/branch.c index 84efadae1..de38e3355 100644 --- a/src/branch.c +++ b/src/branch.c @@ -182,18 +182,21 @@ int git_branch_move( if (!git_reference_is_branch(branch)) return not_a_local_branch(git_reference_name(branch)); - if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0 || - (error = git_buf_printf(&old_config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR))) < 0 || - (error = git_buf_printf(&new_config_section, "branch.%s", new_branch_name)) < 0) + error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name); + if (error < 0) goto done; + git_buf_printf(&old_config_section, + "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)); + + git_buf_printf(&new_config_section, "branch.%s", new_branch_name); + if ((error = git_config_rename_section(git_reference_owner(branch), git_buf_cstr(&old_config_section), git_buf_cstr(&new_config_section))) < 0) goto done; - if ((error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force)) < 0) - goto done; + error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force); done: git_buf_free(&new_reference_name); diff --git a/src/refdb.c b/src/refdb.c index e0701d347..d8daa7773 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -165,14 +165,40 @@ void git_refdb_iterator_free(git_reference_iterator *iter) iter->free(iter); } -int git_refdb_write(git_refdb *db, const git_reference *ref) +int git_refdb_write(git_refdb *db, git_reference *ref, int force) { assert(db && db->backend); - return db->backend->write(db->backend, ref); + + GIT_REFCOUNT_INC(db); + ref->db = db; + + return db->backend->write(db->backend, ref, force); } -int git_refdb_delete(struct git_refdb *db, const git_reference *ref) +int git_refdb_rename( + git_reference **out, + git_refdb *db, + const char *old_name, + const char *new_name, + int force) +{ + int error; + + assert(db && db->backend); + error = db->backend->rename(out, db->backend, old_name, new_name, force); + if (error < 0) + return error; + + if (out) { + GIT_REFCOUNT_INC(db); + (*out)->db = db; + } + + return 0; +} + +int git_refdb_delete(struct git_refdb *db, const char *ref_name) { assert(db && db->backend); - return db->backend->delete(db->backend, ref); + return db->backend->delete(db->backend, ref_name); } diff --git a/src/refdb.h b/src/refdb.h index 1dcd70da8..62068db39 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -26,12 +26,19 @@ int git_refdb_lookup( git_refdb *refdb, const char *ref_name); +int git_refdb_rename( + git_reference **out, + git_refdb *db, + const char *old_name, + const char *new_name, + int force); + int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob); int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter); int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); -int git_refdb_write(git_refdb *refdb, const git_reference *ref); -int git_refdb_delete(git_refdb *refdb, const git_reference *ref); +int git_refdb_write(git_refdb *refdb, git_reference *ref, int force); +int git_refdb_delete(git_refdb *refdb, const char *ref_name); #endif diff --git a/src/refdb_fs.c b/src/refdb_fs.c index ecd033de0..c40c52438 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -116,11 +116,8 @@ static int packed_parse_oid( git_oid_cpy(&ref->oid, &id); - ref->flags = 0; - *ref_out = ref; *buffer_out = refname_end + 1; - return 0; corrupt: @@ -735,6 +732,58 @@ static int refdb_fs_backend__iterator( return 0; } +static bool ref_is_available( + const char *old_ref, const char *new_ref, const char *this_ref) +{ + if (old_ref == NULL || strcmp(old_ref, this_ref)) { + size_t reflen = strlen(this_ref); + size_t newlen = strlen(new_ref); + size_t cmplen = reflen < newlen ? reflen : newlen; + const char *lead = reflen < newlen ? new_ref : this_ref; + + if (!strncmp(new_ref, this_ref, cmplen) && lead[cmplen] == '/') { + return false; + } + } + + return true; +} + +static int reference_path_available( + refdb_fs_backend *backend, + const char *new_ref, + const char* old_ref, + int force) +{ + struct packref *this_ref; + + if (packed_load(backend) < 0) + return -1; + + if (!force) { + int exists; + + if (refdb_fs_backend__exists(&exists, (git_refdb_backend *)backend, new_ref) < 0) + return -1; + + if (exists) { + giterr_set(GITERR_REFERENCE, + "Failed to write reference '%s': a reference with " + " that name already exists.", new_ref); + return GIT_EEXISTS; + } + } + + git_strmap_foreach_value(backend->refcache.packfile, this_ref, { + if (!ref_is_available(old_ref, new_ref, this_ref->name)) { + giterr_set(GITERR_REFERENCE, + "The path to reference '%s' collides with an existing one", new_ref); + return -1; + } + }); + + return 0; +} static int loose_write(refdb_fs_backend *backend, const git_reference *ref) { @@ -744,8 +793,7 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref) /* Remove a possibly existing empty directory hierarchy * which name would collide with the reference name */ - if (git_futils_rmdir_r(ref->name, backend->path, - GIT_RMDIR_SKIP_NONEMPTY) < 0) + if (git_futils_rmdir_r(ref->name, backend->path, GIT_RMDIR_SKIP_NONEMPTY) < 0) return -1; if (git_buf_joinpath(&ref_path, backend->path, ref->name) < 0) @@ -1005,37 +1053,40 @@ cleanup_memory: static int refdb_fs_backend__write( git_refdb_backend *_backend, - const git_reference *ref) + const git_reference *ref, + int force) { refdb_fs_backend *backend; + int error; assert(_backend); backend = (refdb_fs_backend *)_backend; + error = reference_path_available(backend, ref->name, NULL, force); + if (error < 0) + return error; + return loose_write(backend, ref); } static int refdb_fs_backend__delete( git_refdb_backend *_backend, - const git_reference *ref) + const char *ref_name) { refdb_fs_backend *backend; - git_repository *repo; git_buf loose_path = GIT_BUF_INIT; struct packref *pack_ref; khiter_t pack_ref_pos; - int error = 0, pack_error; + int error = 0; bool loose_deleted = 0; assert(_backend); - assert(ref); + assert(ref_name); backend = (refdb_fs_backend *)_backend; - repo = backend->repo; /* If a loose reference exists, remove it from the filesystem */ - - if (git_buf_joinpath(&loose_path, repo->path_repository, ref->name) < 0) + if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0) return -1; if (git_path_isfile(loose_path.ptr)) { @@ -1049,22 +1100,66 @@ static int refdb_fs_backend__delete( return error; /* If a packed reference exists, remove it from the packfile and repack */ + error = packed_map_entry(&pack_ref, &pack_ref_pos, backend, ref_name); - if ((pack_error = packed_map_entry(&pack_ref, &pack_ref_pos, backend, ref->name)) == 0) { + if (error == GIT_ENOTFOUND) + return loose_deleted ? 0 : GIT_ENOTFOUND; + + if (error == 0) { git_strmap_delete_at(backend->refcache.packfile, pack_ref_pos); git__free(pack_ref); - error = packed_write(backend); } - if (pack_error == GIT_ENOTFOUND) - error = loose_deleted ? 0 : GIT_ENOTFOUND; - else - error = pack_error; - return error; } +static int refdb_fs_backend__rename( + git_reference **out, + git_refdb_backend *_backend, + const char *old_name, + const char *new_name, + int force) +{ + refdb_fs_backend *backend; + git_reference *old, *new; + int error; + + assert(_backend); + backend = (refdb_fs_backend *)_backend; + + error = reference_path_available(backend, new_name, old_name, force); + if (error < 0) + return error; + + error = refdb_fs_backend__lookup(&old, _backend, old_name); + if (error < 0) + return error; + + error = refdb_fs_backend__delete(_backend, old_name); + if (error < 0) { + git_reference_free(old); + return error; + } + + new = realloc(old, sizeof(git_reference) + strlen(new_name) + 1); + memcpy(new->name, new_name, strlen(new_name) + 1); + + error = loose_write(backend, new); + if (error < 0) { + git_reference_free(new); + return error; + } + + if (out) { + *out = new; + } else { + git_reference_free(new); + } + + return 0; +} + static int refdb_fs_backend__compress(git_refdb_backend *_backend) { refdb_fs_backend *backend; @@ -1172,6 +1267,7 @@ int git_refdb_backend_fs( backend->parent.iterator = &refdb_fs_backend__iterator; backend->parent.write = &refdb_fs_backend__write; backend->parent.delete = &refdb_fs_backend__delete; + backend->parent.rename = &refdb_fs_backend__rename; backend->parent.compress = &refdb_fs_backend__compress; backend->parent.free = &refdb_fs_backend__free; diff --git a/src/refs.c b/src/refs.c index a8583de19..c60e042d9 100644 --- a/src/refs.c +++ b/src/refs.c @@ -98,122 +98,9 @@ void git_reference_free(git_reference *reference) git__free(reference); } -struct reference_available_t { - const char *new_ref; - const char *old_ref; - int available; -}; - -static int _reference_available_cb(const char *refname, void *data) -{ - struct reference_available_t *d; - - assert(refname && data); - d = (struct reference_available_t *)data; - - if (!d->old_ref || strcmp(d->old_ref, refname)) { - size_t reflen = strlen(refname); - size_t newlen = strlen(d->new_ref); - size_t cmplen = reflen < newlen ? reflen : newlen; - const char *lead = reflen < newlen ? d->new_ref : refname; - - if (!strncmp(d->new_ref, refname, cmplen) && lead[cmplen] == '/') { - d->available = 0; - return -1; - } - } - - return 0; -} - -/** - * TODO: this should be part of the FS backend - */ -static int reference_path_available( - git_repository *repo, - const char *ref, - const char* old_ref) -{ - int error; - struct reference_available_t data; - - data.new_ref = ref; - data.old_ref = old_ref; - data.available = 1; - - error = git_reference_foreach_name(repo, _reference_available_cb, (void *)&data); - if (error < 0) - return error; - - if (!data.available) { - giterr_set(GITERR_REFERENCE, - "The path to reference '%s' collides with an existing one", ref); - return -1; - } - - return 0; -} - -/* - * Check if a reference could be written to disk, based on: - * - * - Whether a reference with the same name already exists, - * and we are allowing or disallowing overwrites - * - * - Whether the name of the reference would collide with - * an existing path - */ -static int reference_can_write( - git_repository *repo, - const char *refname, - const char *previous_name, - int force) -{ - git_refdb *refdb; - - if (git_repository_refdb__weakptr(&refdb, repo) < 0) - return -1; - - /* see if the reference shares a path with an existing reference; - * if a path is shared, we cannot create the reference, even when forcing */ - if (reference_path_available(repo, refname, previous_name) < 0) - return -1; - - /* check if the reference actually exists, but only if we are not forcing - * the rename. If we are forcing, it's OK to overwrite */ - if (!force) { - int exists; - - if (git_refdb_exists(&exists, refdb, refname) < 0) - return -1; - - /* We cannot proceed if the reference already exists and we're not forcing - * the rename; the existing one would be overwritten */ - if (exists) { - giterr_set(GITERR_REFERENCE, - "A reference with that name (%s) already exists", refname); - return GIT_EEXISTS; - } - } - - /* FIXME: if the reference exists and we are forcing, do we really need to - * remove the reference first? - * - * Two cases: - * - * - the reference already exists and is loose: not a problem, the file - * gets overwritten on disk - * - * - the reference already exists and is packed: we write a new one as - * loose, which by all means renders the packed one useless - */ - - return 0; -} - int git_reference_delete(git_reference *ref) { - return git_refdb_delete(ref->db, ref); + return git_refdb_delete(ref->db, ref->name); } int git_reference_lookup(git_reference **ref_out, @@ -420,23 +307,24 @@ static int reference__create( if (ref_out) *ref_out = NULL; - if ((error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name)) < 0 || - (error = reference_can_write(repo, normalized, NULL, force)) < 0 || - (error = git_repository_refdb__weakptr(&refdb, repo)) < 0) + error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name); + if (error < 0) + return error; + + error = git_repository_refdb__weakptr(&refdb, repo); + if (error < 0) return error; if (oid != NULL) { assert(symbolic == NULL); - ref = git_reference__alloc(name, oid, NULL); + ref = git_reference__alloc(normalized, oid, NULL); } else { - ref = git_reference__alloc_symbolic(name, symbolic); + ref = git_reference__alloc_symbolic(normalized, symbolic); } - /* TODO: this needs to be written more explicitly */ GITERR_CHECK_ALLOC(ref); - ref->db = refdb; - if ((error = git_refdb_write(refdb, ref)) < 0) { + if ((error = git_refdb_write(refdb, ref, force)) < 0) { git_reference_free(ref); return error; } @@ -533,77 +421,41 @@ int git_reference_rename( unsigned int normalization_flags; char normalized[GIT_REFNAME_MAX]; bool should_head_be_updated = false; - git_reference *result = NULL; int error = 0; int reference_has_log; - *out = NULL; - normalization_flags = ref->type == GIT_REF_SYMBOLIC ? GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL; if ((error = git_reference_normalize_name( - normalized, sizeof(normalized), new_name, normalization_flags)) < 0 || - (error = reference_can_write(ref->db->repo, normalized, ref->name, force)) < 0) + normalized, sizeof(normalized), new_name, normalization_flags)) < 0) return error; - /* - * Create the new reference. - */ - if (ref->type == GIT_REF_OID) { - result = git_reference__alloc(new_name, &ref->target.oid, &ref->peel); - } else if (ref->type == GIT_REF_SYMBOLIC) { - result = git_reference__alloc_symbolic(new_name, ref->target.symbolic); - } else { - assert(0); - } - - if (result == NULL) - return -1; - - /* TODO: this is bad */ - result->db = ref->db; - /* Check if we have to update HEAD. */ if ((error = git_branch_is_head(ref)) < 0) - goto on_error; + return error; should_head_be_updated = (error > 0); - /* Now delete the old ref and save the new one. */ - if ((error = git_refdb_delete(ref->db, ref)) < 0) - goto on_error; - - /* Save the new reference. */ - if ((error = git_refdb_write(ref->db, result)) < 0) - goto rollback; + if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force)) < 0) + return error; /* Update HEAD it was poiting to the reference being renamed. */ - if (should_head_be_updated && (error = git_repository_set_head(ref->db->repo, new_name)) < 0) { + if (should_head_be_updated && + (error = git_repository_set_head(ref->db->repo, new_name)) < 0) { giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference"); - goto on_error; + return error; } /* Rename the reflog file, if it exists. */ reference_has_log = git_reference_has_log(ref); - if (reference_has_log < 0) { - error = reference_has_log; - goto on_error; - } + if (reference_has_log < 0) + return reference_has_log; + if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0) - goto on_error; + return error; - *out = result; - - return error; - -rollback: - git_refdb_write(ref->db, ref); - -on_error: - git_reference_free(result); - - return error; + return 0; } int git_reference_resolve(git_reference **ref_out, const git_reference *ref) diff --git a/src/remote.c b/src/remote.c index 266a3d914..674c2776c 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1245,7 +1245,6 @@ static int rename_one_remote_reference( { int error = -1; git_buf new_name = GIT_BUF_INIT; - git_reference *newref = NULL; if (git_buf_printf( &new_name, @@ -1254,11 +1253,9 @@ static int rename_one_remote_reference( reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0) return -1; - /* TODO: can we make this NULL? */ - error = git_reference_rename(&newref, reference, git_buf_cstr(&new_name), 0); + error = git_reference_rename(NULL, reference, git_buf_cstr(&new_name), 0); git_reference_free(reference); - git_reference_free(newref); git_buf_free(&new_name); return error; } diff --git a/tests-clar/refdb/testdb.c b/tests-clar/refdb/testdb.c deleted file mode 100644 index 6509ae9a2..000000000 --- a/tests-clar/refdb/testdb.c +++ /dev/null @@ -1,182 +0,0 @@ -#include "vector.h" -#include "util.h" -#include "testdb.h" - -typedef struct refdb_test_backend { - git_refdb_backend parent; - - git_repository *repo; - git_vector refs; -} refdb_test_backend; - -typedef struct refdb_test_entry { - char *name; - git_ref_t type; - - union { - git_oid oid; - char *symbolic; - } target; -} refdb_test_entry; - -static int ref_name_cmp(const void *a, const void *b) -{ - return strcmp(git_reference_name((git_reference *)a), - git_reference_name((git_reference *)b)); -} - -static int refdb_test_backend__exists( - int *exists, - git_refdb_backend *_backend, - const char *ref_name) -{ - refdb_test_backend *backend; - refdb_test_entry *entry; - size_t i; - - assert(_backend); - backend = (refdb_test_backend *)_backend; - - *exists = 0; - - git_vector_foreach(&backend->refs, i, entry) { - if (strcmp(entry->name, ref_name) == 0) { - *exists = 1; - break; - } - } - - return 0; -} - -static int refdb_test_backend__write( - git_refdb_backend *_backend, - const git_reference *ref) -{ - refdb_test_backend *backend; - refdb_test_entry *entry; - - assert(_backend); - backend = (refdb_test_backend *)_backend; - - entry = git__calloc(1, sizeof(refdb_test_entry)); - GITERR_CHECK_ALLOC(entry); - - entry->name = git__strdup(git_reference_name(ref)); - GITERR_CHECK_ALLOC(entry->name); - - entry->type = git_reference_type(ref); - - if (entry->type == GIT_REF_OID) - git_oid_cpy(&entry->target.oid, git_reference_target(ref)); - else { - entry->target.symbolic = git__strdup(git_reference_symbolic_target(ref)); - GITERR_CHECK_ALLOC(entry->target.symbolic); - } - - git_vector_insert(&backend->refs, entry); - - return 0; -} - -static int refdb_test_backend__lookup( - git_reference **out, - git_refdb_backend *_backend, - const char *ref_name) -{ - refdb_test_backend *backend; - refdb_test_entry *entry; - size_t i; - - assert(_backend); - backend = (refdb_test_backend *)_backend; - - git_vector_foreach(&backend->refs, i, entry) { - if (strcmp(entry->name, ref_name) == 0) { - - if (entry->type == GIT_REF_OID) { - *out = git_reference__alloc(ref_name, - &entry->target.oid, NULL); - } else if (entry->type == GIT_REF_SYMBOLIC) { - *out = git_reference__alloc_symbolic(ref_name, - entry->target.symbolic); - } - - if (*out == NULL) - return -1; - - return 0; - } - } - - return GIT_ENOTFOUND; -} - -static void refdb_test_entry_free(refdb_test_entry *entry) -{ - if (entry->type == GIT_REF_SYMBOLIC) - git__free(entry->target.symbolic); - - git__free(entry->name); - git__free(entry); -} - -static int refdb_test_backend__delete( - git_refdb_backend *_backend, - const git_reference *ref) -{ - refdb_test_backend *backend; - refdb_test_entry *entry; - size_t i; - - assert(_backend); - backend = (refdb_test_backend *)_backend; - - git_vector_foreach(&backend->refs, i, entry) { - if (strcmp(entry->name, git_reference_name(ref)) == 0) { - git_vector_remove(&backend->refs, i); - refdb_test_entry_free(entry); - } - } - - return GIT_ENOTFOUND; -} - -static void refdb_test_backend__free(git_refdb_backend *_backend) -{ - refdb_test_backend *backend; - refdb_test_entry *entry; - size_t i; - - assert(_backend); - backend = (refdb_test_backend *)_backend; - - git_vector_foreach(&backend->refs, i, entry) - refdb_test_entry_free(entry); - - git_vector_free(&backend->refs); - git__free(backend); -} - -int refdb_backend_test( - git_refdb_backend **backend_out, - git_repository *repo) -{ - refdb_test_backend *backend; - - backend = git__calloc(1, sizeof(refdb_test_backend)); - GITERR_CHECK_ALLOC(backend); - - git_vector_init(&backend->refs, 0, ref_name_cmp); - - backend->repo = repo; - - backend->parent.exists = &refdb_test_backend__exists; - backend->parent.lookup = &refdb_test_backend__lookup; - backend->parent.write = &refdb_test_backend__write; - backend->parent.delete = &refdb_test_backend__delete; - backend->parent.free = &refdb_test_backend__free; - - *backend_out = (git_refdb_backend *)backend; - return 0; -} diff --git a/tests-clar/refdb/testdb.h b/tests-clar/refdb/testdb.h deleted file mode 100644 index a0d1bbc48..000000000 --- a/tests-clar/refdb/testdb.h +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include -#include -#include -#include - -int refdb_backend_test( - git_refdb_backend **backend_out, - git_repository *repo); From d17db2fd771ed4e0dc7dee6c043b335a89bdd545 Mon Sep 17 00:00:00 2001 From: yorah Date: Thu, 30 May 2013 11:30:34 +0200 Subject: [PATCH 295/384] thread: fix segfault on Windows 64 bits `lpExitCode` is a pointer to a long. A long is 32 bits wide on Windows. It means that on Windows 64bits, `GetExitCodeThread()` doesn't set/clear the high-order bytes of the 64 bits memory space pointed at by `value_ptr`. --- src/win32/pthread.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/win32/pthread.c b/src/win32/pthread.c index 232709e54..2f263b3e0 100644 --- a/src/win32/pthread.c +++ b/src/win32/pthread.c @@ -24,8 +24,10 @@ int pthread_join(pthread_t thread, void **value_ptr) DWORD ret = WaitForSingleObject(thread, INFINITE); if (ret == WAIT_OBJECT_0) { - if (value_ptr != NULL) + if (value_ptr != NULL) { + *value_ptr = NULL; GetExitCodeThread(thread, (void *)value_ptr); + } CloseHandle(thread); return 0; } From 979f75d8e1df7e8e43797822d5a55a8eff74fa74 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 30 May 2013 17:19:43 +0200 Subject: [PATCH 296/384] Refcounting --- src/refdb.c | 6 +++--- src/refdb.h | 2 ++ src/refs.c | 3 +++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/refdb.c b/src/refdb.c index d8daa7773..359842e44 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -86,7 +86,7 @@ int git_refdb_compress(git_refdb *db) return 0; } -static void refdb_free(git_refdb *db) +void git_refdb__free(git_refdb *db) { refdb_free_backend(db); git__free(db); @@ -97,7 +97,7 @@ void git_refdb_free(git_refdb *db) if (db == NULL) return; - GIT_REFCOUNT_DEC(db, refdb_free); + GIT_REFCOUNT_DEC(db, git_refdb__free); } int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name) @@ -161,7 +161,7 @@ int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter) void git_refdb_iterator_free(git_reference_iterator *iter) { - GIT_REFCOUNT_DEC(iter->db, refdb_free); + GIT_REFCOUNT_DEC(iter->db, git_refdb__free); iter->free(iter); } diff --git a/src/refdb.h b/src/refdb.h index 62068db39..3aea37b62 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -16,6 +16,8 @@ struct git_refdb { git_refdb_backend *backend; }; +void git_refdb__free(git_refdb *db); + int git_refdb_exists( int *exists, git_refdb *refdb, diff --git a/src/refs.c b/src/refs.c index c60e042d9..7103decbd 100644 --- a/src/refs.c +++ b/src/refs.c @@ -95,6 +95,9 @@ void git_reference_free(git_reference *reference) if (reference->type == GIT_REF_SYMBOLIC) git__free(reference->target.symbolic); + if (reference->db) + GIT_REFCOUNT_DEC(reference->db, git_refdb__free); + git__free(reference); } From 215af2ccb87ac22d94b7205ff97a050622de6897 Mon Sep 17 00:00:00 2001 From: yorah Date: Thu, 30 May 2013 17:40:56 +0200 Subject: [PATCH 297/384] remote: make default tag retrieving behaviour consistent Default for newly created remotes will be auto. Default when loading existing remotes with no tag retrieving behaviour set, was already auto. --- src/remote.c | 11 +++++------ tests-clar/network/remote/remotes.c | 3 +++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/remote.c b/src/remote.c index 7a64622a5..4fefe8001 100644 --- a/src/remote.c +++ b/src/remote.c @@ -48,9 +48,6 @@ static int download_tags_value(git_remote *remote, git_config *cfg) git_buf buf = GIT_BUF_INIT; int error; - if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSET) - return 0; - /* This is the default, let's see if we need to change it */ remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) @@ -117,10 +114,12 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n goto on_error; } - /* A remote without a name doesn't download tags */ - if (!name) { + if (!name) + /* A remote without a name doesn't download tags */ remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; - } + else + /* the default for a newly created remote is auto */ + remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; *out = remote; git_buf_free(&fetchbuf); diff --git a/tests-clar/network/remote/remotes.c b/tests-clar/network/remote/remotes.c index 34f1c055c..3c4fa96fa 100644 --- a/tests-clar/network/remote/remotes.c +++ b/tests-clar/network/remote/remotes.c @@ -286,11 +286,14 @@ void test_network_remote_remotes__add(void) _remote = NULL; cl_git_pass(git_remote_create(&_remote, _repo, "addtest", "http://github.com/libgit2/libgit2")); + cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, git_remote_autotag(_remote)); git_remote_free(_remote); _remote = NULL; cl_git_pass(git_remote_load(&_remote, _repo, "addtest")); + cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, git_remote_autotag(_remote)); + _refspec = git_vector_get(&_remote->refspecs, 0); cl_assert_equal_s("refs/heads/*", git_refspec_src(_refspec)); cl_assert(git_refspec_force(_refspec) == 1); From df50512aebb77f9fab3a930e367fba3777d6ee81 Mon Sep 17 00:00:00 2001 From: yorah Date: Thu, 30 May 2013 18:06:54 +0200 Subject: [PATCH 298/384] Proposal to handle default value (auto = 0) --- include/git2/remote.h | 7 +++---- src/clone.c | 1 - src/remote.c | 7 +------ 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 2aa384a54..3f43916b5 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -426,10 +426,9 @@ GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, git_remote_callback GIT_EXTERN(const git_transfer_progress *) git_remote_stats(git_remote *remote); typedef enum { - GIT_REMOTE_DOWNLOAD_TAGS_UNSET, - GIT_REMOTE_DOWNLOAD_TAGS_NONE, - GIT_REMOTE_DOWNLOAD_TAGS_AUTO, - GIT_REMOTE_DOWNLOAD_TAGS_ALL + GIT_REMOTE_DOWNLOAD_TAGS_AUTO = 0, + GIT_REMOTE_DOWNLOAD_TAGS_NONE = 1, + GIT_REMOTE_DOWNLOAD_TAGS_ALL = 2 } git_remote_autotag_option_t; /** diff --git a/src/clone.c b/src/clone.c index 7ebdb5765..4f763268b 100644 --- a/src/clone.c +++ b/src/clone.c @@ -422,7 +422,6 @@ static void normalize_options(git_clone_options *dst, const git_clone_options *s /* Provide defaults for null pointers */ if (!dst->remote_name) dst->remote_name = "origin"; - if (!dst->remote_autotag) dst->remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; } int git_clone( diff --git a/src/remote.c b/src/remote.c index 4fefe8001..4c1b39818 100644 --- a/src/remote.c +++ b/src/remote.c @@ -48,8 +48,7 @@ static int download_tags_value(git_remote *remote, git_config *cfg) git_buf buf = GIT_BUF_INIT; int error; - /* This is the default, let's see if we need to change it */ - remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; + /* The 0 value is the default (auto), let's see if we need to change it */ if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) return -1; @@ -117,9 +116,6 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n if (!name) /* A remote without a name doesn't download tags */ remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; - else - /* the default for a newly created remote is auto */ - remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; *out = remote; git_buf_free(&fetchbuf); @@ -245,7 +241,6 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) git_config *config; struct refspec_cb_data data; - assert(out && repo && name); if ((error = ensure_remote_name_is_valid(name)) < 0) From 7a6e0281c6367a278e1e06235c9a7217088d1205 Mon Sep 17 00:00:00 2001 From: Veeti Paananen Date: Thu, 2 May 2013 14:07:22 +0300 Subject: [PATCH 299/384] Build with the system's http-parser installation if available --- CMakeLists.txt | 15 ++++++++--- cmake/Modules/FindHTTP_Parser.cmake | 39 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 cmake/Modules/FindHTTP_Parser.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 016d77ad1..b931dc5f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,8 @@ PROJECT(libgit2 C) CMAKE_MINIMUM_REQUIRED(VERSION 2.6) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") +# Add find modules to the path +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") # Build options # @@ -100,8 +101,16 @@ ELSE () IF (NOT AMIGA) FIND_PACKAGE(OpenSSL) ENDIF () - FILE(GLOB SRC_HTTP deps/http-parser/*.c) - INCLUDE_DIRECTORIES(deps/http-parser) + + FIND_PACKAGE(HTTP_Parser QUIET) + IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2) + INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS}) + LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES}) + ELSE() + MESSAGE("http-parser was not found or is too old; using bundled 3rd-party sources.") + INCLUDE_DIRECTORIES(deps/http-parser) + FILE(GLOB SRC_HTTP deps/http-parser/*.c) + ENDIF() ENDIF() # Specify sha1 implementation diff --git a/cmake/Modules/FindHTTP_Parser.cmake b/cmake/Modules/FindHTTP_Parser.cmake new file mode 100644 index 000000000..d92bf75cc --- /dev/null +++ b/cmake/Modules/FindHTTP_Parser.cmake @@ -0,0 +1,39 @@ +# - Try to find http-parser +# +# Defines the following variables: +# +# HTTP_PARSER_FOUND - system has http-parser +# HTTP_PARSER_INCLUDE_DIR - the http-parser include directory +# HTTP_PARSER_LIBRARIES - Link these to use http-parser +# HTTP_PARSER_VERSION_MAJOR - major version +# HTTP_PARSER_VERSION_MINOR - minor version +# HTTP_PARSER_VERSION_STRING - the version of http-parser found + +# Find the header and library +FIND_PATH(HTTP_PARSER_INCLUDE_DIR NAMES http_parser.h) +FIND_LIBRARY(HTTP_PARSER_LIBRARY NAMES http_parser libhttp_parser) + +# Found the header, read version +if (HTTP_PARSER_INCLUDE_DIR AND EXISTS "${HTTP_PARSER_INCLUDE_DIR}/http_parser.h") + FILE(READ "${HTTP_PARSER_INCLUDE_DIR}/http_parser.h" HTTP_PARSER_H) + IF (HTTP_PARSER_H) + STRING(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_MAJOR[\t ]+([0-9]+).*" "\\1" HTTP_PARSER_VERSION_MAJOR "${HTTP_PARSER_H}") + STRING(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_MINOR[\t ]+([0-9]+).*" "\\1" HTTP_PARSER_VERSION_MINOR "${HTTP_PARSER_H}") + SET(HTTP_PARSER_VERSION_STRING "${HTTP_PARSER_VERSION_MAJOR}.${HTTP_PARSER_VERSION_MINOR}") + ENDIF() + UNSET(HTTP_PARSER_H) +ENDIF() + +# Handle the QUIETLY and REQUIRED arguments and set HTTP_PARSER_FOUND +# to TRUE if all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(HTTP_Parser REQUIRED_VARS HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY) + +# Hide advanced variables +MARK_AS_ADVANCED(HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY) + +# Set standard variables +IF (HTTP_PARSER_FOUND) + SET(HTTP_PARSER_LIBRARIES ${HTTP_PARSER_LIBRARY}) + set(HTTP_PARSER_INCLUDE_DIRS ${HTTP_PARSER_INCLUDE_DIR}) +ENDIF() From 1ed356dcfef449313bac3ce795f26d19423c744c Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Thu, 30 May 2013 21:04:28 +0200 Subject: [PATCH 300/384] Frees --- src/refdb_fs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/refdb_fs.c b/src/refdb_fs.c index c40c52438..e7ffcd4a1 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -566,7 +566,7 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter) size_t i; git_vector_foreach(&iter->loose, i, loose_path) { - free(loose_path); + git__free(loose_path); } git_vector_free(&iter->loose); @@ -1232,7 +1232,7 @@ static int setup_namespace(git_buf *path, git_repository *repo) } git_buf_printf(path, "refs/namespaces/%s/refs", end); - free(parts); + git__free(parts); /* Make sure that the folder with the namespace exists */ if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0) From 883929144d785702f783e956ff55434566cb009f Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Thu, 30 May 2013 01:12:27 -0400 Subject: [PATCH 301/384] Added support for setting transport flags (No check SSL cert) to git_clone call. --- include/git2/clone.h | 3 +++ src/clone.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/include/git2/clone.h b/include/git2/clone.h index 20df49104..5858b4e32 100644 --- a/include/git2/clone.h +++ b/include/git2/clone.h @@ -51,6 +51,8 @@ GIT_BEGIN_DECL * - `cred_acquire_cb` is a callback to be used if credentials are required * during the initial fetch. * - `cred_acquire_payload` is the payload for the above callback. + * - `transport_flags` is flags used to create transport if no transport is + * provided. * - `transport` is a custom transport to be used for the initial fetch. NULL * means use the transport autodetected from the URL. * - `remote_callbacks` may be used to specify custom progress callbacks for @@ -75,6 +77,7 @@ typedef struct git_clone_options { const char *push_spec; git_cred_acquire_cb cred_acquire_cb; void *cred_acquire_payload; + git_transport_flags_t transport_flags; git_transport *transport; git_remote_callbacks *remote_callbacks; git_remote_autotag_option_t remote_autotag; diff --git a/src/clone.c b/src/clone.c index 7ebdb5765..ba19caef8 100644 --- a/src/clone.c +++ b/src/clone.c @@ -336,6 +336,10 @@ static int create_and_configure_origin( (error = git_remote_set_pushurl(origin, options->pushurl)) < 0) goto on_error; + if (options->transport_flags == GIT_TRANSPORTFLAGS_NO_CHECK_CERT) { + git_remote_check_cert(origin, 0); + } + if ((error = git_remote_save(origin)) < 0) goto on_error; From 8c2458bea6c9cbc66959dce8fd51d21f0f46b81d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 31 May 2013 11:41:33 -0500 Subject: [PATCH 302/384] improve test for index extension truncation --- src/index.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/index.c b/src/index.c index d5d9aef48..45ce2f3d6 100644 --- a/src/index.c +++ b/src/index.c @@ -1575,7 +1575,8 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer total_size = dest.extension_size + sizeof(struct index_extension); - if (buffer_size - total_size < INDEX_FOOTER_SIZE) + if (buffer_size < total_size || + buffer_size - total_size < INDEX_FOOTER_SIZE) return 0; /* optional extension */ @@ -1661,7 +1662,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) /* see if we have read any bytes from the extension */ if (extension_size == 0) - return index_error_invalid("extension size is zero"); + return index_error_invalid("extension is truncated"); seek_forward(extension_size); } From cee695ae6b9a9f586d32d0b9460a358bfdc4fe1b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 31 May 2013 12:18:43 -0700 Subject: [PATCH 303/384] Make iterators use GIT_ITEROVER & smart advance 1. internal iterators now return GIT_ITEROVER when you go past the last item in the iteration. 2. git_iterator_advance will "advance" to the first item in the iteration if it is called immediately after creating the iterator, which allows a simpler idiom for basic iteration. 3. if git_iterator_advance encounters an error reading data (e.g. a missing tree or an unreadable file), it returns the error but also attempts to advance past the invalid data to prevent an infinite loop. Updated all tests and internal usage of iterators to account for these new behaviors. --- src/checkout.c | 31 ++--- src/diff.c | 34 ++++-- src/iterator.c | 214 +++++++++++++++++++++++----------- src/iterator.h | 38 +++++- src/merge.c | 15 ++- src/notes.c | 14 +-- src/refdb_fs.c | 10 +- src/submodule.c | 18 ++- tests-clar/diff/iterator.c | 116 +++++++++--------- tests-clar/repo/iterator.c | 21 ++-- tests-clar/submodule/status.c | 5 +- 11 files changed, 329 insertions(+), 187 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index c28fcdee0..7a2e68300 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -495,12 +495,9 @@ static int checkout_action( if (cmp == 0) { if (wd->mode == GIT_FILEMODE_TREE) { /* case 2 - entry prefixed by workdir tree */ - if ((error = git_iterator_advance_into(&wd, workdir)) < 0) { - if (error != GIT_ENOTFOUND || - git_iterator_advance(&wd, workdir) < 0) - goto fail; - } - + error = git_iterator_advance_into_or_over(&wd, workdir); + if (error && error != GIT_ITEROVER) + goto fail; *wditem_ptr = wd; continue; } @@ -515,8 +512,10 @@ static int checkout_action( } /* case 1 - handle wd item (if it matches pathspec) */ - if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0 || - git_iterator_advance(&wd, workdir) < 0) + if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0) + goto fail; + if ((error = git_iterator_advance(&wd, workdir)) < 0 && + error != GIT_ITEROVER) goto fail; *wditem_ptr = wd; @@ -539,8 +538,9 @@ static int checkout_action( if (delta->status == GIT_DELTA_TYPECHANGE) { if (delta->old_file.mode == GIT_FILEMODE_TREE) { act = checkout_action_with_wd(data, delta, wd); - if (git_iterator_advance_into(&wd, workdir) < 0) - wd = NULL; + if ((error = git_iterator_advance_into(&wd, workdir)) < 0 && + error != GIT_ENOTFOUND) + goto fail; *wditem_ptr = wd; return act; } @@ -550,8 +550,9 @@ static int checkout_action( delta->old_file.mode == GIT_FILEMODE_COMMIT) { act = checkout_action_with_wd(data, delta, wd); - if (git_iterator_advance(&wd, workdir) < 0) - wd = NULL; + if ((error = git_iterator_advance(&wd, workdir)) < 0 && + error != GIT_ITEROVER) + goto fail; *wditem_ptr = wd; return act; } @@ -582,6 +583,9 @@ static int checkout_remaining_wd_items( error = git_iterator_advance(&wd, workdir); } + if (error == GIT_ITEROVER) + error = 0; + return error; } @@ -603,7 +607,8 @@ static int checkout_get_actions( git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0) return -1; - if ((error = git_iterator_current(&wditem, workdir)) < 0) + if ((error = git_iterator_current(&wditem, workdir)) < 0 && + error != GIT_ITEROVER) goto fail; deltas = &data->diff->deltas; diff --git a/src/diff.c b/src/diff.c index b96ff4705..05ef4f16b 100644 --- a/src/diff.c +++ b/src/diff.c @@ -796,6 +796,9 @@ static int diff_scan_inside_untracked_dir( done: git_buf_free(&base); + if (error == GIT_ITEROVER) + error = 0; + return error; } @@ -981,8 +984,11 @@ static int handle_matched_item( { int error = 0; - if (!(error = maybe_modified(diff, info)) && - !(error = git_iterator_advance(&info->oitem, info->old_iter))) + if ((error = maybe_modified(diff, info)) < 0) + return error; + + if (!(error = git_iterator_advance(&info->oitem, info->old_iter)) || + error == GIT_ITEROVER) error = git_iterator_advance(&info->nitem, info->new_iter); return error; @@ -1011,15 +1017,22 @@ int git_diff__from_iterators( /* make iterators have matching icase behavior */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) { - if (!(error = git_iterator_set_ignore_case(old_iter, true))) - error = git_iterator_set_ignore_case(new_iter, true); + if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 || + (error = git_iterator_set_ignore_case(new_iter, true)) < 0) + goto cleanup; } /* finish initialization */ - if (!error && - !(error = diff_list_apply_options(diff, opts)) && - !(error = git_iterator_current(&info.oitem, old_iter))) - error = git_iterator_current(&info.nitem, new_iter); + if ((error = diff_list_apply_options(diff, opts)) < 0) + goto cleanup; + + if ((error = git_iterator_current(&info.oitem, old_iter)) < 0 && + error != GIT_ITEROVER) + goto cleanup; + if ((error = git_iterator_current(&info.nitem, new_iter)) < 0 && + error != GIT_ITEROVER) + goto cleanup; + error = 0; /* run iterators building diffs */ while (!error && (info.oitem || info.nitem)) { @@ -1041,8 +1054,13 @@ int git_diff__from_iterators( */ else error = handle_matched_item(diff, &info); + + /* because we are iterating over two lists, ignore ITEROVER */ + if (error == GIT_ITEROVER) + error = 0; } +cleanup: if (!error) *diff_ptr = diff; else diff --git a/src/iterator.c b/src/iterator.c index ff08c1ce0..4360b99ad 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -46,6 +46,9 @@ #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND) #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND) +#define GIT_ITERATOR_FIRST_ACCESS (1 << 15) +#define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS) + #define iterator__end(I) ((git_iterator *)(I))->end #define iterator__past_end(I,PATH) \ (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0) @@ -68,6 +71,8 @@ static int iterator__reset_range( GITERR_CHECK_ALLOC(iter->end); } + iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS; + return 0; } @@ -109,7 +114,7 @@ static int empty_iterator__noop(const git_index_entry **e, git_iterator *i) { GIT_UNUSED(i); iterator__clear_entry(e); - return 0; + return GIT_ITEROVER; } static int empty_iterator__seek(git_iterator *i, const char *p) @@ -193,6 +198,7 @@ typedef struct { git_buf path; int path_ambiguities; bool path_has_filename; + bool entry_is_current; int (*strncomp)(const char *a, const char *b, size_t sz); } tree_iterator; @@ -266,9 +272,28 @@ static int tree_iterator__search_cmp(const void *key, const void *val, void *p) ((tree_iterator *)p)->strncomp); } +static bool tree_iterator__move_to_next( + tree_iterator *ti, tree_iterator_frame *tf) +{ + if (tf->next > tf->current + 1) + ti->path_ambiguities--; + + if (!tf->up) { /* at root */ + tf->current = tf->next; + return false; + } + + for (; tf->current < tf->next; tf->current++) { + git_tree_free(tf->entries[tf->current]->tree); + tf->entries[tf->current]->tree = NULL; + } + + return (tf->current < tf->n_entries); +} + static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) { - int error; + int error = 0; const git_tree_entry *te, *last = NULL; tf->next = tf->current; @@ -279,18 +304,23 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) if (last && tree_iterator__te_cmp(last, te, ti->strncomp)) break; - /* load trees for items in [current,next) range */ - if (git_tree_entry__is_tree(te) && - (error = git_tree_lookup( - &tf->entries[tf->next]->tree, ti->base.repo, &te->oid)) < 0) - return error; + /* try to load trees for items in [current,next) range */ + if (!error && git_tree_entry__is_tree(te)) + error = git_tree_lookup( + &tf->entries[tf->next]->tree, ti->base.repo, &te->oid); } if (tf->next > tf->current + 1) ti->path_ambiguities++; + /* if a tree lookup failed, advance over this span and return failure */ + if (error < 0) { + tree_iterator__move_to_next(ti, tf); + return error; + } + if (last && !tree_iterator__current_filename(ti, last)) - return -1; + return -1; /* must have been allocation failure */ return 0; } @@ -308,7 +338,7 @@ static int tree_iterator__push_frame(tree_iterator *ti) size_t i, n_entries = 0; if (head->current >= head->n_entries || !head->entries[head->current]->tree) - return 0; + return GIT_ITEROVER; for (i = head->current; i < head->next; ++i) n_entries += git_tree_entrycount(head->entries[i]->tree); @@ -359,7 +389,7 @@ static int tree_iterator__push_frame(tree_iterator *ti) } } - ti->path_has_filename = false; + ti->path_has_filename = ti->entry_is_current = false; if ((error = tree_iterator__set_next(ti, tf)) < 0) return error; @@ -371,25 +401,6 @@ static int tree_iterator__push_frame(tree_iterator *ti) return 0; } -static bool tree_iterator__move_to_next( - tree_iterator *ti, tree_iterator_frame *tf) -{ - if (tf->next > tf->current + 1) - ti->path_ambiguities--; - - if (!tf->up) { /* at root */ - tf->current = tf->next; - return false; - } - - for (; tf->current < tf->next; tf->current++) { - git_tree_free(tf->entries[tf->current]->tree); - tf->entries[tf->current]->tree = NULL; - } - - return (tf->current < tf->n_entries); -} - static bool tree_iterator__pop_frame(tree_iterator *ti, bool final) { tree_iterator_frame *tf = ti->head; @@ -412,7 +423,7 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final) return true; } -static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final) +static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final) { while (tree_iterator__pop_frame(ti, final)) /* pop to root */; @@ -421,22 +432,18 @@ static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final) ti->path_ambiguities = 0; git_buf_clear(&ti->path); } - - return 0; } -static int tree_iterator__current( - const git_index_entry **entry, git_iterator *self) +static int tree_iterator__update_entry(tree_iterator *ti) { - tree_iterator *ti = (tree_iterator *)self; - tree_iterator_frame *tf = ti->head; - const git_tree_entry *te; + tree_iterator_frame *tf; + const git_tree_entry *te; - iterator__clear_entry(entry); + if (ti->entry_is_current) + return 0; - if (tf->current >= tf->n_entries) - return 0; - te = tf->entries[tf->current]->te; + tf = ti->head; + te = tf->entries[tf->current]->te; ti->entry.mode = te->attr; git_oid_cpy(&ti->entry.oid, &te->oid); @@ -447,12 +454,36 @@ static int tree_iterator__current( if (ti->path_ambiguities > 0) tree_iterator__rewrite_filename(ti); - if (iterator__past_end(ti, ti->entry.path)) - return tree_iterator__pop_all(ti, true, false); + if (iterator__past_end(ti, ti->entry.path)) { + tree_iterator__pop_all(ti, true, false); + return GIT_ITEROVER; + } + + ti->entry_is_current = true; + + return 0; +} + +static int tree_iterator__current( + const git_index_entry **entry, git_iterator *self) +{ + int error; + tree_iterator *ti = (tree_iterator *)self; + tree_iterator_frame *tf = ti->head; + + iterator__clear_entry(entry); + + if (tf->current >= tf->n_entries) + return GIT_ITEROVER; + + if ((error = tree_iterator__update_entry(ti)) < 0) + return error; if (entry) *entry = &ti->entry; + ti->base.flags |= GIT_ITERATOR_FIRST_ACCESS; + return 0; } @@ -464,8 +495,10 @@ static int tree_iterator__advance_into( iterator__clear_entry(entry); - if (tree_iterator__at_tree(ti) && - !(error = tree_iterator__push_frame(ti))) + if (tree_iterator__at_tree(ti)) + error = tree_iterator__push_frame(ti); + + if (!error && entry) error = tree_iterator__current(entry, self); return error; @@ -480,8 +513,11 @@ static int tree_iterator__advance( iterator__clear_entry(entry); - if (tf->current > tf->n_entries) - return 0; + if (tf->current >= tf->n_entries) + return GIT_ITEROVER; + + if (!iterator__has_been_accessed(ti)) + return tree_iterator__current(entry, self); if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) && tree_iterator__at_tree(ti)) @@ -489,7 +525,7 @@ static int tree_iterator__advance( if (ti->path_has_filename) { git_buf_rtruncate_at_char(&ti->path, '/'); - ti->path_has_filename = false; + ti->path_has_filename = ti->entry_is_current = false; } /* scan forward and up, advancing in frame or popping frame when done */ @@ -699,7 +735,9 @@ static int index_iterator__current( if (entry) *entry = ie; - return 0; + ii->base.flags |= GIT_ITERATOR_FIRST_ACCESS; + + return (ie != NULL) ? 0 : GIT_ITEROVER; } static int index_iterator__at_end(git_iterator *self) @@ -715,6 +753,9 @@ static int index_iterator__advance( size_t entrycount = git_index_entrycount(ii->index); const git_index_entry *ie; + if (!iterator__has_been_accessed(ii)) + return index_iterator__current(entry, self); + if (index_iterator__at_tree(ii)) { if (iterator__do_autoexpand(ii)) { ii->partial.ptr[ii->partial_pos] = ii->restore_terminator; @@ -906,6 +947,8 @@ static void fs_iterator__pop_frame( } static int fs_iterator__update_entry(fs_iterator *fi); +static int fs_iterator__advance_over( + const git_index_entry **entry, git_iterator *self); static int fs_iterator__entry_cmp(const void *i, const void *item) { @@ -945,7 +988,13 @@ static int fs_iterator__expand_dir(fs_iterator *fi) fi->path.ptr, fi->root_len, iterator__ignore_case(fi), fi->base.start, fi->base.end, &ff->entries); - if (error < 0 || ff->entries.length == 0) { + if (error < 0) { + fs_iterator__free_frame(ff); + fs_iterator__advance_over(NULL, (git_iterator *)fi); + return error; + } + + if (ff->entries.length == 0) { fs_iterator__free_frame(ff); return GIT_ENOTFOUND; } @@ -966,9 +1015,14 @@ static int fs_iterator__current( const git_index_entry **entry, git_iterator *self) { fs_iterator *fi = (fs_iterator *)self; + const git_index_entry *fe = (fi->entry.path == NULL) ? NULL : &fi->entry; + if (entry) - *entry = (fi->entry.path == NULL) ? NULL : &fi->entry; - return 0; + *entry = fe; + + fi->base.flags |= GIT_ITERATOR_FIRST_ACCESS; + + return (fe != NULL) ? 0 : GIT_ITEROVER; } static int fs_iterator__at_end(git_iterator *self) @@ -998,6 +1052,9 @@ static int fs_iterator__advance_into( if (!error && entry) error = fs_iterator__current(entry, iter); + if (!error && !fi->entry.path) + error = GIT_ITEROVER; + return error; } @@ -1035,6 +1092,9 @@ static int fs_iterator__advance( { fs_iterator *fi = (fs_iterator *)self; + if (!iterator__has_been_accessed(fi)) + return fs_iterator__current(entry, self); + /* given include_trees & autoexpand, we might have to go into a tree */ if (iterator__do_autoexpand(fi) && fi->entry.path != NULL && @@ -1063,18 +1123,23 @@ static int fs_iterator__seek(git_iterator *self, const char *prefix) static int fs_iterator__reset( git_iterator *self, const char *start, const char *end) { + int error; fs_iterator *fi = (fs_iterator *)self; while (fi->stack != NULL && fi->stack->next != NULL) fs_iterator__pop_frame(fi, fi->stack, false); fi->depth = 0; - if (iterator__reset_range(self, start, end) < 0) - return -1; + if ((error = iterator__reset_range(self, start, end)) < 0) + return error; fs_iterator__seek_frame_start(fi, fi->stack); - return fs_iterator__update_entry(fi); + error = fs_iterator__update_entry(fi); + if (error == GIT_ITEROVER) + error = 0; + + return error; } static void fs_iterator__free(git_iterator *self) @@ -1089,18 +1154,23 @@ static void fs_iterator__free(git_iterator *self) static int fs_iterator__update_entry(fs_iterator *fi) { - git_path_with_stat *ps = - git_vector_get(&fi->stack->entries, fi->stack->index); + git_path_with_stat *ps; - git_buf_truncate(&fi->path, fi->root_len); memset(&fi->entry, 0, sizeof(fi->entry)); + if (!fi->stack) + return GIT_ITEROVER; + + ps = git_vector_get(&fi->stack->entries, fi->stack->index); if (!ps) - return 0; + return GIT_ITEROVER; + + git_buf_truncate(&fi->path, fi->root_len); if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0) return -1; + if (iterator__past_end(fi, fi->path.ptr + fi->root_len)) - return 0; + return GIT_ITEROVER; fi->entry.path = ps->path; git_index_entry__init_from_stat(&fi->entry, &ps->st); @@ -1114,8 +1184,13 @@ static int fs_iterator__update_entry(fs_iterator *fi) return fs_iterator__advance_over(NULL, (git_iterator *)fi); /* if this is a tree and trees aren't included, then skip */ - if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) - return git_iterator_advance(NULL, (git_iterator *)fi); + if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) { + int error = fs_iterator__advance_into(NULL, (git_iterator *)fi); + if (error != GIT_ENOTFOUND) + return error; + giterr_clear(); + return fs_iterator__advance_over(NULL, (git_iterator *)fi); + } return 0; } @@ -1131,13 +1206,14 @@ static int fs_iterator__initialize( } fi->root_len = fi->path.size; - if ((error = fs_iterator__expand_dir(fi)) == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; - } - if (error) { - git_iterator_free((git_iterator *)fi); - fi = NULL; + if ((error = fs_iterator__expand_dir(fi)) < 0) { + if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) { + giterr_clear(); + error = 0; + } else { + git_iterator_free((git_iterator *)fi); + fi = NULL; + } } *out = (git_iterator *)fi; diff --git a/src/iterator.h b/src/iterator.h index 7998f7c6b..493ff4b2a 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -142,9 +142,9 @@ GIT_INLINE(int) git_iterator_advance( * * If the current item is not a tree, this is a no-op. * - * For working directory iterators only, a tree (i.e. directory) can be - * empty. In that case, this function returns GIT_ENOTFOUND and does not - * advance. That can't happen for tree and index iterators. + * For filesystem and working directory iterators, a tree (i.e. directory) + * can be empty. In that case, this function returns GIT_ENOTFOUND and + * does not advance. That can't happen for tree and index iterators. */ GIT_INLINE(int) git_iterator_advance_into( const git_index_entry **entry, git_iterator *iter) @@ -152,18 +152,50 @@ GIT_INLINE(int) git_iterator_advance_into( return iter->cb->advance_into(entry, iter); } +/** + * Advance into a tree or skip over it if it is empty. + * + * Because `git_iterator_advance_into` may return GIT_ENOTFOUND if the + * directory is empty (only with filesystem and working directory + * iterators) and a common response is to just call `git_iterator_advance` + * when that happens, this bundles the two into a single simple call. + */ +GIT_INLINE(int) git_iterator_advance_into_or_over( + const git_index_entry **entry, git_iterator *iter) +{ + int error = iter->cb->advance_into(entry, iter); + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = iter->cb->advance(entry, iter); + } + return error; +} + +/* Seek is currently unimplemented */ GIT_INLINE(int) git_iterator_seek( git_iterator *iter, const char *prefix) { return iter->cb->seek(iter, prefix); } +/** + * Go back to the start of the iteration. + * + * This resets the iterator to the start of the iteration. It also allows + * you to reset the `start` and `end` pathname boundaries of the iteration + * when doing so. + */ GIT_INLINE(int) git_iterator_reset( git_iterator *iter, const char *start, const char *end) { return iter->cb->reset(iter, start, end); } +/** + * Check if the iterator is at the end + * + * @return 0 if not at end, >0 if at end + */ GIT_INLINE(int) git_iterator_at_end(git_iterator *iter) { return iter->cb->at_end(iter); diff --git a/src/merge.c b/src/merge.c index 11345587c..047d96013 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1259,7 +1259,8 @@ int git_merge_diff_list__find_differences( /* Set up the iterators */ for (i = 0; i < 3; i++) { - if ((error = git_iterator_current(&items[i], iterators[i])) < 0) + error = git_iterator_current(&items[i], iterators[i]); + if (error < 0 && error != GIT_ITEROVER) goto done; } @@ -1313,11 +1314,16 @@ int git_merge_diff_list__find_differences( error = merge_index_insert_conflict(diff_list, &df_data, cur_items); else error = merge_index_insert_unmodified(diff_list, cur_items); + if (error < 0) + goto done; /* Advance each iterator that participated */ for (i = 0; i < 3; i++) { - if (cur_items[i] != NULL && - (error = git_iterator_advance(&items[i], iterators[i])) < 0) + if (cur_items[i] == NULL) + continue; + + error = git_iterator_advance(&items[i], iterators[i]); + if (error < 0 && error != GIT_ITEROVER) goto done; } } @@ -1326,6 +1332,9 @@ done: for (i = 0; i < 3; i++) git_iterator_free(iterators[i]); + if (error == GIT_ITEROVER) + error = 0; + return error; } diff --git a/src/notes.c b/src/notes.c index 3e3db58db..beace1b50 100644 --- a/src/notes.c +++ b/src/notes.c @@ -647,18 +647,12 @@ int git_note_next( const git_index_entry *item; if ((error = git_iterator_current(&item, it)) < 0) - goto exit; + return error; - if (item != NULL) { - git_oid_cpy(note_id, &item->oid); - error = process_entry_path(item->path, annotated_id); + git_oid_cpy(note_id, &item->oid); - if (error >= 0) - error = git_iterator_advance(NULL, it); - } else { - error = GIT_ITEROVER; - } + if (!(error = process_entry_path(item->path, annotated_id))) + git_iterator_advance(NULL, it); -exit: return error; } diff --git a/src/refdb_fs.c b/src/refdb_fs.c index e7ffcd4a1..4083ba9e5 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -582,6 +582,9 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) git_iterator *fsit; const git_index_entry *entry = NULL; + if (!backend->path) /* do nothing if no path for loose refs */ + return 0; + if (git_buf_printf(&path, "%s/refs", backend->path) < 0) return -1; @@ -591,7 +594,7 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) git_vector_init(&iter->loose, 8, NULL); git_buf_sets(&path, GIT_REFS_DIR); - while (!git_iterator_current(&entry, fsit) && entry) { + while (!git_iterator_advance(&entry, fsit)) { const char *ref_name; khiter_t pos; @@ -600,10 +603,8 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) ref_name = git_buf_cstr(&path); if (git__suffixcmp(ref_name, ".lock") == 0 || - (iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0)) { - git_iterator_advance(NULL, fsit); + (iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0)) continue; - } pos = git_strmap_lookup_index(packfile, ref_name); if (git_strmap_valid_index(packfile, pos)) { @@ -612,7 +613,6 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) } git_vector_insert(&iter->loose, git__strdup(ref_name)); - git_iterator_advance(NULL, fsit); } git_iterator_free(fsit); diff --git a/src/submodule.c b/src/submodule.c index f4fbcd35a..16114d8ac 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1143,9 +1143,7 @@ static int load_submodule_config_from_index( (error = git_iterator_for_index(&i, index, 0, NULL, NULL)) < 0) return error; - error = git_iterator_current(&entry, i); - - while (!error && entry != NULL) { + while (!(error = git_iterator_advance(&entry, i))) { if (S_ISGITLINK(entry->mode)) { error = submodule_load_from_index(repo, entry); @@ -1158,10 +1156,11 @@ static int load_submodule_config_from_index( if (strcmp(entry->path, GIT_MODULES_FILE) == 0) git_oid_cpy(gitmodules_oid, &entry->oid); } - - error = git_iterator_advance(&entry, i); } + if (error == GIT_ITEROVER) + error = 0; + git_iterator_free(i); return error; @@ -1183,9 +1182,7 @@ static int load_submodule_config_from_head( return error; } - error = git_iterator_current(&entry, i); - - while (!error && entry != NULL) { + while (!(error = git_iterator_advance(&entry, i))) { if (S_ISGITLINK(entry->mode)) { error = submodule_load_from_head(repo, entry->path, &entry->oid); @@ -1199,10 +1196,11 @@ static int load_submodule_config_from_head( git_oid_iszero(gitmodules_oid)) git_oid_cpy(gitmodules_oid, &entry->oid); } - - error = git_iterator_advance(&entry, i); } + if (error == GIT_ITEROVER) + error = 0; + git_iterator_free(i); git_tree_free(head); diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c index 15b10465a..bbdae8ad1 100644 --- a/tests-clar/diff/iterator.c +++ b/tests-clar/diff/iterator.c @@ -31,7 +31,7 @@ static void tree_iterator_test( git_tree *t; git_iterator *i; const git_index_entry *entry; - int count = 0, count_post_reset = 0; + int error, count = 0, count_post_reset = 0; git_repository *repo = cl_git_sandbox_init(sandbox); cl_assert(t = resolve_commit_oid_to_tree(repo, treeish)); @@ -39,29 +39,30 @@ static void tree_iterator_test( &i, t, GIT_ITERATOR_DONT_IGNORE_CASE, start, end)); /* test loop */ - cl_git_pass(git_iterator_current(&entry, i)); - while (entry != NULL) { + while (!(error = git_iterator_advance(&entry, i))) { + cl_assert(entry); if (expected_values != NULL) cl_assert_equal_s(expected_values[count], entry->path); count++; - cl_git_pass(git_iterator_advance(&entry, i)); } + cl_assert_equal_i(GIT_ITEROVER, error); + cl_assert(!entry); + cl_assert_equal_i(expected_count, count); /* test reset */ cl_git_pass(git_iterator_reset(i, NULL, NULL)); - cl_git_pass(git_iterator_current(&entry, i)); - while (entry != NULL) { + + while (!(error = git_iterator_advance(&entry, i))) { + cl_assert(entry); if (expected_values != NULL) cl_assert_equal_s(expected_values[count_post_reset], entry->path); count_post_reset++; - cl_git_pass(git_iterator_advance(&entry, i)); } - - git_iterator_free(i); - - cl_assert_equal_i(expected_count, count); + cl_assert_equal_i(GIT_ITEROVER, error); + cl_assert(!entry); cl_assert_equal_i(count, count_post_reset); + git_iterator_free(i); git_tree_free(t); } @@ -298,7 +299,7 @@ void test_diff_iterator__tree_special_functions(void) git_iterator *i; const git_index_entry *entry; git_repository *repo = cl_git_sandbox_init("attr"); - int cases = 0; + int error, cases = 0; const char *rootoid = "ce39a97a7fb1fa90bcf5e711249c1e507476ae0e"; t = resolve_commit_oid_to_tree( @@ -307,9 +308,10 @@ void test_diff_iterator__tree_special_functions(void) cl_git_pass(git_iterator_for_tree( &i, t, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); - cl_git_pass(git_iterator_current(&entry, i)); - while (entry != NULL) { + while (!(error = git_iterator_advance(&entry, i))) { + cl_assert(entry); + if (strcmp(entry->path, "sub/file") == 0) { cases++; check_tree_entry( @@ -338,11 +340,11 @@ void test_diff_iterator__tree_special_functions(void) "2929de282ce999e95183aedac6451d3384559c4b", rootoid, NULL); } - - cl_git_pass(git_iterator_advance(&entry, i)); } - + cl_assert_equal_i(GIT_ITEROVER, error); + cl_assert(!entry); cl_assert_equal_i(4, cases); + git_iterator_free(i); git_tree_free(t); } @@ -360,14 +362,15 @@ static void index_iterator_test( git_index *index; git_iterator *i; const git_index_entry *entry; - int count = 0; + int error, count = 0; git_repository *repo = cl_git_sandbox_init(sandbox); cl_git_pass(git_repository_index(&index, repo)); cl_git_pass(git_iterator_for_index(&i, index, 0, start, end)); - cl_git_pass(git_iterator_current(&entry, i)); - while (entry != NULL) { + while (!(error = git_iterator_advance(&entry, i))) { + cl_assert(entry); + if (expected_names != NULL) cl_assert_equal_s(expected_names[count], entry->path); @@ -378,13 +381,14 @@ static void index_iterator_test( } count++; - cl_git_pass(git_iterator_advance(&entry, i)); } + cl_assert_equal_i(GIT_ITEROVER, error); + cl_assert(!entry); + cl_assert_equal_i(expected_count, count); + git_iterator_free(i); git_index_free(index); - - cl_assert_equal_i(expected_count, count); } static const char *expected_index_0[] = { @@ -535,12 +539,15 @@ static void workdir_iterator_test( { git_iterator *i; const git_index_entry *entry; - int count = 0, count_all = 0, count_all_post_reset = 0; + int error, count = 0, count_all = 0, count_all_post_reset = 0; git_repository *repo = cl_git_sandbox_init(sandbox); cl_git_pass(git_iterator_for_workdir( &i, repo, GIT_ITERATOR_DONT_AUTOEXPAND, start, end)); - cl_git_pass(git_iterator_current(&entry, i)); + + error = git_iterator_current(&entry, i); + cl_assert((error == 0 && entry != NULL) || + (error == GIT_ITEROVER && entry == NULL)); while (entry != NULL) { int ignored = git_iterator_current_is_ignored(i); @@ -560,29 +567,39 @@ static void workdir_iterator_test( count++; count_all++; - cl_git_pass(git_iterator_advance(&entry, i)); + error = git_iterator_advance(&entry, i); + + cl_assert((error == 0 && entry != NULL) || + (error == GIT_ITEROVER && entry == NULL)); } + cl_assert_equal_i(expected_count, count); + cl_assert_equal_i(expected_count + expected_ignores, count_all); + cl_git_pass(git_iterator_reset(i, NULL, NULL)); - cl_git_pass(git_iterator_current(&entry, i)); + + error = git_iterator_current(&entry, i); + cl_assert((error == 0 && entry != NULL) || + (error == GIT_ITEROVER && entry == NULL)); while (entry != NULL) { if (S_ISDIR(entry->mode)) { cl_git_pass(git_iterator_advance_into(&entry, i)); continue; } + if (expected_names != NULL) cl_assert_equal_s( expected_names[count_all_post_reset], entry->path); count_all_post_reset++; - cl_git_pass(git_iterator_advance(&entry, i)); + + error = git_iterator_advance(&entry, i); + cl_assert(error == 0 || error == GIT_ITEROVER); } - git_iterator_free(i); - - cl_assert_equal_i(expected_count, count); - cl_assert_equal_i(expected_count + expected_ignores, count_all); cl_assert_equal_i(count_all, count_all_post_reset); + + git_iterator_free(i); } void test_diff_iterator__workdir_0(void) @@ -752,8 +769,10 @@ void test_diff_iterator__workdir_builtin_ignores(void) { /* it is possible to advance "into" a submodule */ cl_git_pass(git_iterator_advance_into(&entry, i)); - } else - cl_git_pass(git_iterator_advance(&entry, i)); + } else { + int error = git_iterator_advance(&entry, i); + cl_assert(!error || error == GIT_ITEROVER); + } } cl_assert(expected[idx].path == NULL); @@ -766,7 +785,7 @@ static void check_wd_first_through_third_range( { git_iterator *i; const git_index_entry *entry; - int idx; + int error, idx; static const char *expected[] = { "FIRST", "second", "THIRD", NULL }; cl_git_pass(git_iterator_for_workdir( @@ -776,7 +795,8 @@ static void check_wd_first_through_third_range( for (idx = 0; entry != NULL; ++idx) { cl_assert_equal_s(expected[idx], entry->path); - cl_git_pass(git_iterator_advance(&entry, i)); + error = git_iterator_advance(&entry, i); + cl_assert(!error || error == GIT_ITEROVER); } cl_assert(expected[idx] == NULL); @@ -814,8 +834,7 @@ static void check_tree_range( { git_tree *head; git_iterator *i; - const git_index_entry *entry; - int count; + int error, count; cl_git_pass(git_repository_head_tree(&head, repo)); @@ -824,13 +843,10 @@ static void check_tree_range( ignore_case ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE, start, end)); - cl_git_pass(git_iterator_current(&entry, i)); - - for (count = 0; entry != NULL; ) { - ++count; - cl_git_pass(git_iterator_advance(&entry, i)); - } + for (count = 0; !(error = git_iterator_advance(NULL, i)); ++count) + /* count em up */; + cl_assert_equal_i(GIT_ITEROVER, error); cl_assert_equal_i(expected_count, count); git_iterator_free(i); @@ -872,8 +888,7 @@ static void check_index_range( { git_index *index; git_iterator *i; - const git_index_entry *entry; - int count, caps; + int error, count, caps; bool is_ignoring_case; cl_git_pass(git_repository_index(&index, repo)); @@ -888,13 +903,10 @@ static void check_index_range( cl_assert(git_iterator_ignore_case(i) == ignore_case); - cl_git_pass(git_iterator_current(&entry, i)); - - for (count = 0; entry != NULL; ) { - ++count; - cl_git_pass(git_iterator_advance(&entry, i)); - } + for (count = 0; !(error = git_iterator_advance(NULL, i)); ++count) + /* count em up */; + cl_assert_equal_i(GIT_ITEROVER, error); cl_assert_equal_i(expected_count, count); git_iterator_free(i); diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index ab460735c..11a7d2a23 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -31,12 +31,11 @@ static void expect_iterator_items( if (expected_flat < 0) { v = true; expected_flat = -expected_flat; } if (expected_total < 0) { v = true; expected_total = -expected_total; } - count = 0; - cl_git_pass(git_iterator_current(&entry, i)); - if (v) fprintf(stderr, "== %s ==\n", no_trees ? "notrees" : "trees"); - while (entry != NULL) { + count = 0; + + while (!git_iterator_advance(&entry, i)) { if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode); if (no_trees) @@ -54,8 +53,6 @@ static void expect_iterator_items( cl_assert(entry->mode != GIT_FILEMODE_TREE); } - cl_git_pass(git_iterator_advance(&entry, i)); - if (++count > expected_flat) break; } @@ -93,10 +90,14 @@ static void expect_iterator_items( /* could return NOTFOUND if directory is empty */ cl_assert(!error || error == GIT_ENOTFOUND); - if (error == GIT_ENOTFOUND) - cl_git_pass(git_iterator_advance(&entry, i)); - } else - cl_git_pass(git_iterator_advance(&entry, i)); + if (error == GIT_ENOTFOUND) { + error = git_iterator_advance(&entry, i); + cl_assert(!error || error == GIT_ITEROVER); + } + } else { + error = git_iterator_advance(&entry, i); + cl_assert(!error || error == GIT_ITEROVER); + } if (++count > expected_total) break; diff --git a/tests-clar/submodule/status.c b/tests-clar/submodule/status.c index 88f388052..39c83a0b7 100644 --- a/tests-clar/submodule/status.c +++ b/tests-clar/submodule/status.c @@ -370,12 +370,9 @@ void test_submodule_status__iterator(void) cl_git_pass(git_iterator_for_workdir(&iter, g_repo, GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); - cl_git_pass(git_iterator_current(&entry, iter)); - for (i = 0; entry; ++i) { + for (i = 0; !git_iterator_advance(&entry, iter); ++i) cl_assert_equal_s(expected[i], entry->path); - cl_git_pass(git_iterator_advance(&entry, iter)); - } git_iterator_free(iter); From f658dc433cae351e72b1c8b245724eafb43f5844 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 31 May 2013 14:09:58 -0700 Subject: [PATCH 304/384] Zero memory for major objects before freeing By zeroing out the memory when we free larger objects (i.e. those that serve as collections of other data, such as repos, odb, refdb), I'm hoping that it will be easier for libgit2 bindings to find errors in their object management code. --- src/cache.c | 4 ++-- src/config.c | 4 +++- src/diff.c | 2 ++ src/index.c | 2 ++ src/odb.c | 2 ++ src/pack.c | 6 +++++- src/refdb.c | 1 + src/repository.c | 1 + 8 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/cache.c b/src/cache.c index dc3af063a..6205ef8a9 100644 --- a/src/cache.c +++ b/src/cache.c @@ -66,7 +66,7 @@ void git_cache_dump_stats(git_cache *cache) int git_cache_init(git_cache *cache) { - cache->used_memory = 0; + memset(cache, 0, sizeof(*cache)); cache->map = git_oidmap_alloc(); git_mutex_init(&cache->lock); return 0; @@ -102,9 +102,9 @@ void git_cache_clear(git_cache *cache) void git_cache_free(git_cache *cache) { git_cache_clear(cache); - git_oidmap_free(cache->map); git_mutex_free(&cache->lock); + memset(cache, 0, sizeof(*cache)); } /* Called with lock */ diff --git a/src/config.c b/src/config.c index 9491d267a..2c4b15540 100644 --- a/src/config.c +++ b/src/config.c @@ -40,12 +40,14 @@ static void config_free(git_config *cfg) size_t i; file_internal *internal; - for(i = 0; i < cfg->files.length; ++i){ + for (i = 0; i < cfg->files.length; ++i) { internal = git_vector_get(&cfg->files, i); GIT_REFCOUNT_DEC(internal, file_internal_free); } git_vector_free(&cfg->files); + + memset(cfg, 0, sizeof(*cfg)); git__free(cfg); } diff --git a/src/diff.c b/src/diff.c index b96ff4705..f1d1010b4 100644 --- a/src/diff.c +++ b/src/diff.c @@ -463,6 +463,8 @@ static void diff_list_free(git_diff_list *diff) git_pathspec_free(&diff->pathspec); git_pool_clear(&diff->pool); + + memset(diff, 0, sizeof(*diff)); git__free(diff); } diff --git a/src/index.c b/src/index.c index 45ce2f3d6..abc9495bd 100644 --- a/src/index.c +++ b/src/index.c @@ -348,6 +348,8 @@ static void index_free(git_index *index) git_vector_free(&index->reuc); git__free(index->index_file_path); + + memset(index, 0, sizeof(*index)); git__free(index); } diff --git a/src/odb.c b/src/odb.c index 9f18b5153..246f7d1ea 100644 --- a/src/odb.c +++ b/src/odb.c @@ -589,6 +589,8 @@ static void odb_free(git_odb *db) git_vector_free(&db->backends); git_cache_free(&db->own_cache); + + memset(db, 0, sizeof(*db)); git__free(db); } diff --git a/src/pack.c b/src/pack.c index 417d225f3..a9b835fca 100644 --- a/src/pack.c +++ b/src/pack.c @@ -85,13 +85,17 @@ static void cache_free(git_pack_cache *cache) git_offmap_free(cache->entries); git_mutex_free(&cache->lock); } + + memset(cache, 0, sizeof(*cache)); } static int cache_init(git_pack_cache *cache) { - memset(cache, 0, sizeof(git_pack_cache)); + memset(cache, 0, sizeof(*cache)); + cache->entries = git_offmap_alloc(); GITERR_CHECK_ALLOC(cache->entries); + cache->memory_limit = GIT_PACK_CACHE_MEMORY_LIMIT; git_mutex_init(&cache->lock); diff --git a/src/refdb.c b/src/refdb.c index 9f9037ce7..02244c908 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -89,6 +89,7 @@ int git_refdb_compress(git_refdb *db) static void refdb_free(git_refdb *db) { refdb_free_backend(db); + memset(db, 0, sizeof(*db)); git__free(db); } diff --git a/src/repository.c b/src/repository.c index 28505e822..8b16f00a4 100644 --- a/src/repository.c +++ b/src/repository.c @@ -113,6 +113,7 @@ void git_repository_free(git_repository *repo) git__free(repo->workdir); git__free(repo->namespace); + memset(repo, 0, sizeof(*repo)); git__free(repo); } From 1a42dd17eb2c35fa572418f5958595cbe297d229 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 31 May 2013 14:13:11 -0700 Subject: [PATCH 305/384] Mutex init can fail It is obviously quite a serious problem if this happens, but mutex initialization can fail and we should detect it. It's a bit like a memory allocation failure, in that you're probably pretty screwed if this occurs, but at least we'll catch it. --- src/cache.c | 5 ++++- src/global.c | 6 ++++-- src/pack-objects.c | 3 +++ src/pack.c | 16 ++++++++++++++-- src/thread-utils.h | 2 +- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/cache.c b/src/cache.c index 6205ef8a9..afc7c5b3a 100644 --- a/src/cache.c +++ b/src/cache.c @@ -68,7 +68,10 @@ int git_cache_init(git_cache *cache) { memset(cache, 0, sizeof(*cache)); cache->map = git_oidmap_alloc(); - git_mutex_init(&cache->lock); + if (git_mutex_init(&cache->lock)) { + giterr_set(GITERR_OS, "Failed to initialize cache mutex"); + return -1; + } return 0; } diff --git a/src/global.c b/src/global.c index a0571d127..2d40ca2fc 100644 --- a/src/global.c +++ b/src/global.c @@ -61,7 +61,8 @@ int git_threads_init(void) return 0; _tls_index = TlsAlloc(); - git_mutex_init(&git__mwindow_mutex); + if (git_mutex_init(&git__mwindow_mutex)) + return -1; /* Initialize any other subsystems that have global state */ if ((error = git_hash_global_init()) >= 0) @@ -121,7 +122,8 @@ int git_threads_init(void) if (_tls_init) return 0; - git_mutex_init(&git__mwindow_mutex); + if (git_mutex_init(&git__mwindow_mutex)) + return -1; pthread_key_create(&_tls_key, &cb__free_status); /* Initialize any other subsystems that have global state */ diff --git a/src/pack-objects.c b/src/pack-objects.c index 3d382026e..500104c55 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -132,7 +132,10 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo) if (git_mutex_init(&pb->cache_mutex) || git_mutex_init(&pb->progress_mutex) || git_cond_init(&pb->progress_cond)) + { + giterr_set(GITERR_OS, "Failed to initialize packbuilder mutex"); goto on_error; + } #endif diff --git a/src/pack.c b/src/pack.c index a9b835fca..7ce7099e0 100644 --- a/src/pack.c +++ b/src/pack.c @@ -97,7 +97,15 @@ static int cache_init(git_pack_cache *cache) GITERR_CHECK_ALLOC(cache->entries); cache->memory_limit = GIT_PACK_CACHE_MEMORY_LIMIT; - git_mutex_init(&cache->lock); + + if (git_mutex_init(&cache->lock)) { + giterr_set(GITERR_OS, "Failed to initialize pack cache mutex"); + + git__free(cache->entries); + cache->entries = NULL; + + return -1; + } return 0; } @@ -948,7 +956,11 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) p->mtime = (git_time_t)st.st_mtime; p->index_version = -1; - git_mutex_init(&p->lock); + if (git_mutex_init(&p->lock)) { + giterr_set(GITERR_OS, "Failed to initialize packfile mutex"); + git__free(p); + return -1; + } /* see if we can parse the sha1 oid in the packfile name */ if (path_len < 40 || diff --git a/src/thread-utils.h b/src/thread-utils.h index 49b5f3b5e..d4ed7e73b 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -140,7 +140,7 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend) /* Pthreads Mutex */ #define git_mutex unsigned int -#define git_mutex_init(a) (void)0 +#define git_mutex_init(a) 0 #define git_mutex_lock(a) 0 #define git_mutex_unlock(a) (void)0 #define git_mutex_free(a) (void)0 From 03a89070782f92d994feae960b1a8b3fb14adbc8 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 31 May 2013 21:49:40 -0700 Subject: [PATCH 306/384] Make git_index_read_tree preserve stat cache Instead of just blowing away the stat cache data when loading a new tree into the index, this checks if each loaded item has a corresponding existing item with the same OID and if so, copies the stat data from the old item to the new one so it will not be blown away. --- src/index.c | 68 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/src/index.c b/src/index.c index 45ce2f3d6..25c38b026 100644 --- a/src/index.c +++ b/src/index.c @@ -359,22 +359,25 @@ void git_index_free(git_index *index) GIT_REFCOUNT_DEC(index, index_free); } -void git_index_clear(git_index *index) +static void index_entries_free(git_vector *entries) { size_t i; - assert(index); - - for (i = 0; i < index->entries.length; ++i) { - git_index_entry *e; - e = git_vector_get(&index->entries, i); + for (i = 0; i < entries->length; ++i) { + git_index_entry *e = git_vector_get(entries, i); git__free(e->path); git__free(e); } - git_vector_clear(&index->entries); + git_vector_clear(entries); +} + +void git_index_clear(git_index *index) +{ + assert(index); + + index_entries_free(&index->entries); git_index_reuc_clear(index); - git_index_name_clear(index); git_futils_filestamp_set(&index->stamp, NULL); @@ -1951,13 +1954,14 @@ int git_index_entry_stage(const git_index_entry *entry) typedef struct read_tree_data { git_index *index; - git_transfer_progress *stats; + git_vector *old_entries; } read_tree_data; -static int read_tree_cb(const char *root, const git_tree_entry *tentry, void *data) +static int read_tree_cb( + const char *root, const git_tree_entry *tentry, void *payload) { - git_index *index = (git_index *)data; - git_index_entry *entry = NULL; + read_tree_data *data = payload; + git_index_entry *entry = NULL, *old_entry; git_buf path = GIT_BUF_INIT; if (git_tree_entry__is_tree(tentry)) @@ -1972,6 +1976,25 @@ static int read_tree_cb(const char *root, const git_tree_entry *tentry, void *da entry->mode = tentry->attr; entry->oid = tentry->oid; + /* look for corresponding old entry and copy data to new entry */ + if (data->old_entries) { + size_t pos; + struct entry_srch_key skey; + + skey.path = path.ptr; + skey.stage = 0; + + if (!git_vector_bsearch2( + &pos, data->old_entries, data->index->entries_search, &skey) && + (old_entry = git_vector_get(data->old_entries, pos)) != NULL && + entry->mode == old_entry->mode && + git_oid_equal(&entry->oid, &old_entry->oid)) + { + memcpy(entry, old_entry, sizeof(*entry)); + entry->flags_extended = 0; + } + } + if (path.size < GIT_IDXENTRY_NAMEMASK) entry->flags = path.size & GIT_IDXENTRY_NAMEMASK; else @@ -1980,7 +2003,7 @@ static int read_tree_cb(const char *root, const git_tree_entry *tentry, void *da entry->path = git_buf_detach(&path); git_buf_free(&path); - if (git_vector_insert(&index->entries, entry) < 0) { + if (git_vector_insert(&data->index->entries, entry) < 0) { index_entry_free(entry); return -1; } @@ -1990,9 +2013,26 @@ static int read_tree_cb(const char *root, const git_tree_entry *tentry, void *da int git_index_read_tree(git_index *index, const git_tree *tree) { + int error = 0; + git_vector entries = GIT_VECTOR_INIT; + read_tree_data data; + + git_vector_sort(&index->entries); + + entries._cmp = index->entries._cmp; + git_vector_swap(&entries, &index->entries); + git_index_clear(index); - return git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, index); + data.index = index; + data.old_entries = &entries; + + error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data); + + index_entries_free(&entries); + git_vector_sort(&index->entries); + + return error; } git_repository *git_index_owner(const git_index *index) From dc33b3d7b21b2003d6835a06fecf9ed4f4535f7e Mon Sep 17 00:00:00 2001 From: "Scott J. Goldman" Date: Sun, 2 Jun 2013 02:13:45 -0700 Subject: [PATCH 307/384] Don't bail on parsing commits with an invalid timezone git doesn't do that, and it's not something that's usually actionable to fix. if you have a git repository with one bad timezone in the history, it's too late to change it most likely. --- src/signature.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/signature.c b/src/signature.c index 1131fb789..cd6167fb4 100644 --- a/src/signature.c +++ b/src/signature.c @@ -174,8 +174,10 @@ int git_signature__parse(git_signature *sig, const char **buffer_out, tz_start = time_end + 1; if ((tz_start[0] != '-' && tz_start[0] != '+') || - git__strtol32(&offset, tz_start + 1, &tz_end, 10) < 0) - return signature_error("malformed timezone"); + git__strtol32(&offset, tz_start + 1, &tz_end, 10) < 0) { + //malformed timezone, just assume it's zero + offset = 0; + } hours = offset / 100; mins = offset % 100; From daf98cb2edaa2d8b45c3e88d2d2f4c5db7925b2f Mon Sep 17 00:00:00 2001 From: Jameson Miller Date: Tue, 4 Jun 2013 14:49:39 -0400 Subject: [PATCH 308/384] Allow creation of directories under the volume root in Win32 We ran into an issue where cloning a repository to a folder directly underneath the root of a volume (e.g. 'd:\libgit2') would fail with an access denied error. This was traced down to a call to make a directory that is the root (e.g. 'd:') could return an error indicated access denied instead of an error indicating the path already exists. This change now handles the access denied error on Win32 and checks for the existence of the folder. --- src/fileops.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index a3e43214f..a4f56e0db 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -277,7 +277,7 @@ int git_futils_mkdir( mode_t mode, uint32_t flags) { - int error = -1; + int error = -1, tmp_errno; git_buf make_path = GIT_BUF_INIT; ssize_t root = 0; char lastch, *tail; @@ -345,18 +345,26 @@ int git_futils_mkdir( already_exists = 1; break; +#ifdef GIT_WIN32 + case EACCES: +#endif case ENOSYS: - /* Solaris can generate this error if you try to mkdir - * a path which is already a mount point. In that case, - * the path does already exist; but it's not implied by + /* The following errors can be generated if: + * EACCES - Win32 can generate this error if you try to mkdir + * a path which is the root of a volume. + * ENOSYS - Solaris can generate a ENOSYS error if you try to mkdir + * a path which is already a mount point. + * In these cases, the path does already exist; but it's not implied by * the definition of the error, so let's recheck */ + tmp_errno = errno; + if (git_path_isdir(make_path.ptr)) { already_exists = 1; break; } /* Fall through */ - errno = ENOSYS; + errno = tmp_errno; default: giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); From b832ecf71c8e8d6787f9252b92e97153047a3ec3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 5 Jun 2013 09:46:51 -0700 Subject: [PATCH 309/384] Ensure git_futils_mkdir won't mkdir root This makes sure that git_futils_mkdir always skips over the root directory at a minimum, even on platforms where the root is not simply '/'. Also, this removes the GIT_WIN32 ifdef in favor of making EACCES as a potentially recoverable error on all platforms. --- src/fileops.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index a4f56e0db..8f308d258 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -277,7 +277,7 @@ int git_futils_mkdir( mode_t mode, uint32_t flags) { - int error = -1, tmp_errno; + int error = -1, tmp; git_buf make_path = GIT_BUF_INIT; ssize_t root = 0; char lastch, *tail; @@ -315,6 +315,11 @@ int git_futils_mkdir( if (root < 0) root = 0; + /* make sure mkdir root is at least after filesystem root */ + tmp = git_path_root(make_path.ptr); + if (root < tmp) + root = tmp; + tail = & make_path.ptr[root]; while (*tail) { @@ -345,18 +350,14 @@ int git_futils_mkdir( already_exists = 1; break; -#ifdef GIT_WIN32 - case EACCES: -#endif case ENOSYS: - /* The following errors can be generated if: - * EACCES - Win32 can generate this error if you try to mkdir - * a path which is the root of a volume. - * ENOSYS - Solaris can generate a ENOSYS error if you try to mkdir - * a path which is already a mount point. - * In these cases, the path does already exist; but it's not implied by - * the definition of the error, so let's recheck */ - tmp_errno = errno; + case EACCES: + /* Possible recoverable errors. These errors could occur + * on some OS if we try to mkdir at a network mount point + * or at the root of a volume. If the path is a dir, just + * treat as EEXIST. + */ + tmp = errno; if (git_path_isdir(make_path.ptr)) { already_exists = 1; @@ -364,7 +365,7 @@ int git_futils_mkdir( } /* Fall through */ - errno = tmp_errno; + errno = tmp; default: giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); From 2e1fa15fcd69ba41bb25be92e2218a5ff3dd47fb Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 5 Jun 2013 19:00:16 +0200 Subject: [PATCH 310/384] I'm a dick --- src/fileops.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index 8f308d258..088ae5e13 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -277,9 +277,9 @@ int git_futils_mkdir( mode_t mode, uint32_t flags) { - int error = -1, tmp; + int error = -1, tmp_errno; git_buf make_path = GIT_BUF_INIT; - ssize_t root = 0; + ssize_t root = 0, min_root_len; char lastch, *tail; /* build path and find "root" where we should start calling mkdir */ @@ -316,9 +316,9 @@ int git_futils_mkdir( root = 0; /* make sure mkdir root is at least after filesystem root */ - tmp = git_path_root(make_path.ptr); - if (root < tmp) - root = tmp; + min_root_len = git_path_root(make_path.ptr); + if (root < min_root_len) + root = min_root_len; tail = & make_path.ptr[root]; @@ -357,7 +357,7 @@ int git_futils_mkdir( * or at the root of a volume. If the path is a dir, just * treat as EEXIST. */ - tmp = errno; + tmp_errno = errno; if (git_path_isdir(make_path.ptr)) { already_exists = 1; @@ -365,7 +365,7 @@ int git_futils_mkdir( } /* Fall through */ - errno = tmp; + errno = tmp_errno; default: giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); From 999d4405a69e8d4d3d3bcd4a5d4bf45d8283b172 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 5 Jun 2013 12:02:28 -0700 Subject: [PATCH 311/384] Simplify git_futils_mkdir This routine was (is) pretty complicated, but given the recent changes, it seemed like it could be simplified a bit. --- src/fileops.c | 80 ++++++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 45 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index 088ae5e13..73b03f0a0 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -277,10 +277,11 @@ int git_futils_mkdir( mode_t mode, uint32_t flags) { - int error = -1, tmp_errno; + int error = -1, verify_final; git_buf make_path = GIT_BUF_INIT; ssize_t root = 0, min_root_len; char lastch, *tail; + struct stat st; /* build path and find "root" where we should start calling mkdir */ if (git_path_join_unrooted(&make_path, path, base, &root) < 0) @@ -288,7 +289,7 @@ int git_futils_mkdir( if (make_path.size == 0) { giterr_set(GITERR_OS, "Attempt to create empty path"); - goto fail; + goto done; } /* remove trailing slashes on path */ @@ -315,12 +316,15 @@ int git_futils_mkdir( if (root < 0) root = 0; + tail = &make_path.ptr[root]; + + /* is there any path to make? */ + verify_final = ((flags & GIT_MKDIR_VERIFY_DIR) != 0) && (*tail != 0); + /* make sure mkdir root is at least after filesystem root */ min_root_len = git_path_root(make_path.ptr); if (root < min_root_len) - root = min_root_len; - - tail = & make_path.ptr[root]; + tail = &make_path.ptr[min_root_len]; while (*tail) { /* advance tail to include next path component */ @@ -332,72 +336,58 @@ int git_futils_mkdir( /* truncate path at next component */ lastch = *tail; *tail = '\0'; + st.st_mode = 0; /* make directory */ if (p_mkdir(make_path.ptr, mode) < 0) { - int already_exists = 0; + int tmp_errno = errno; - switch (errno) { - case EEXIST: - if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 && - !git_path_isdir(make_path.ptr)) { - giterr_set( - GITERR_OS, "Existing path is not a directory '%s'", - make_path.ptr); - error = GIT_ENOTFOUND; - goto fail; - } - - already_exists = 1; - break; - case ENOSYS: - case EACCES: - /* Possible recoverable errors. These errors could occur - * on some OS if we try to mkdir at a network mount point - * or at the root of a volume. If the path is a dir, just - * treat as EEXIST. - */ - tmp_errno = errno; - - if (git_path_isdir(make_path.ptr)) { - already_exists = 1; - break; - } - - /* Fall through */ + /* ignore error if directory already exists */ + if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { errno = tmp_errno; - default: giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); - goto fail; + goto done; } - if (already_exists && (flags & GIT_MKDIR_EXCL) != 0) { + /* with exclusive create, existing dir is an error */ + if ((flags & GIT_MKDIR_EXCL) != 0) { giterr_set(GITERR_OS, "Directory already exists '%s'", make_path.ptr); error = GIT_EEXISTS; - goto fail; + goto done; } } - /* chmod if requested */ + /* chmod if requested and necessary */ if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 || - ((flags & GIT_MKDIR_CHMOD) != 0 && lastch == '\0')) + (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) { - if (p_chmod(make_path.ptr, mode) < 0) { + if (st.st_mode != mode && p_chmod(make_path.ptr, mode) < 0) { giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr); - goto fail; + goto done; } } + /* if we made it to the end, then final isdir check is not needed */ + if (lastch == '\0') + verify_final = false; + *tail = lastch; } - git_buf_free(&make_path); - return 0; + error = 0; -fail: + /* check that full path really is a directory if requested & needed */ + if (verify_final && + (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) { + giterr_set( + GITERR_OS, "Path is not a directory '%s'", make_path.ptr); + error = GIT_ENOTFOUND; + } + +done: git_buf_free(&make_path); return error; } From aad6967be1025b1819c3ba25163d7f69fc814130 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 29 May 2013 21:52:21 -0700 Subject: [PATCH 312/384] Basic function context header This implements a basic callback to extract function context for a diff. It always uses the same search heuristic right now with no regular expressions or language-specific variants. Those will come next, I think. --- src/diff_output.c | 83 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 16 deletions(-) diff --git a/src/diff_output.c b/src/diff_output.c index 8dd110cbf..bcd39f093 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -196,26 +196,77 @@ static int diff_delta_is_binary_by_size( return 0; } -static void setup_xdiff_options( - const git_diff_options *opts, xdemitconf_t *cfg, xpparam_t *param) +static long diff_context_find( + const char *line, + long line_len, + char *out, + long out_size, + void *payload) { - memset(cfg, 0, sizeof(xdemitconf_t)); - memset(param, 0, sizeof(xpparam_t)); + diff_context *ctxt = payload; + const char *scan; + bool found_paren = false; - cfg->ctxlen = - (!opts) ? 3 : opts->context_lines; - cfg->interhunkctxlen = - (!opts) ? 0 : opts->interhunk_lines; + if (line_len > 0 && line[line_len - 1] == '\n') + line_len--; + if (line_len > 0 && line[line_len - 1] == '\r') + line_len--; + if (!line_len) + return -1; - if (!opts) + if (!isalpha(*line)) + return -1; + + for (scan = &line[line_len - 1]; scan > line && *scan != '('; --scan) + /* search backward for ( */; + if (scan != line) { + found_paren = true; + line_len = scan - line; + + for (--scan; scan > line && !isalpha(*scan); --scan) + --line_len; + } + + if (!line_len) + return -1; + + if (out_size > line_len) { + memcpy(out, line, line_len); + + if (found_paren) + out[line_len++] = '('; + out[line_len] = '\0'; + } else { + memcpy(out, line, out_size); + line_len = out_size; + } + + return line_len; +} + +static void setup_xdiff_options(diff_context *ctxt) +{ + memset(&ctxt->xdiff_config, 0, sizeof(ctxt->xdiff_config)); + memset(&ctxt->xdiff_params, 0, sizeof(ctxt->xdiff_params)); + + ctxt->xdiff_config.ctxlen = + (!ctxt->opts) ? 3 : ctxt->opts->context_lines; + ctxt->xdiff_config.interhunkctxlen = + (!ctxt->opts) ? 0 : ctxt->opts->interhunk_lines; + + ctxt->xdiff_config.flags = XDL_EMIT_FUNCNAMES; + ctxt->xdiff_config.find_func = diff_context_find; + ctxt->xdiff_config.find_func_priv = ctxt; + + if (!ctxt->opts) return; - if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE) - param->flags |= XDF_WHITESPACE_FLAGS; - if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE) - param->flags |= XDF_IGNORE_WHITESPACE_CHANGE; - if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL) - param->flags |= XDF_IGNORE_WHITESPACE_AT_EOL; + if (ctxt->opts->flags & GIT_DIFF_IGNORE_WHITESPACE) + ctxt->xdiff_params.flags |= XDF_WHITESPACE_FLAGS; + if (ctxt->opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE) + ctxt->xdiff_params.flags |= XDF_IGNORE_WHITESPACE_CHANGE; + if (ctxt->opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL) + ctxt->xdiff_params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; } @@ -499,7 +550,7 @@ static int diff_context_init( ctxt->payload = payload; ctxt->error = 0; - setup_xdiff_options(ctxt->opts, &ctxt->xdiff_config, &ctxt->xdiff_params); + setup_xdiff_options(ctxt); return 0; } From 7000f3fa7bad25ec07355d6afb640ea272201dff Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 4 Jun 2013 10:32:59 -0700 Subject: [PATCH 313/384] Move some diff helpers into separate file --- src/diff_output.c | 424 +-------------------------------------------- src/diff_print.c | 431 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 433 insertions(+), 422 deletions(-) create mode 100644 src/diff_print.c diff --git a/src/diff_output.c b/src/diff_output.c index bcd39f093..9a5be2a10 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -207,6 +207,8 @@ static long diff_context_find( const char *scan; bool found_paren = false; + GIT_UNUSED(ctxt); + if (line_len > 0 && line[line_len - 1] == '\n') line_len--; if (line_len > 0 && line[line_len - 1] == '\r') @@ -1064,359 +1066,6 @@ int git_diff_foreach( return error; } - -typedef struct { - git_diff_list *diff; - git_diff_data_cb print_cb; - void *payload; - git_buf *buf; - int oid_strlen; -} diff_print_info; - -static int diff_print_info_init( - diff_print_info *pi, - git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload) -{ - assert(diff && diff->repo); - - pi->diff = diff; - pi->print_cb = cb; - pi->payload = payload; - pi->buf = out; - - if (git_repository__cvar(&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0) - return -1; - - pi->oid_strlen += 1; /* for NUL byte */ - - if (pi->oid_strlen < 2) - pi->oid_strlen = 2; - else if (pi->oid_strlen > GIT_OID_HEXSZ + 1) - pi->oid_strlen = GIT_OID_HEXSZ + 1; - - return 0; -} - -static char pick_suffix(int mode) -{ - if (S_ISDIR(mode)) - return '/'; - else if (mode & 0100) //-V536 - /* in git, modes are very regular, so we must have 0100755 mode */ - return '*'; - else - return ' '; -} - -char git_diff_status_char(git_delta_t status) -{ - char code; - - switch (status) { - case GIT_DELTA_ADDED: code = 'A'; break; - case GIT_DELTA_DELETED: code = 'D'; break; - case GIT_DELTA_MODIFIED: code = 'M'; break; - case GIT_DELTA_RENAMED: code = 'R'; break; - case GIT_DELTA_COPIED: code = 'C'; break; - case GIT_DELTA_IGNORED: code = 'I'; break; - case GIT_DELTA_UNTRACKED: code = '?'; break; - default: code = ' '; break; - } - - return code; -} - -static int callback_error(void) -{ - giterr_clear(); - return GIT_EUSER; -} - -static int print_compact( - const git_diff_delta *delta, float progress, void *data) -{ - diff_print_info *pi = data; - char old_suffix, new_suffix, code = git_diff_status_char(delta->status); - - GIT_UNUSED(progress); - - if (code == ' ') - return 0; - - old_suffix = pick_suffix(delta->old_file.mode); - new_suffix = pick_suffix(delta->new_file.mode); - - git_buf_clear(pi->buf); - - if (delta->old_file.path != delta->new_file.path && - pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0) - git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code, - delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); - else if (delta->old_file.mode != delta->new_file.mode && - delta->old_file.mode != 0 && delta->new_file.mode != 0) - git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code, - delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode); - else if (old_suffix != ' ') - git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); - else - git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path); - - if (git_buf_oom(pi->buf)) - return -1; - - if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, - git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - return callback_error(); - - return 0; -} - -int git_diff_print_compact( - git_diff_list *diff, - git_diff_data_cb print_cb, - void *payload) -{ - int error; - git_buf buf = GIT_BUF_INIT; - diff_print_info pi; - - if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) - error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi); - - git_buf_free(&buf); - - return error; -} - -static int print_raw( - const git_diff_delta *delta, float progress, void *data) -{ - diff_print_info *pi = data; - char code = git_diff_status_char(delta->status); - char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; - - GIT_UNUSED(progress); - - if (code == ' ') - return 0; - - git_buf_clear(pi->buf); - - git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid); - git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid); - - git_buf_printf( - pi->buf, ":%06o %06o %s... %s... %c", - delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); - - if (delta->similarity > 0) - git_buf_printf(pi->buf, "%03u", delta->similarity); - - if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED) - git_buf_printf( - pi->buf, "\t%s %s\n", delta->old_file.path, delta->new_file.path); - else - git_buf_printf( - pi->buf, "\t%s\n", delta->old_file.path ? - delta->old_file.path : delta->new_file.path); - - if (git_buf_oom(pi->buf)) - return -1; - - if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, - git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - return callback_error(); - - return 0; -} - -int git_diff_print_raw( - git_diff_list *diff, - git_diff_data_cb print_cb, - void *payload) -{ - int error; - git_buf buf = GIT_BUF_INIT; - diff_print_info pi; - - if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) - error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi); - - git_buf_free(&buf); - - return error; -} - -static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta) -{ - char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; - - git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid); - git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid); - - /* TODO: Match git diff more closely */ - if (delta->old_file.mode == delta->new_file.mode) { - git_buf_printf(pi->buf, "index %s..%s %o\n", - start_oid, end_oid, delta->old_file.mode); - } else { - if (delta->old_file.mode == 0) { - git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode); - } else if (delta->new_file.mode == 0) { - git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode); - } else { - git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode); - git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode); - } - git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid); - } - - if (git_buf_oom(pi->buf)) - return -1; - - return 0; -} - -static int print_patch_file( - const git_diff_delta *delta, float progress, void *data) -{ - diff_print_info *pi = data; - const char *oldpfx = pi->diff->opts.old_prefix; - const char *oldpath = delta->old_file.path; - const char *newpfx = pi->diff->opts.new_prefix; - const char *newpath = delta->new_file.path; - - GIT_UNUSED(progress); - - if (S_ISDIR(delta->new_file.mode) || - delta->status == GIT_DELTA_UNMODIFIED || - delta->status == GIT_DELTA_IGNORED || - (delta->status == GIT_DELTA_UNTRACKED && - (pi->diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0)) - return 0; - - if (!oldpfx) - oldpfx = DIFF_OLD_PREFIX_DEFAULT; - - if (!newpfx) - newpfx = DIFF_NEW_PREFIX_DEFAULT; - - git_buf_clear(pi->buf); - git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path); - - if (print_oid_range(pi, delta) < 0) - return -1; - - if (git_oid_iszero(&delta->old_file.oid)) { - oldpfx = ""; - oldpath = "/dev/null"; - } - if (git_oid_iszero(&delta->new_file.oid)) { - newpfx = ""; - newpath = "/dev/null"; - } - - if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) { - git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath); - git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath); - } - - if (git_buf_oom(pi->buf)) - return -1; - - if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, - git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - return callback_error(); - - if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) - return 0; - - git_buf_clear(pi->buf); - git_buf_printf( - pi->buf, "Binary files %s%s and %s%s differ\n", - oldpfx, oldpath, newpfx, newpath); - if (git_buf_oom(pi->buf)) - return -1; - - if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY, - git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - return callback_error(); - - return 0; -} - -static int print_patch_hunk( - const git_diff_delta *d, - const git_diff_range *r, - const char *header, - size_t header_len, - void *data) -{ - diff_print_info *pi = data; - - if (S_ISDIR(d->new_file.mode)) - return 0; - - git_buf_clear(pi->buf); - if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0) - return -1; - - if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR, - git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - return callback_error(); - - return 0; -} - -static int print_patch_line( - const git_diff_delta *delta, - const git_diff_range *range, - char line_origin, /* GIT_DIFF_LINE value from above */ - const char *content, - size_t content_len, - void *data) -{ - diff_print_info *pi = data; - - if (S_ISDIR(delta->new_file.mode)) - return 0; - - git_buf_clear(pi->buf); - - if (line_origin == GIT_DIFF_LINE_ADDITION || - line_origin == GIT_DIFF_LINE_DELETION || - line_origin == GIT_DIFF_LINE_CONTEXT) - git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content); - else if (content_len > 0) - git_buf_printf(pi->buf, "%.*s", (int)content_len, content); - - if (git_buf_oom(pi->buf)) - return -1; - - if (pi->print_cb(delta, range, line_origin, - git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) - return callback_error(); - - return 0; -} - -int git_diff_print_patch( - git_diff_list *diff, - git_diff_data_cb print_cb, - void *payload) -{ - int error; - git_buf buf = GIT_BUF_INIT; - diff_print_info pi; - - if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) - error = git_diff_foreach( - diff, print_patch_file, print_patch_hunk, print_patch_line, &pi); - - git_buf_free(&buf); - - return error; -} - static void set_data_from_blob( const git_blob *blob, git_map *map, git_diff_file *file) { @@ -1826,75 +1475,6 @@ notfound: return diff_error_outofrange(thing); } -static int print_to_buffer_cb( - const git_diff_delta *delta, - const git_diff_range *range, - char line_origin, - const char *content, - size_t content_len, - void *payload) -{ - git_buf *output = payload; - GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin); - return git_buf_put(output, content, content_len); -} - -int git_diff_patch_print( - git_diff_patch *patch, - git_diff_data_cb print_cb, - void *payload) -{ - int error; - git_buf temp = GIT_BUF_INIT; - diff_print_info pi; - size_t h, l; - - assert(patch && print_cb); - - if (!(error = diff_print_info_init( - &pi, &temp, patch->diff, print_cb, payload))) - error = print_patch_file(patch->delta, 0, &pi); - - for (h = 0; h < patch->hunks_size && !error; ++h) { - diff_patch_hunk *hunk = &patch->hunks[h]; - - error = print_patch_hunk( - patch->delta, &hunk->range, hunk->header, hunk->header_len, &pi); - - for (l = 0; l < hunk->line_count && !error; ++l) { - diff_patch_line *line = &patch->lines[hunk->line_start + l]; - - error = print_patch_line( - patch->delta, &hunk->range, - line->origin, line->ptr, line->len, &pi); - } - } - - git_buf_free(&temp); - - return error; -} - -int git_diff_patch_to_str( - char **string, - git_diff_patch *patch) -{ - int error; - git_buf output = GIT_BUF_INIT; - - error = git_diff_patch_print(patch, print_to_buffer_cb, &output); - - /* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1, - * meaning a memory allocation failure, so just map to -1... - */ - if (error == GIT_EUSER) - error = -1; - - *string = git_buf_detach(&output); - - return error; -} - int git_diff__paired_foreach( git_diff_list *idx2head, git_diff_list *wd2idx, diff --git a/src/diff_print.c b/src/diff_print.c new file mode 100644 index 000000000..b6fbec829 --- /dev/null +++ b/src/diff_print.c @@ -0,0 +1,431 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#include "common.h" +#include "diff.h" +#include "diff_output.h" + +typedef struct { + git_diff_list *diff; + git_diff_data_cb print_cb; + void *payload; + git_buf *buf; + int oid_strlen; +} diff_print_info; + +static int diff_print_info_init( + diff_print_info *pi, + git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload) +{ + assert(diff && diff->repo); + + pi->diff = diff; + pi->print_cb = cb; + pi->payload = payload; + pi->buf = out; + + if (git_repository__cvar(&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0) + return -1; + + pi->oid_strlen += 1; /* for NUL byte */ + + if (pi->oid_strlen < 2) + pi->oid_strlen = 2; + else if (pi->oid_strlen > GIT_OID_HEXSZ + 1) + pi->oid_strlen = GIT_OID_HEXSZ + 1; + + return 0; +} + +static char pick_suffix(int mode) +{ + if (S_ISDIR(mode)) + return '/'; + else if (mode & 0100) //-V536 + /* in git, modes are very regular, so we must have 0100755 mode */ + return '*'; + else + return ' '; +} + +char git_diff_status_char(git_delta_t status) +{ + char code; + + switch (status) { + case GIT_DELTA_ADDED: code = 'A'; break; + case GIT_DELTA_DELETED: code = 'D'; break; + case GIT_DELTA_MODIFIED: code = 'M'; break; + case GIT_DELTA_RENAMED: code = 'R'; break; + case GIT_DELTA_COPIED: code = 'C'; break; + case GIT_DELTA_IGNORED: code = 'I'; break; + case GIT_DELTA_UNTRACKED: code = '?'; break; + default: code = ' '; break; + } + + return code; +} + +static int callback_error(void) +{ + giterr_clear(); + return GIT_EUSER; +} + +static int print_compact( + const git_diff_delta *delta, float progress, void *data) +{ + diff_print_info *pi = data; + char old_suffix, new_suffix, code = git_diff_status_char(delta->status); + + GIT_UNUSED(progress); + + if (code == ' ') + return 0; + + old_suffix = pick_suffix(delta->old_file.mode); + new_suffix = pick_suffix(delta->new_file.mode); + + git_buf_clear(pi->buf); + + if (delta->old_file.path != delta->new_file.path && + pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0) + git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code, + delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); + else if (delta->old_file.mode != delta->new_file.mode && + delta->old_file.mode != 0 && delta->new_file.mode != 0) + git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code, + delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode); + else if (old_suffix != ' ') + git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); + else + git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path); + + if (git_buf_oom(pi->buf)) + return -1; + + if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, + git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) + return callback_error(); + + return 0; +} + +int git_diff_print_compact( + git_diff_list *diff, + git_diff_data_cb print_cb, + void *payload) +{ + int error; + git_buf buf = GIT_BUF_INIT; + diff_print_info pi; + + if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) + error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi); + + git_buf_free(&buf); + + return error; +} + +static int print_raw( + const git_diff_delta *delta, float progress, void *data) +{ + diff_print_info *pi = data; + char code = git_diff_status_char(delta->status); + char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; + + GIT_UNUSED(progress); + + if (code == ' ') + return 0; + + git_buf_clear(pi->buf); + + git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid); + git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid); + + git_buf_printf( + pi->buf, ":%06o %06o %s... %s... %c", + delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); + + if (delta->similarity > 0) + git_buf_printf(pi->buf, "%03u", delta->similarity); + + if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED) + git_buf_printf( + pi->buf, "\t%s %s\n", delta->old_file.path, delta->new_file.path); + else + git_buf_printf( + pi->buf, "\t%s\n", delta->old_file.path ? + delta->old_file.path : delta->new_file.path); + + if (git_buf_oom(pi->buf)) + return -1; + + if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, + git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) + return callback_error(); + + return 0; +} + +int git_diff_print_raw( + git_diff_list *diff, + git_diff_data_cb print_cb, + void *payload) +{ + int error; + git_buf buf = GIT_BUF_INIT; + diff_print_info pi; + + if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) + error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi); + + git_buf_free(&buf); + + return error; +} + +static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta) +{ + char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; + + git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid); + git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid); + + /* TODO: Match git diff more closely */ + if (delta->old_file.mode == delta->new_file.mode) { + git_buf_printf(pi->buf, "index %s..%s %o\n", + start_oid, end_oid, delta->old_file.mode); + } else { + if (delta->old_file.mode == 0) { + git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode); + } else if (delta->new_file.mode == 0) { + git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode); + } else { + git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode); + git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode); + } + git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid); + } + + if (git_buf_oom(pi->buf)) + return -1; + + return 0; +} + +static int print_patch_file( + const git_diff_delta *delta, float progress, void *data) +{ + diff_print_info *pi = data; + const char *oldpfx = pi->diff->opts.old_prefix; + const char *oldpath = delta->old_file.path; + const char *newpfx = pi->diff->opts.new_prefix; + const char *newpath = delta->new_file.path; + + GIT_UNUSED(progress); + + if (S_ISDIR(delta->new_file.mode) || + delta->status == GIT_DELTA_UNMODIFIED || + delta->status == GIT_DELTA_IGNORED || + (delta->status == GIT_DELTA_UNTRACKED && + (pi->diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0)) + return 0; + + if (!oldpfx) + oldpfx = DIFF_OLD_PREFIX_DEFAULT; + + if (!newpfx) + newpfx = DIFF_NEW_PREFIX_DEFAULT; + + git_buf_clear(pi->buf); + git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path); + + if (print_oid_range(pi, delta) < 0) + return -1; + + if (git_oid_iszero(&delta->old_file.oid)) { + oldpfx = ""; + oldpath = "/dev/null"; + } + if (git_oid_iszero(&delta->new_file.oid)) { + newpfx = ""; + newpath = "/dev/null"; + } + + if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) { + git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath); + git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath); + } + + if (git_buf_oom(pi->buf)) + return -1; + + if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, + git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) + return callback_error(); + + if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) + return 0; + + git_buf_clear(pi->buf); + git_buf_printf( + pi->buf, "Binary files %s%s and %s%s differ\n", + oldpfx, oldpath, newpfx, newpath); + if (git_buf_oom(pi->buf)) + return -1; + + if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY, + git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) + return callback_error(); + + return 0; +} + +static int print_patch_hunk( + const git_diff_delta *d, + const git_diff_range *r, + const char *header, + size_t header_len, + void *data) +{ + diff_print_info *pi = data; + + if (S_ISDIR(d->new_file.mode)) + return 0; + + git_buf_clear(pi->buf); + if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0) + return -1; + + if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR, + git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) + return callback_error(); + + return 0; +} + +static int print_patch_line( + const git_diff_delta *delta, + const git_diff_range *range, + char line_origin, /* GIT_DIFF_LINE value from above */ + const char *content, + size_t content_len, + void *data) +{ + diff_print_info *pi = data; + + if (S_ISDIR(delta->new_file.mode)) + return 0; + + git_buf_clear(pi->buf); + + if (line_origin == GIT_DIFF_LINE_ADDITION || + line_origin == GIT_DIFF_LINE_DELETION || + line_origin == GIT_DIFF_LINE_CONTEXT) + git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content); + else if (content_len > 0) + git_buf_printf(pi->buf, "%.*s", (int)content_len, content); + + if (git_buf_oom(pi->buf)) + return -1; + + if (pi->print_cb(delta, range, line_origin, + git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) + return callback_error(); + + return 0; +} + +int git_diff_print_patch( + git_diff_list *diff, + git_diff_data_cb print_cb, + void *payload) +{ + int error; + git_buf buf = GIT_BUF_INIT; + diff_print_info pi; + + if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) + error = git_diff_foreach( + diff, print_patch_file, print_patch_hunk, print_patch_line, &pi); + + git_buf_free(&buf); + + return error; +} + + +static int print_to_buffer_cb( + const git_diff_delta *delta, + const git_diff_range *range, + char line_origin, + const char *content, + size_t content_len, + void *payload) +{ + git_buf *output = payload; + GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin); + return git_buf_put(output, content, content_len); +} + +int git_diff_patch_print( + git_diff_patch *patch, + git_diff_data_cb print_cb, + void *payload) +{ + int error; + git_buf temp = GIT_BUF_INIT; + diff_print_info pi; + size_t h, l; + + assert(patch && print_cb); + + if (!(error = diff_print_info_init( + &pi, &temp, patch->diff, print_cb, payload))) + error = print_patch_file(patch->delta, 0, &pi); + + for (h = 0; h < patch->hunks_size && !error; ++h) { + diff_patch_hunk *hunk = &patch->hunks[h]; + + error = print_patch_hunk( + patch->delta, &hunk->range, hunk->header, hunk->header_len, &pi); + + for (l = 0; l < hunk->line_count && !error; ++l) { + diff_patch_line *line = &patch->lines[hunk->line_start + l]; + + error = print_patch_line( + patch->delta, &hunk->range, + line->origin, line->ptr, line->len, &pi); + } + } + + git_buf_free(&temp); + + return error; +} + +int git_diff_patch_to_str( + char **string, + git_diff_patch *patch) +{ + int error; + git_buf output = GIT_BUF_INIT; + + error = git_diff_patch_print(patch, print_to_buffer_cb, &output); + + /* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1, + * meaning a memory allocation failure, so just map to -1... + */ + if (error == GIT_EUSER) + error = -1; + + *string = git_buf_detach(&output); + + return error; +} From f7e5615086134f1d433a1b7a03ee9bbbbe0844af Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 5 Jun 2013 15:41:42 -0700 Subject: [PATCH 314/384] Make mkdir early exit cases clearer There are two places where git_futils_mkdir should exit early or at least do less. The first is when using GIT_MKDIR_SKIP_LAST and having that flag leave no directory left to create; it was being handled previously, but the behavior was subtle. Now I put in a clear explicit check that exits early in that case. The second is when there is no directory to create, but there is a valid path that should be verified. I shifted the logic a bit so we'll be better about not entering the loop than that happens. --- src/fileops.c | 65 ++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index 73b03f0a0..02f48e120 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -277,10 +277,10 @@ int git_futils_mkdir( mode_t mode, uint32_t flags) { - int error = -1, verify_final; + int error = -1; git_buf make_path = GIT_BUF_INIT; ssize_t root = 0, min_root_len; - char lastch, *tail; + char lastch = '/', *tail; struct stat st; /* build path and find "root" where we should start calling mkdir */ @@ -306,27 +306,32 @@ int git_futils_mkdir( if ((flags & GIT_MKDIR_SKIP_LAST) != 0) git_buf_rtruncate_at_char(&make_path, '/'); + /* if nothing left after truncation, then we're done! */ + if (!make_path.size) { + error = 0; + goto done; + } + /* if we are not supposed to make the whole path, reset root */ if ((flags & GIT_MKDIR_PATH) == 0) root = git_buf_rfind(&make_path, '/'); + /* advance root past drive name or network mount prefix */ + min_root_len = git_path_root(make_path.ptr); + if (root < min_root_len) + root = min_root_len; + while (make_path.ptr[root] == '/') + ++root; + /* clip root to make_path length */ - if (root >= (ssize_t)make_path.size) - root = (ssize_t)make_path.size - 1; + if (root > (ssize_t)make_path.size) + root = (ssize_t)make_path.size; /* i.e. NUL byte of string */ if (root < 0) root = 0; - tail = &make_path.ptr[root]; + /* walk down tail of path making each directory */ + for (tail = &make_path.ptr[root]; *tail; *tail = lastch) { - /* is there any path to make? */ - verify_final = ((flags & GIT_MKDIR_VERIFY_DIR) != 0) && (*tail != 0); - - /* make sure mkdir root is at least after filesystem root */ - min_root_len = git_path_root(make_path.ptr); - if (root < min_root_len) - tail = &make_path.ptr[min_root_len]; - - while (*tail) { /* advance tail to include next path component */ while (*tail == '/') tail++; @@ -345,45 +350,35 @@ int git_futils_mkdir( /* ignore error if directory already exists */ if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { errno = tmp_errno; - giterr_set(GITERR_OS, "Failed to make directory '%s'", - make_path.ptr); + giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); goto done; } /* with exclusive create, existing dir is an error */ if ((flags & GIT_MKDIR_EXCL) != 0) { - giterr_set(GITERR_OS, "Directory already exists '%s'", - make_path.ptr); + giterr_set(GITERR_OS, "Directory already exists '%s'", make_path.ptr); error = GIT_EEXISTS; goto done; } } /* chmod if requested and necessary */ - if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 || - (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) - { - if (st.st_mode != mode && p_chmod(make_path.ptr, mode) < 0) { - giterr_set(GITERR_OS, "Failed to set permissions on '%s'", - make_path.ptr); - goto done; - } + if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 || + (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) && + st.st_mode != mode && + (error = p_chmod(make_path.ptr, mode)) < 0) { + giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr); + goto done; } - - /* if we made it to the end, then final isdir check is not needed */ - if (lastch == '\0') - verify_final = false; - - *tail = lastch; } error = 0; /* check that full path really is a directory if requested & needed */ - if (verify_final && + if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 && + lastch != '\0' && (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) { - giterr_set( - GITERR_OS, "Path is not a directory '%s'", make_path.ptr); + giterr_set(GITERR_OS, "Path is not a directory '%s'", make_path.ptr); error = GIT_ENOTFOUND; } From 3e9e6cdaff8acb11399736abbf793bf2d000d037 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 7 Jun 2013 09:54:33 -0700 Subject: [PATCH 315/384] Add safe memset and use it This adds a `git__memset` routine that will not be optimized away and updates the places where I memset() right before a free() call to use it. --- src/cache.c | 2 +- src/config.c | 2 +- src/diff.c | 2 +- src/index.c | 2 +- src/odb.c | 2 +- src/refdb.c | 2 +- src/repository.c | 6 ++---- src/util.c | 10 ++++++++++ src/util.h | 11 ++++++++--- 9 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/cache.c b/src/cache.c index afc7c5b3a..570838e44 100644 --- a/src/cache.c +++ b/src/cache.c @@ -107,7 +107,7 @@ void git_cache_free(git_cache *cache) git_cache_clear(cache); git_oidmap_free(cache->map); git_mutex_free(&cache->lock); - memset(cache, 0, sizeof(*cache)); + git__memset(cache, 0, sizeof(*cache)); } /* Called with lock */ diff --git a/src/config.c b/src/config.c index 2c4b15540..75cbe348c 100644 --- a/src/config.c +++ b/src/config.c @@ -47,7 +47,7 @@ static void config_free(git_config *cfg) git_vector_free(&cfg->files); - memset(cfg, 0, sizeof(*cfg)); + git__memset(cfg, 0, sizeof(*cfg)); git__free(cfg); } diff --git a/src/diff.c b/src/diff.c index f1d1010b4..982d64051 100644 --- a/src/diff.c +++ b/src/diff.c @@ -464,7 +464,7 @@ static void diff_list_free(git_diff_list *diff) git_pathspec_free(&diff->pathspec); git_pool_clear(&diff->pool); - memset(diff, 0, sizeof(*diff)); + git__memset(diff, 0, sizeof(*diff)); git__free(diff); } diff --git a/src/index.c b/src/index.c index abc9495bd..2bb7d6ee6 100644 --- a/src/index.c +++ b/src/index.c @@ -349,7 +349,7 @@ static void index_free(git_index *index) git__free(index->index_file_path); - memset(index, 0, sizeof(*index)); + git__memset(index, 0, sizeof(*index)); git__free(index); } diff --git a/src/odb.c b/src/odb.c index 246f7d1ea..5e27edacd 100644 --- a/src/odb.c +++ b/src/odb.c @@ -590,7 +590,7 @@ static void odb_free(git_odb *db) git_vector_free(&db->backends); git_cache_free(&db->own_cache); - memset(db, 0, sizeof(*db)); + git__memset(db, 0, sizeof(*db)); git__free(db); } diff --git a/src/refdb.c b/src/refdb.c index 02244c908..4271b58e4 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -89,7 +89,7 @@ int git_refdb_compress(git_refdb *db) static void refdb_free(git_refdb *db) { refdb_free_backend(db); - memset(db, 0, sizeof(*db)); + git__memset(db, 0, sizeof(*db)); git__free(db); } diff --git a/src/repository.c b/src/repository.c index 8b16f00a4..ee6c5bad4 100644 --- a/src/repository.c +++ b/src/repository.c @@ -113,7 +113,7 @@ void git_repository_free(git_repository *repo) git__free(repo->workdir); git__free(repo->namespace); - memset(repo, 0, sizeof(*repo)); + git__memset(repo, 0, sizeof(*repo)); git__free(repo); } @@ -140,12 +140,10 @@ static bool valid_repository_path(git_buf *repository_path) static git_repository *repository_alloc(void) { - git_repository *repo = git__malloc(sizeof(git_repository)); + git_repository *repo = git__calloc(1, sizeof(git_repository)); if (!repo) return NULL; - memset(repo, 0x0, sizeof(git_repository)); - if (git_cache_init(&repo->objects) < 0) { git__free(repo); return NULL; diff --git a/src/util.c b/src/util.c index da15a039d..248cf4c42 100644 --- a/src/util.c +++ b/src/util.c @@ -722,3 +722,13 @@ void git__insertsort_r( if (freeswap) git__free(swapel); } + +void git__memset(void *data, int c, size_t size) +{ + volatile uint8_t *scan = data; + uint8_t *end = scan + size; + uint8_t val = (uint8_t)c; + + while (scan < end) + *scan++ = val; +} diff --git a/src/util.h b/src/util.h index 5ae87ac10..fd3ea22ed 100644 --- a/src/util.h +++ b/src/util.h @@ -293,8 +293,7 @@ GIT_INLINE(bool) git__iswildcard(int c) } /* - * Parse a string value as a boolean, just like Core Git - * does. + * Parse a string value as a boolean, just like Core Git does. * * Valid values for true are: 'true', 'yes', 'on' * Valid values for false are: 'false', 'no', 'off' @@ -309,7 +308,7 @@ extern int git__parse_bool(int *out, const char *value); * - "July 17, 2003" * - "2003-7-17 08:23" */ -int git__date_parse(git_time_t *out, const char *date); +extern int git__date_parse(git_time_t *out, const char *date); /* * Unescapes a string in-place. @@ -320,4 +319,10 @@ int git__date_parse(git_time_t *out, const char *date); */ extern size_t git__unescape(char *str); +/* + * Memset that will not be optimized away by the compiler. + * You usually should just use regular `memset()`. + */ +extern void git__memset(void *data, int c, size_t size); + #endif /* INCLUDE_util_h__ */ From 114f5a6c41ea03393e00ae41126a6ddb0ef39a15 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 10 Jun 2013 10:10:39 -0700 Subject: [PATCH 316/384] Reorganize diff and add basic diff driver This is a significant reorganization of the diff code to break it into a set of more clearly distinct files and to document the new organization. Hopefully this will make the diff code easier to understand and to extend. This adds a new `git_diff_driver` object that looks of diff driver information from the attributes and the config so that things like function content in diff headers can be provided. The full driver spec is not implemented in the commit - this is focused on the reorganization of the code and putting the driver hooks in place. This also removes a few #includes from src/repository.h that were overbroad, but as a result required extra #includes in a variety of places since including src/repository.h no longer results in pulling in the whole world. --- docs/diff-internals.md | 89 ++ src/blob.c | 1 + src/checkout.c | 1 + src/clone.c | 1 + src/crlf.c | 6 +- src/diff.c | 72 ++ src/diff.h | 11 + src/diff_driver.c | 160 ++++ src/diff_driver.h | 34 + src/diff_file.c | 442 +++++++++ src/diff_file.h | 54 ++ src/diff_output.c | 1526 ------------------------------ src/diff_output.h | 93 -- src/diff_patch.c | 723 ++++++++++++++ src/diff_patch.h | 75 ++ src/diff_print.c | 10 +- src/diff_tform.c | 6 +- src/diff_xdiff.c | 161 ++++ src/diff_xdiff.h | 28 + src/fetch.c | 2 + src/iterator.c | 1 + src/merge.c | 2 + src/refdb_fs.c | 1 + src/refs.c | 1 + src/remote.h | 2 +- src/repository.c | 4 + src/repository.h | 5 +- src/signature.c | 1 + src/stash.c | 1 + src/status.c | 2 +- src/submodule.c | 2 + src/thread-utils.h | 2 - src/tree.c | 3 + src/util.h | 2 + tests-clar/checkout/index.c | 1 + tests-clar/clar.c | 36 +- tests-clar/clone/nonetwork.c | 3 +- tests-clar/diff/patch.c | 2 +- tests-clar/diff/rename.c | 2 +- tests-clar/diff/submodules.c | 1 + tests-clar/fetchhead/nonetwork.c | 2 +- tests-clar/merge/merge_helpers.c | 2 +- tests-clar/odb/alternates.c | 2 +- tests-clar/online/clone.c | 3 +- tests-clar/online/fetchhead.c | 2 +- tests-clar/refs/delete.c | 5 +- tests-clar/refs/pack.c | 10 +- tests-clar/refs/reflog/reflog.c | 2 +- tests-clar/refs/rename.c | 13 +- tests-clar/repo/discover.c | 2 +- tests-clar/status/ignore.c | 2 +- tests-clar/status/worktree.c | 2 +- 52 files changed, 1951 insertions(+), 1665 deletions(-) create mode 100644 docs/diff-internals.md create mode 100644 src/diff_driver.c create mode 100644 src/diff_driver.h create mode 100644 src/diff_file.c create mode 100644 src/diff_file.h delete mode 100644 src/diff_output.c delete mode 100644 src/diff_output.h create mode 100644 src/diff_patch.c create mode 100644 src/diff_patch.h create mode 100644 src/diff_xdiff.c create mode 100644 src/diff_xdiff.h diff --git a/docs/diff-internals.md b/docs/diff-internals.md new file mode 100644 index 000000000..1983b7939 --- /dev/null +++ b/docs/diff-internals.md @@ -0,0 +1,89 @@ +Diff is broken into four phases: + +1. Building a list of things that have changed. These changes are called + deltas (git_diff_delta objects) and are grouped into a git_diff_list. +2. Applying file similarity measurement for rename and copy detection (and + to potentially split files that have changed radically). This step is + optional. +3. Computing the textual diff for each delta. Not all deltas have a + meaningful textual diff. For those that do, the textual diff can + either be generated on the fly and passed to output callbacks or can be + turned into a git_diff_patch object. +4. Formatting the diff and/or patch into standard text formats (such as + patches, raw lists, etc). + +In the source code, step 1 is implemented in `src/diff.c`, step 2 in +`src/diff_tform.c`, step 3 in `src/diff_patch.c`, and step 4 in +`src/diff_print.c`. Additionally, when it comes to accessing file +content, everything goes through diff drivers that are implemented in +`src/diff_driver.c`. + +External Objects +---------------- + +* `git_diff_options` repesents user choices about how a diff should be + performed and is passed to most diff generating functions. +* `git_diff_file` represents an item on one side of a possible delta +* `git_diff_delta` represents a pair of items that have changed in some + way - it contains two `git_diff_file` plus a status and other stuff. +* `git_diff_list` is a list of deltas along with information about how + those particular deltas were found. +* `git_diff_patch` represents the actual diff between a pair of items. In + some cases, a delta may not have a corresponding patch, if the objects + are binary, for example. The content of a patch will be a set of hunks + and lines. +* A `hunk` is range of lines described by a `git_diff_range` (i.e. "lines + 10-20 in the old file became lines 12-23 in the new"). It will have a + header that compactly represents that information, and it will have a + number of lines of context surrounding added and deleted lines. +* A `line` is simple a line of data along with a `git_diff_line_t` value + that tells how the data should be interpretted (e.g. context or added). + +Internal Objects +---------------- + +* `git_diff_file_content` is an internal structure that represents the + data on one side of an item to be diffed; it is an augmented + `git_diff_file` with more flags and the actual file data. +** it is created from a repository plus a) a git_diff_file, b) a git_blob, + or c) raw data and size +** there are three main operations on git_diff_file_content: +*** _initialization_ sets up the data structure and does what it can up to, + but not including loading and looking at the actual data +*** _loading_ loads the data, preprocesses it (i.e. applies filters) and + potentially analyzes it (to decide if binary) +*** _free_ releases loaded data and frees any allocated memory + +* The internal structure of a `git_diff_patch` stores the actual diff + between a pair of `git_diff_file_content` items +** it may be "unset" if the items are not diffable +** "empty" if the items are the same +** otherwise it will consist of a set of hunks each of which covers some + number of lines of context, additions and deletions +** a patch is created from two git_diff_file_content items +** a patch is fully instantiated in three phases: +*** initial creation and initialization +*** loading of data and preliminary data examination +*** diffing of data and optional storage of diffs +** (TBD) if a patch is asked to store the diffs and the size of the diff + is significantly smaller than the raw data of the two sides, then the + patch may be flattened using a pool of string data + +* `git_diff_output` is an internal structure that represents an output + target for a `git_diff_patch` +** It consists of file, hunk, and line callbacks, plus a payload +** There is a standard flattened output that can be used for plain text output +** Typically we use a `git_xdiff_output` which drives the callbacks via the + xdiff code taken from core Git. + +* `git_diff_driver` is an internal structure that encapsulates the logic + for a given type of file +** a driver is looked up based on the name and mode of a file. +** the driver can then be used to: +*** determine if a file is binary (by attributes, by git_diff_options + settings, or by examining the content) +*** give you a function pointer that is used to evaluate function context + for hunk headers +** At some point, the logic for getting a filtered version of file content + or calculating the OID of a file may be moved into the driver. + diff --git a/src/blob.c b/src/blob.c index a68c4cc3e..2e4d5f479 100644 --- a/src/blob.c +++ b/src/blob.c @@ -11,6 +11,7 @@ #include "git2/odb_backend.h" #include "common.h" +#include "filebuf.h" #include "blob.h" #include "filter.h" #include "buf_text.h" diff --git a/src/checkout.c b/src/checkout.c index 7a2e68300..ede0be8e8 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -20,6 +20,7 @@ #include "refs.h" #include "repository.h" +#include "index.h" #include "filter.h" #include "blob.h" #include "diff.h" diff --git a/src/clone.c b/src/clone.c index af3298fd0..5b6c6f77d 100644 --- a/src/clone.c +++ b/src/clone.c @@ -21,6 +21,7 @@ #include "fileops.h" #include "refs.h" #include "path.h" +#include "repository.h" static int create_branch( git_reference **branch, diff --git a/src/crlf.c b/src/crlf.c index 81268da83..65039f9cc 100644 --- a/src/crlf.c +++ b/src/crlf.c @@ -5,14 +5,16 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "git2/attr.h" +#include "git2/blob.h" +#include "git2/index.h" + #include "common.h" #include "fileops.h" #include "hash.h" #include "filter.h" #include "buf_text.h" #include "repository.h" -#include "git2/attr.h" -#include "git2/blob.h" struct crlf_attrs { int crlf_action; diff --git a/src/diff.c b/src/diff.c index 05ef4f16b..97ccb3cbd 100644 --- a/src/diff.c +++ b/src/diff.c @@ -11,6 +11,8 @@ #include "attr_file.h" #include "filter.h" #include "pathspec.h" +#include "index.h" +#include "odb.h" #define DIFF_FLAG_IS_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) != 0) #define DIFF_FLAG_ISNT_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) == 0) @@ -1170,3 +1172,73 @@ int git_diff_tree_to_workdir( return error; } + +size_t git_diff_num_deltas(git_diff_list *diff) +{ + assert(diff); + return (size_t)diff->deltas.length; +} + +size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type) +{ + size_t i, count = 0; + git_diff_delta *delta; + + assert(diff); + + git_vector_foreach(&diff->deltas, i, delta) { + count += (delta->status == type); + } + + return count; +} + +int git_diff__paired_foreach( + git_diff_list *idx2head, + git_diff_list *wd2idx, + int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload), + void *payload) +{ + int cmp; + git_diff_delta *i2h, *w2i; + size_t i, j, i_max, j_max; + int (*strcomp)(const char *, const char *); + + i_max = idx2head ? idx2head->deltas.length : 0; + j_max = wd2idx ? wd2idx->deltas.length : 0; + + /* Get appropriate strcmp function */ + strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL; + + /* Assert both iterators use matching ignore-case. If this function ever + * supports merging diffs that are not sorted by the same function, then + * it will need to spool and sort on one of the results before merging + */ + if (idx2head && wd2idx) { + assert(idx2head->strcomp == wd2idx->strcomp); + } + + for (i = 0, j = 0; i < i_max || j < j_max; ) { + i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL; + w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL; + + cmp = !w2i ? -1 : !i2h ? 1 : + strcomp(i2h->old_file.path, w2i->old_file.path); + + if (cmp < 0) { + if (cb(i2h, NULL, payload)) + return GIT_EUSER; + i++; + } else if (cmp > 0) { + if (cb(NULL, w2i, payload)) + return GIT_EUSER; + j++; + } else { + if (cb(i2h, w2i, payload)) + return GIT_EUSER; + i++; j++; + } + } + + return 0; +} diff --git a/src/diff.h b/src/diff.h index ac8ab2aed..ad12e7731 100644 --- a/src/diff.h +++ b/src/diff.h @@ -29,11 +29,16 @@ enum { GIT_DIFFCAPS_TRUST_NANOSECS = (1 << 5), /* use stat time nanoseconds */ }; +#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY) +#define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA) + enum { GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */ GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */ GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */ GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */ + GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */ + GIT_DIFF_FLAG__LOADED = (1 << 12), /* file data has been loaded */ GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */ GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */ @@ -83,6 +88,12 @@ extern int git_diff__from_iterators( git_iterator *new_iter, const git_diff_options *opts); +extern int git_diff__paired_foreach( + git_diff_list *idx2head, + git_diff_list *wd2idx, + int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload), + void *payload); + int git_diff_find_similar__hashsig_for_file( void **out, const git_diff_file *f, const char *path, void *p); diff --git a/src/diff_driver.c b/src/diff_driver.c new file mode 100644 index 000000000..5438afc67 --- /dev/null +++ b/src/diff_driver.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#include "common.h" + +#include "git2/attr.h" + +#include "diff.h" +#include "diff_patch.h" +#include "diff_driver.h" +#include "strmap.h" +#include "pool.h" +#include "map.h" +#include "buf_text.h" + +typedef enum { + DIFF_DRIVER_AUTO = 0, + DIFF_DRIVER_FALSE = 1, + DIFF_DRIVER_TRUE = 2, + DIFF_DRIVER_NAMED = 3, +} git_diff_driver_t; + +enum { + DIFF_CONTEXT_FIND_NORMAL = 0, + DIFF_CONTEXT_FIND_ICASE = (1 << 0), + DIFF_CONTEXT_FIND_EXT = (1 << 1), +}; + +/* data for finding function context for a given file type */ +struct git_diff_driver { + git_diff_driver_t type; + git_strarray fn_patterns; + int binary; +}; + +struct git_diff_driver_registry { + git_strmap *drivers; + git_pool strings; +}; + +static git_diff_driver global_drivers[3] = { + { DIFF_DRIVER_AUTO, { NULL, 0 }, -1 }, + { DIFF_DRIVER_FALSE, { NULL, 0 }, 1 }, + { DIFF_DRIVER_TRUE, { NULL, 0 }, 0 }, +}; + +git_diff_driver_registry *git_diff_driver_registry_new() +{ + return git__calloc(1, sizeof(git_diff_driver_registry)); +} + +void git_diff_driver_registry_free(git_diff_driver_registry *reg) +{ + git__free(reg); +} + +int git_diff_driver_lookup( + git_diff_driver **out, git_repository *repo, const char *path) +{ + const char *value; + + assert(out); + + if (!repo || !path || !strlen(path)) + goto use_auto; + + if (git_attr_get(&value, repo, 0, path, "diff") < 0) + return -1; + + if (GIT_ATTR_FALSE(value)) { + *out = &global_drivers[DIFF_DRIVER_FALSE]; + return 0; + } + + else if (GIT_ATTR_TRUE(value)) { + *out = &global_drivers[DIFF_DRIVER_TRUE]; + return 0; + } + + /* otherwise look for driver information in config and build driver */ + +use_auto: + *out = &global_drivers[DIFF_DRIVER_AUTO]; + return 0; +} + +void git_diff_driver_free(git_diff_driver *driver) +{ + GIT_UNUSED(driver); + /* do nothing for now */ +} + +int git_diff_driver_is_binary(git_diff_driver *driver) +{ + return driver ? driver->binary : -1; +} + +int git_diff_driver_content_is_binary( + git_diff_driver *driver, const char *content, size_t content_len) +{ + const git_buf search = { (char *)content, 0, min(content_len, 4000) }; + + GIT_UNUSED(driver); + + /* TODO: provide encoding / binary detection callbacks that can + * be UTF-8 aware, etc. For now, instead of trying to be smart, + * let's just use the simple NUL-byte detection that core git uses. + */ + + /* previously was: if (git_buf_text_is_binary(&search)) */ + if (git_buf_text_contains_nul(&search)) + return 1; + + return 0; +} + +static long diff_context_find( + const char *line, + long line_len, + char *out, + long out_size, + void *payload) +{ + git_diff_driver *driver = payload; + const char *scan; + + GIT_UNUSED(driver); + + if (line_len > 0 && line[line_len - 1] == '\n') + line_len--; + if (line_len > 0 && line[line_len - 1] == '\r') + line_len--; + if (!line_len) + return -1; + + if (!git__isalpha(*line) && *line != '_' && *line != '$') + return -1; + + for (scan = &line[line_len-1]; scan > line && git__isspace(*scan); --scan) + /* search backward for non-space */; + line_len = scan - line; + + if (line_len >= out_size) + line_len = out_size - 1; + + memcpy(out, line, line_len); + out[line_len] = '\0'; + + return line_len; +} + +git_diff_find_context_fn git_diff_driver_find_content_fn(git_diff_driver *driver) +{ + GIT_UNUSED(driver); + return diff_context_find; +} + diff --git a/src/diff_driver.h b/src/diff_driver.h new file mode 100644 index 000000000..b9881a7ed --- /dev/null +++ b/src/diff_driver.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_diff_driver_h__ +#define INCLUDE_diff_driver_h__ + +#include "common.h" + +typedef struct git_diff_driver_registry git_diff_driver_registry; + +git_diff_driver_registry *git_diff_driver_registry_new(); +void git_diff_driver_registry_free(git_diff_driver_registry *); + +typedef struct git_diff_driver git_diff_driver; + +int git_diff_driver_lookup(git_diff_driver **, git_repository *, const char *); +void git_diff_driver_free(git_diff_driver *); + +/* returns -1 meaning "unknown", 0 meaning not binary, 1 meaning binary */ +int git_diff_driver_is_binary(git_diff_driver *); + +/* returns -1 meaning "unknown", 0 meaning not binary, 1 meaning binary */ +int git_diff_driver_content_is_binary( + git_diff_driver *, const char *content, size_t content_len); + +typedef long (*git_diff_find_context_fn)( + const char *, long, char *, long, void *); + +git_diff_find_context_fn git_diff_driver_find_content_fn(git_diff_driver *); + +#endif diff --git a/src/diff_file.c b/src/diff_file.c new file mode 100644 index 000000000..e4f8ca1e8 --- /dev/null +++ b/src/diff_file.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#include "common.h" +#include "git2/blob.h" +#include "git2/submodule.h" +#include "diff.h" +#include "diff_file.h" +#include "odb.h" +#include "fileops.h" +#include "filter.h" + +#define DIFF_MAX_FILESIZE 0x20000000 + +static bool diff_file_content_binary_by_size(git_diff_file_content *fc) +{ + /* if we have diff opts, check max_size vs file size */ + if ((fc->file.flags & DIFF_FLAGS_KNOWN_BINARY) == 0 && + fc->opts && fc->opts->max_size >= 0) + { + git_off_t threshold = DIFF_MAX_FILESIZE; + if (fc->opts->max_size > 0) + threshold = fc->opts->max_size; + if (fc->file.size > threshold) + fc->file.flags |= GIT_DIFF_FLAG_BINARY; + } + + return ((fc->file.flags & GIT_DIFF_FLAG_BINARY) != 0); +} + +static void diff_file_content_binary_by_content(git_diff_file_content *fc) +{ + if ((fc->file.flags & DIFF_FLAGS_KNOWN_BINARY) != 0) + return; + + switch (git_diff_driver_content_is_binary( + fc->driver, fc->map.data, fc->map.len)) { + case 0: fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; break; + case 1: fc->file.flags |= GIT_DIFF_FLAG_BINARY; break; + default: break; + } +} + +static int diff_file_content_init_common(git_diff_file_content *fc) +{ + uint32_t flags = fc->opts ? fc->opts->flags : GIT_DIFF_NORMAL; + + if (!fc->driver) { + if (git_diff_driver_lookup(&fc->driver, fc->repo, "") < 0) + return -1; + fc->src = GIT_ITERATOR_TYPE_TREE; + } + + /* make sure file is conceivable mmap-able */ + if ((git_off_t)((size_t)fc->file.size) != fc->file.size) + fc->file.flags |= GIT_DIFF_FLAG_BINARY; + + /* check if user is forcing is to text diff the file */ + else if (flags & GIT_DIFF_FORCE_TEXT) + fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; + + /* otherwise see if diff driver forces a behavior */ + else switch (git_diff_driver_is_binary(fc->driver)) { + case 0: fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; break; + case 1: fc->file.flags |= GIT_DIFF_FLAG_BINARY; break; + default: break; + } + + diff_file_content_binary_by_size(fc); + + if ((fc->file.flags & GIT_DIFF_FLAG__NO_DATA) != 0) { + fc->file.flags |= GIT_DIFF_FLAG__LOADED; + fc->map.len = 0; + fc->map.data = ""; + } + + if ((fc->file.flags & GIT_DIFF_FLAG__LOADED) != 0) + diff_file_content_binary_by_content(fc); + + return 0; +} + +int diff_file_content_init_from_diff( + git_diff_file_content *fc, + git_diff_list *diff, + size_t delta_index, + bool use_old) +{ + git_diff_delta *delta = git_vector_get(&diff->deltas, delta_index); + git_diff_file *file = use_old ? &delta->old_file : &delta->new_file; + bool has_data = true; + + memset(fc, 0, sizeof(*fc)); + fc->repo = diff->repo; + fc->opts = &diff->opts; + fc->src = use_old ? diff->old_src : diff->new_src; + memcpy(&fc->file, file, sizeof(fc->file)); + + if (git_diff_driver_lookup(&fc->driver, fc->repo, file->path) < 0) + return -1; + + switch (delta->status) { + case GIT_DELTA_ADDED: + has_data = !use_old; break; + case GIT_DELTA_DELETED: + has_data = use_old; break; + case GIT_DELTA_UNTRACKED: + has_data = !use_old && + (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) != 0; + break; + case GIT_DELTA_MODIFIED: + case GIT_DELTA_COPIED: + case GIT_DELTA_RENAMED: + break; + default: + has_data = false; + break; + } + + if (!has_data) + fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; + + return diff_file_content_init_common(fc); +} + +int diff_file_content_init_from_blob( + git_diff_file_content *fc, + git_repository *repo, + const git_diff_options *opts, + const git_blob *blob) +{ + memset(fc, 0, sizeof(*fc)); + fc->repo = repo; + fc->opts = opts; + fc->blob = blob; + + if (!blob) { + fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; + } else { + fc->file.flags |= GIT_DIFF_FLAG__LOADED | GIT_DIFF_FLAG_VALID_OID; + fc->file.size = git_blob_rawsize(blob); + fc->file.mode = 0644; + git_oid_cpy(&fc->file.oid, git_blob_id(blob)); + + fc->map.len = (size_t)fc->file.size; + fc->map.data = (char *)git_blob_rawcontent(blob); + } + + return diff_file_content_init_common(fc); +} + +int diff_file_content_init_from_raw( + git_diff_file_content *fc, + git_repository *repo, + const git_diff_options *opts, + const char *buf, + size_t buflen) +{ + memset(fc, 0, sizeof(*fc)); + fc->repo = repo; + fc->opts = opts; + + if (!buf) { + fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; + } else { + fc->file.flags |= GIT_DIFF_FLAG__LOADED | GIT_DIFF_FLAG_VALID_OID; + fc->file.size = buflen; + fc->file.mode = 0644; + git_odb_hash(&fc->file.oid, buf, buflen, GIT_OBJ_BLOB); + + fc->map.len = buflen; + fc->map.data = (char *)buf; + } + + return diff_file_content_init_common(fc); +} + +static int diff_file_content_commit_to_str( + git_diff_file_content *fc, bool check_status) +{ + char oid[GIT_OID_HEXSZ+1]; + git_buf content = GIT_BUF_INIT; + const char *status = ""; + + if (check_status) { + int error = 0; + git_submodule *sm = NULL; + unsigned int sm_status = 0; + const git_oid *sm_head; + + if ((error = git_submodule_lookup(&sm, fc->repo, fc->file.path)) < 0 || + (error = git_submodule_status(&sm_status, sm)) < 0) { + /* GIT_EEXISTS means a "submodule" that has not been git added */ + if (error == GIT_EEXISTS) + error = 0; + return error; + } + + /* update OID if we didn't have it previously */ + if ((fc->file.flags & GIT_DIFF_FLAG_VALID_OID) == 0 && + ((sm_head = git_submodule_wd_id(sm)) != NULL || + (sm_head = git_submodule_head_id(sm)) != NULL)) + { + git_oid_cpy(&fc->file.oid, sm_head); + fc->file.flags |= GIT_DIFF_FLAG_VALID_OID; + } + + if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) + status = "-dirty"; + } + + git_oid_tostr(oid, sizeof(oid), &fc->file.oid); + if (git_buf_printf(&content, "Subproject commit %s%s\n", oid, status) < 0) + return -1; + + fc->map.len = git_buf_len(&content); + fc->map.data = git_buf_detach(&content); + fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA; + + return 0; +} + +static int diff_file_content_load_blob(git_diff_file_content *fc) +{ + int error = 0; + git_odb_object *odb_obj = NULL; + + if (git_oid_iszero(&fc->file.oid)) + return 0; + + if (fc->file.mode == GIT_FILEMODE_COMMIT) + return diff_file_content_commit_to_str(fc, false); + + /* if we don't know size, try to peek at object header first */ + if (!fc->file.size) { + git_odb *odb; + size_t len; + git_otype type; + + if (!(error = git_repository_odb__weakptr(&odb, fc->repo))) { + error = git_odb__read_header_or_object( + &odb_obj, &len, &type, odb, &fc->file.oid); + git_odb_free(odb); + } + if (error) + return error; + + fc->file.size = len; + } + + if (diff_file_content_binary_by_size(fc)) + return 0; + + if (odb_obj != NULL) { + error = git_object__from_odb_object( + (git_object **)&fc->blob, fc->repo, odb_obj, GIT_OBJ_BLOB); + git_odb_object_free(odb_obj); + } else { + error = git_blob_lookup( + (git_blob **)&fc->blob, fc->repo, &fc->file.oid); + } + + if (!error) { + fc->file.flags |= GIT_DIFF_FLAG__FREE_BLOB; + fc->map.data = (void *)git_blob_rawcontent(fc->blob); + fc->map.len = (size_t)git_blob_rawsize(fc->blob); + } + + return error; +} + +static int diff_file_content_load_workdir_symlink( + git_diff_file_content *fc, git_buf *path) +{ + ssize_t alloc_len, read_len; + + /* link path on disk could be UTF-16, so prepare a buffer that is + * big enough to handle some UTF-8 data expansion + */ + alloc_len = (ssize_t)(fc->file.size * 2) + 1; + + fc->map.data = git__calloc(alloc_len, sizeof(char)); + GITERR_CHECK_ALLOC(fc->map.data); + + fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA; + + read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len); + if (read_len < 0) { + giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file.path); + return -1; + } + + fc->map.len = read_len; + return 0; +} + +static int diff_file_content_load_workdir_file( + git_diff_file_content *fc, git_buf *path) +{ + int error = 0; + git_vector filters = GIT_VECTOR_INIT; + git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT; + git_file fd = git_futils_open_ro(git_buf_cstr(path)); + + if (fd < 0) + return fd; + + if (!fc->file.size && + !(fc->file.size = git_futils_filesize(fd))) + goto cleanup; + + if (diff_file_content_binary_by_size(fc)) + goto cleanup; + + if ((error = git_filters_load( + &filters, fc->repo, fc->file.path, GIT_FILTER_TO_ODB)) < 0) + goto cleanup; + /* error >= is a filter count */ + + if (error == 0) { + if (!(error = git_futils_mmap_ro( + &fc->map, fd, 0, (size_t)fc->file.size))) + fc->file.flags |= GIT_DIFF_FLAG__UNMAP_DATA; + else /* fall through to try readbuffer below */ + giterr_clear(); + } + + if (error != 0) { + error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file.size); + if (error < 0) + goto cleanup; + + if (!filters.length) + git_buf_swap(&filtered, &raw); + else + error = git_filters_apply(&filtered, &raw, &filters); + + if (!error) { + fc->map.len = git_buf_len(&filtered); + fc->map.data = git_buf_detach(&filtered); + fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA; + } + + git_buf_free(&raw); + git_buf_free(&filtered); + } + +cleanup: + git_filters_free(&filters); + p_close(fd); + + return error; +} + +static int diff_file_content_load_workdir(git_diff_file_content *fc) +{ + int error = 0; + git_buf path = GIT_BUF_INIT; + + if (fc->file.mode == GIT_FILEMODE_COMMIT) + return diff_file_content_commit_to_str(fc, true); + + if (fc->file.mode == GIT_FILEMODE_TREE) + return 0; + + if (git_buf_joinpath( + &path, git_repository_workdir(fc->repo), fc->file.path) < 0) + return -1; + + if (S_ISLNK(fc->file.mode)) + error = diff_file_content_load_workdir_symlink(fc, &path); + else + error = diff_file_content_load_workdir_file(fc, &path); + + /* once data is loaded, update OID if we didn't have it previously */ + if (!error && (fc->file.flags & GIT_DIFF_FLAG_VALID_OID) == 0) { + error = git_odb_hash( + &fc->file.oid, fc->map.data, fc->map.len, GIT_OBJ_BLOB); + fc->file.flags |= GIT_DIFF_FLAG_VALID_OID; + } + + git_buf_free(&path); + return error; +} + +int diff_file_content_load(git_diff_file_content *fc) +{ + int error = 0; + + if ((fc->file.flags & GIT_DIFF_FLAG__LOADED) != 0) + return 0; + + if (fc->file.flags & GIT_DIFF_FLAG_BINARY) + return 0; + + if (fc->src == GIT_ITERATOR_TYPE_WORKDIR) + error = diff_file_content_load_workdir(fc); + else + error = diff_file_content_load_blob(fc); + if (error) + return error; + + fc->file.flags |= GIT_DIFF_FLAG__LOADED; + + diff_file_content_binary_by_content(fc); + + return 0; +} + +void diff_file_content_unload(git_diff_file_content *fc) +{ + if (fc->file.flags & GIT_DIFF_FLAG__FREE_DATA) { + git__free(fc->map.data); + fc->map.data = ""; + fc->map.len = 0; + fc->file.flags &= ~GIT_DIFF_FLAG__FREE_DATA; + } + else if (fc->file.flags & GIT_DIFF_FLAG__UNMAP_DATA) { + git_futils_mmap_free(&fc->map); + fc->map.data = ""; + fc->map.len = 0; + fc->file.flags &= ~GIT_DIFF_FLAG__UNMAP_DATA; + } + + if (fc->file.flags & GIT_DIFF_FLAG__FREE_BLOB) { + git_blob_free((git_blob *)fc->blob); + fc->blob = NULL; + fc->file.flags &= ~GIT_DIFF_FLAG__FREE_BLOB; + } + + fc->file.flags &= ~GIT_DIFF_FLAG__LOADED; +} + +void diff_file_content_clear(git_diff_file_content *fc) +{ + diff_file_content_unload(fc); + + /* for now, nothing else to do */ +} diff --git a/src/diff_file.h b/src/diff_file.h new file mode 100644 index 000000000..51c6878a9 --- /dev/null +++ b/src/diff_file.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_diff_file_h__ +#define INCLUDE_diff_file_h__ + +#include "common.h" +#include "diff.h" +#include "diff_driver.h" +#include "map.h" + +/* expanded information for one side of a delta */ +typedef struct { + git_repository *repo; + const git_diff_options *opts; + git_diff_file file; + git_diff_driver *driver; + git_iterator_type_t src; + const git_blob *blob; + git_map map; +} git_diff_file_content; + +extern int diff_file_content_init_from_diff( + git_diff_file_content *fc, + git_diff_list *diff, + size_t delta_index, + bool use_old); + +extern int diff_file_content_init_from_blob( + git_diff_file_content *fc, + git_repository *repo, + const git_diff_options *opts, + const git_blob *blob); + +extern int diff_file_content_init_from_raw( + git_diff_file_content *fc, + git_repository *repo, + const git_diff_options *opts, + const char *buf, + size_t buflen); + +/* this loads the blob/file-on-disk as needed */ +extern int diff_file_content_load(git_diff_file_content *fc); + +/* this releases the blob/file-in-memory */ +extern void diff_file_content_unload(git_diff_file_content *fc); + +/* this unloads and also releases any other resources */ +extern void diff_file_content_clear(git_diff_file_content *fc); + +#endif diff --git a/src/diff_output.c b/src/diff_output.c deleted file mode 100644 index 9a5be2a10..000000000 --- a/src/diff_output.c +++ /dev/null @@ -1,1526 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ -#include "common.h" -#include "git2/attr.h" -#include "git2/oid.h" -#include "git2/submodule.h" -#include "diff_output.h" -#include -#include "fileops.h" -#include "filter.h" -#include "buf_text.h" - -static int read_next_int(const char **str, int *value) -{ - const char *scan = *str; - int v = 0, digits = 0; - /* find next digit */ - for (scan = *str; *scan && !isdigit(*scan); scan++); - /* parse next number */ - for (; isdigit(*scan); scan++, digits++) - v = (v * 10) + (*scan - '0'); - *str = scan; - *value = v; - return (digits > 0) ? 0 : -1; -} - -static int parse_hunk_header(git_diff_range *range, const char *header) -{ - /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ - if (*header != '@') - return -1; - if (read_next_int(&header, &range->old_start) < 0) - return -1; - if (*header == ',') { - if (read_next_int(&header, &range->old_lines) < 0) - return -1; - } else - range->old_lines = 1; - if (read_next_int(&header, &range->new_start) < 0) - return -1; - if (*header == ',') { - if (read_next_int(&header, &range->new_lines) < 0) - return -1; - } else - range->new_lines = 1; - if (range->old_start < 0 || range->new_start < 0) - return -1; - - return 0; -} - -#define KNOWN_BINARY_FLAGS (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY) -#define NOT_BINARY_FLAGS (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA) - -static int update_file_is_binary_by_attr( - git_repository *repo, git_diff_file *file) -{ - const char *value; - - /* because of blob diffs, cannot assume path is set */ - if (!file->path || !strlen(file->path)) - return 0; - - if (git_attr_get(&value, repo, 0, file->path, "diff") < 0) - return -1; - - if (GIT_ATTR_FALSE(value)) - file->flags |= GIT_DIFF_FLAG_BINARY; - else if (GIT_ATTR_TRUE(value)) - file->flags |= GIT_DIFF_FLAG_NOT_BINARY; - /* otherwise leave file->flags alone */ - - return 0; -} - -static void update_delta_is_binary(git_diff_delta *delta) -{ - if ((delta->old_file.flags & GIT_DIFF_FLAG_BINARY) != 0 || - (delta->new_file.flags & GIT_DIFF_FLAG_BINARY) != 0) - delta->flags |= GIT_DIFF_FLAG_BINARY; - - else if ((delta->old_file.flags & NOT_BINARY_FLAGS) != 0 && - (delta->new_file.flags & NOT_BINARY_FLAGS) != 0) - delta->flags |= GIT_DIFF_FLAG_NOT_BINARY; - - /* otherwise leave delta->flags binary value untouched */ -} - -/* returns if we forced binary setting (and no further checks needed) */ -static bool diff_delta_is_binary_forced( - diff_context *ctxt, - git_diff_delta *delta) -{ - /* return true if binary-ness has already been settled */ - if ((delta->flags & KNOWN_BINARY_FLAGS) != 0) - return true; - - /* make sure files are conceivably mmap-able */ - if ((git_off_t)((size_t)delta->old_file.size) != delta->old_file.size || - (git_off_t)((size_t)delta->new_file.size) != delta->new_file.size) { - - delta->old_file.flags |= GIT_DIFF_FLAG_BINARY; - delta->new_file.flags |= GIT_DIFF_FLAG_BINARY; - delta->flags |= GIT_DIFF_FLAG_BINARY; - return true; - } - - /* check if user is forcing us to text diff these files */ - if (ctxt->opts && (ctxt->opts->flags & GIT_DIFF_FORCE_TEXT) != 0) { - delta->old_file.flags |= GIT_DIFF_FLAG_NOT_BINARY; - delta->new_file.flags |= GIT_DIFF_FLAG_NOT_BINARY; - delta->flags |= GIT_DIFF_FLAG_NOT_BINARY; - return true; - } - - return false; -} - -static int diff_delta_is_binary_by_attr( - diff_context *ctxt, git_diff_patch *patch) -{ - int error = 0, mirror_new; - git_diff_delta *delta = patch->delta; - - if (diff_delta_is_binary_forced(ctxt, delta)) - return 0; - - /* check diff attribute +, -, or 0 */ - if (update_file_is_binary_by_attr(ctxt->repo, &delta->old_file) < 0) - return -1; - - mirror_new = (delta->new_file.path == delta->old_file.path || - ctxt->diff->strcomp(delta->new_file.path, delta->old_file.path) == 0); - if (mirror_new) - delta->new_file.flags |= (delta->old_file.flags & KNOWN_BINARY_FLAGS); - else - error = update_file_is_binary_by_attr(ctxt->repo, &delta->new_file); - - update_delta_is_binary(delta); - - return error; -} - -static int diff_delta_is_binary_by_content( - diff_context *ctxt, - git_diff_delta *delta, - git_diff_file *file, - const git_map *map) -{ - const git_buf search = { map->data, 0, min(map->len, 4000) }; - - if (diff_delta_is_binary_forced(ctxt, delta)) - return 0; - - /* TODO: provide encoding / binary detection callbacks that can - * be UTF-8 aware, etc. For now, instead of trying to be smart, - * let's just use the simple NUL-byte detection that core git uses. - */ - - /* previously was: if (git_buf_text_is_binary(&search)) */ - if (git_buf_text_contains_nul(&search)) - file->flags |= GIT_DIFF_FLAG_BINARY; - else - file->flags |= GIT_DIFF_FLAG_NOT_BINARY; - - update_delta_is_binary(delta); - - return 0; -} - -static int diff_delta_is_binary_by_size( - diff_context *ctxt, git_diff_delta *delta, git_diff_file *file) -{ - git_off_t threshold = MAX_DIFF_FILESIZE; - - if ((file->flags & KNOWN_BINARY_FLAGS) != 0) - return 0; - - if (ctxt && ctxt->opts) { - if (ctxt->opts->max_size < 0) - return 0; - - if (ctxt->opts->max_size > 0) - threshold = ctxt->opts->max_size; - } - - if (file->size > threshold) - file->flags |= GIT_DIFF_FLAG_BINARY; - - update_delta_is_binary(delta); - - return 0; -} - -static long diff_context_find( - const char *line, - long line_len, - char *out, - long out_size, - void *payload) -{ - diff_context *ctxt = payload; - const char *scan; - bool found_paren = false; - - GIT_UNUSED(ctxt); - - if (line_len > 0 && line[line_len - 1] == '\n') - line_len--; - if (line_len > 0 && line[line_len - 1] == '\r') - line_len--; - if (!line_len) - return -1; - - if (!isalpha(*line)) - return -1; - - for (scan = &line[line_len - 1]; scan > line && *scan != '('; --scan) - /* search backward for ( */; - if (scan != line) { - found_paren = true; - line_len = scan - line; - - for (--scan; scan > line && !isalpha(*scan); --scan) - --line_len; - } - - if (!line_len) - return -1; - - if (out_size > line_len) { - memcpy(out, line, line_len); - - if (found_paren) - out[line_len++] = '('; - out[line_len] = '\0'; - } else { - memcpy(out, line, out_size); - line_len = out_size; - } - - return line_len; -} - -static void setup_xdiff_options(diff_context *ctxt) -{ - memset(&ctxt->xdiff_config, 0, sizeof(ctxt->xdiff_config)); - memset(&ctxt->xdiff_params, 0, sizeof(ctxt->xdiff_params)); - - ctxt->xdiff_config.ctxlen = - (!ctxt->opts) ? 3 : ctxt->opts->context_lines; - ctxt->xdiff_config.interhunkctxlen = - (!ctxt->opts) ? 0 : ctxt->opts->interhunk_lines; - - ctxt->xdiff_config.flags = XDL_EMIT_FUNCNAMES; - ctxt->xdiff_config.find_func = diff_context_find; - ctxt->xdiff_config.find_func_priv = ctxt; - - if (!ctxt->opts) - return; - - if (ctxt->opts->flags & GIT_DIFF_IGNORE_WHITESPACE) - ctxt->xdiff_params.flags |= XDF_WHITESPACE_FLAGS; - if (ctxt->opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE) - ctxt->xdiff_params.flags |= XDF_IGNORE_WHITESPACE_CHANGE; - if (ctxt->opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL) - ctxt->xdiff_params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; -} - - -static int get_blob_content( - diff_context *ctxt, - git_diff_delta *delta, - git_diff_file *file, - git_map *map, - git_blob **blob) -{ - int error; - git_odb_object *odb_obj = NULL; - - if (git_oid_iszero(&file->oid)) - return 0; - - if (file->mode == GIT_FILEMODE_COMMIT) { - char oidstr[GIT_OID_HEXSZ+1]; - git_buf content = GIT_BUF_INIT; - - git_oid_tostr(oidstr, sizeof(oidstr), &file->oid); - git_buf_printf(&content, "Subproject commit %s\n", oidstr); - - map->data = git_buf_detach(&content); - map->len = strlen(map->data); - - file->flags |= GIT_DIFF_FLAG__FREE_DATA; - return 0; - } - - if (!file->size) { - git_odb *odb; - size_t len; - git_otype type; - - /* peek at object header to avoid loading if too large */ - if ((error = git_repository_odb__weakptr(&odb, ctxt->repo)) < 0 || - (error = git_odb__read_header_or_object( - &odb_obj, &len, &type, odb, &file->oid)) < 0) - return error; - - assert(type == GIT_OBJ_BLOB); - - file->size = len; - } - - /* if blob is too large to diff, mark as binary */ - if ((error = diff_delta_is_binary_by_size(ctxt, delta, file)) < 0) - return error; - if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0) - return 0; - - if (odb_obj != NULL) { - error = git_object__from_odb_object( - (git_object **)blob, ctxt->repo, odb_obj, GIT_OBJ_BLOB); - git_odb_object_free(odb_obj); - } else - error = git_blob_lookup(blob, ctxt->repo, &file->oid); - - if (error) - return error; - - map->data = (void *)git_blob_rawcontent(*blob); - map->len = (size_t)git_blob_rawsize(*blob); - - return diff_delta_is_binary_by_content(ctxt, delta, file, map); -} - -static int get_workdir_sm_content( - diff_context *ctxt, - git_diff_file *file, - git_map *map) -{ - int error = 0; - git_buf content = GIT_BUF_INIT; - git_submodule* sm = NULL; - unsigned int sm_status = 0; - const char* sm_status_text = ""; - char oidstr[GIT_OID_HEXSZ+1]; - - if ((error = git_submodule_lookup(&sm, ctxt->repo, file->path)) < 0 || - (error = git_submodule_status(&sm_status, sm)) < 0) { - - /* GIT_EEXISTS means a "submodule" that has not been git added */ - if (error == GIT_EEXISTS) - error = 0; - return error; - } - - /* update OID if we didn't have it previously */ - if ((file->flags & GIT_DIFF_FLAG_VALID_OID) == 0) { - const git_oid* sm_head; - - if ((sm_head = git_submodule_wd_id(sm)) != NULL || - (sm_head = git_submodule_head_id(sm)) != NULL) { - - git_oid_cpy(&file->oid, sm_head); - file->flags |= GIT_DIFF_FLAG_VALID_OID; - } - } - - git_oid_tostr(oidstr, sizeof(oidstr), &file->oid); - - if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) - sm_status_text = "-dirty"; - - git_buf_printf( - &content, "Subproject commit %s%s\n", oidstr, sm_status_text); - - map->data = git_buf_detach(&content); - map->len = strlen(map->data); - - file->flags |= GIT_DIFF_FLAG__FREE_DATA; - - return 0; -} - -static int get_filtered( - git_map *map, git_file fd, git_diff_file *file, git_vector *filters) -{ - int error; - git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT; - - if ((error = git_futils_readbuffer_fd(&raw, fd, (size_t)file->size)) < 0) - return error; - - if (!filters->length) - git_buf_swap(&filtered, &raw); - else - error = git_filters_apply(&filtered, &raw, filters); - - if (!error) { - map->len = git_buf_len(&filtered); - map->data = git_buf_detach(&filtered); - - file->flags |= GIT_DIFF_FLAG__FREE_DATA; - } - - git_buf_free(&raw); - git_buf_free(&filtered); - - return error; -} - -static int get_workdir_content( - diff_context *ctxt, - git_diff_delta *delta, - git_diff_file *file, - git_map *map) -{ - int error = 0; - git_buf path = GIT_BUF_INIT; - const char *wd = git_repository_workdir(ctxt->repo); - - if (S_ISGITLINK(file->mode)) - return get_workdir_sm_content(ctxt, file, map); - - if (S_ISDIR(file->mode)) - return 0; - - if (git_buf_joinpath(&path, wd, file->path) < 0) - return -1; - - if (S_ISLNK(file->mode)) { - ssize_t alloc_len, read_len; - - file->flags |= GIT_DIFF_FLAG__FREE_DATA; - file->flags |= GIT_DIFF_FLAG_BINARY; - - /* link path on disk could be UTF-16, so prepare a buffer that is - * big enough to handle some UTF-8 data expansion - */ - alloc_len = (ssize_t)(file->size * 2) + 1; - - map->data = git__malloc(alloc_len); - GITERR_CHECK_ALLOC(map->data); - - read_len = p_readlink(path.ptr, map->data, alloc_len); - if (read_len < 0) { - giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path); - error = -1; - goto cleanup; - } - - map->len = read_len; - } - else { - git_file fd = git_futils_open_ro(path.ptr); - git_vector filters = GIT_VECTOR_INIT; - - if (fd < 0) { - error = fd; - goto cleanup; - } - - if (!file->size && !(file->size = git_futils_filesize(fd))) - goto close_and_cleanup; - - if ((error = diff_delta_is_binary_by_size(ctxt, delta, file)) < 0 || - (delta->flags & GIT_DIFF_FLAG_BINARY) != 0) - goto close_and_cleanup; - - error = git_filters_load( - &filters, ctxt->repo, file->path, GIT_FILTER_TO_ODB); - if (error < 0) - goto close_and_cleanup; - - if (error == 0) { /* note: git_filters_load returns filter count */ - error = git_futils_mmap_ro(map, fd, 0, (size_t)file->size); - if (!error) - file->flags |= GIT_DIFF_FLAG__UNMAP_DATA; - } - if (error != 0) - error = get_filtered(map, fd, file, &filters); - -close_and_cleanup: - git_filters_free(&filters); - p_close(fd); - } - - /* once data is loaded, update OID if we didn't have it previously */ - if (!error && (file->flags & GIT_DIFF_FLAG_VALID_OID) == 0) { - error = git_odb_hash( - &file->oid, map->data, map->len, GIT_OBJ_BLOB); - if (!error) - file->flags |= GIT_DIFF_FLAG_VALID_OID; - } - - if (!error) - error = diff_delta_is_binary_by_content(ctxt, delta, file, map); - -cleanup: - git_buf_free(&path); - return error; -} - -static void release_content(git_diff_file *file, git_map *map, git_blob *blob) -{ - if (blob != NULL) - git_blob_free(blob); - - if (file->flags & GIT_DIFF_FLAG__FREE_DATA) { - git__free(map->data); - map->data = ""; - map->len = 0; - file->flags &= ~GIT_DIFF_FLAG__FREE_DATA; - } - else if (file->flags & GIT_DIFF_FLAG__UNMAP_DATA) { - git_futils_mmap_free(map); - map->data = ""; - map->len = 0; - file->flags &= ~GIT_DIFF_FLAG__UNMAP_DATA; - } -} - - -static int diff_context_init( - diff_context *ctxt, - git_diff_list *diff, - git_repository *repo, - const git_diff_options *opts, - git_diff_file_cb file_cb, - git_diff_hunk_cb hunk_cb, - git_diff_data_cb data_cb, - void *payload) -{ - memset(ctxt, 0, sizeof(diff_context)); - - if (!repo && diff) - repo = diff->repo; - - if (!opts && diff) - opts = &diff->opts; - - ctxt->repo = repo; - ctxt->diff = diff; - ctxt->opts = opts; - ctxt->file_cb = file_cb; - ctxt->hunk_cb = hunk_cb; - ctxt->data_cb = data_cb; - ctxt->payload = payload; - ctxt->error = 0; - - setup_xdiff_options(ctxt); - - return 0; -} - -static int diff_delta_file_callback( - diff_context *ctxt, git_diff_delta *delta, size_t idx) -{ - float progress; - - if (!ctxt->file_cb) - return 0; - - progress = ctxt->diff ? ((float)idx / ctxt->diff->deltas.length) : 1.0f; - - if (ctxt->file_cb(delta, progress, ctxt->payload) != 0) - ctxt->error = GIT_EUSER; - - return ctxt->error; -} - -static void diff_patch_init( - diff_context *ctxt, git_diff_patch *patch) -{ - memset(patch, 0, sizeof(git_diff_patch)); - - patch->diff = ctxt->diff; - patch->ctxt = ctxt; - - if (patch->diff) { - patch->old_src = patch->diff->old_src; - patch->new_src = patch->diff->new_src; - } else { - patch->old_src = patch->new_src = GIT_ITERATOR_TYPE_TREE; - } -} - -static git_diff_patch *diff_patch_alloc( - diff_context *ctxt, git_diff_delta *delta) -{ - git_diff_patch *patch = git__malloc(sizeof(git_diff_patch)); - if (!patch) - return NULL; - - diff_patch_init(ctxt, patch); - - git_diff_list_addref(patch->diff); - - GIT_REFCOUNT_INC(patch); - - patch->delta = delta; - patch->flags = GIT_DIFF_PATCH_ALLOCATED; - - return patch; -} - -static int diff_patch_load( - diff_context *ctxt, git_diff_patch *patch) -{ - int error = 0; - git_diff_delta *delta = patch->delta; - bool check_if_unmodified = false; - - if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0) - return 0; - - error = diff_delta_is_binary_by_attr(ctxt, patch); - - patch->old_data.data = ""; - patch->old_data.len = 0; - patch->old_blob = NULL; - - patch->new_data.data = ""; - patch->new_data.len = 0; - patch->new_blob = NULL; - - if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0) - goto cleanup; - - if (!ctxt->hunk_cb && - !ctxt->data_cb && - (ctxt->opts->flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0) - goto cleanup; - - switch (delta->status) { - case GIT_DELTA_ADDED: - delta->old_file.flags |= GIT_DIFF_FLAG__NO_DATA; - break; - case GIT_DELTA_DELETED: - delta->new_file.flags |= GIT_DIFF_FLAG__NO_DATA; - break; - case GIT_DELTA_MODIFIED: - case GIT_DELTA_COPIED: - case GIT_DELTA_RENAMED: - break; - case GIT_DELTA_UNTRACKED: - delta->old_file.flags |= GIT_DIFF_FLAG__NO_DATA; - if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0) - delta->new_file.flags |= GIT_DIFF_FLAG__NO_DATA; - break; - default: - delta->new_file.flags |= GIT_DIFF_FLAG__NO_DATA; - delta->old_file.flags |= GIT_DIFF_FLAG__NO_DATA; - break; - } - -#define CHECK_UNMODIFIED (GIT_DIFF_FLAG__NO_DATA | GIT_DIFF_FLAG_VALID_OID) - - check_if_unmodified = - (delta->old_file.flags & CHECK_UNMODIFIED) == 0 && - (delta->new_file.flags & CHECK_UNMODIFIED) == 0; - - /* Always try to load workdir content first, since it may need to be - * filtered (and hence use 2x memory) and we want to minimize the max - * memory footprint during diff. - */ - - if ((delta->old_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 && - patch->old_src == GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = get_workdir_content( - ctxt, delta, &delta->old_file, &patch->old_data)) < 0) - goto cleanup; - if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0) - goto cleanup; - } - - if ((delta->new_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 && - patch->new_src == GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = get_workdir_content( - ctxt, delta, &delta->new_file, &patch->new_data)) < 0) - goto cleanup; - if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0) - goto cleanup; - } - - if ((delta->old_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 && - patch->old_src != GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = get_blob_content( - ctxt, delta, &delta->old_file, - &patch->old_data, &patch->old_blob)) < 0) - goto cleanup; - if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0) - goto cleanup; - } - - if ((delta->new_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 && - patch->new_src != GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = get_blob_content( - ctxt, delta, &delta->new_file, - &patch->new_data, &patch->new_blob)) < 0) - goto cleanup; - if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0) - goto cleanup; - } - - /* if we did not previously have the definitive oid, we may have - * incorrect status and need to switch this to UNMODIFIED. - */ - if (check_if_unmodified && - delta->old_file.mode == delta->new_file.mode && - !git_oid__cmp(&delta->old_file.oid, &delta->new_file.oid)) { - - delta->status = GIT_DELTA_UNMODIFIED; - - if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) - goto cleanup; - } - -cleanup: - if ((delta->flags & KNOWN_BINARY_FLAGS) == 0) - update_delta_is_binary(delta); - - if (!error) { - patch->flags |= GIT_DIFF_PATCH_LOADED; - - /* patch is diffable only for non-binary, modified files where at - * least one side has data and there is actual change in the data - */ - if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0 && - delta->status != GIT_DELTA_UNMODIFIED && - (patch->old_data.len || patch->new_data.len) && - (patch->old_data.len != patch->new_data.len || - !git_oid_equal(&delta->old_file.oid, &delta->new_file.oid))) - patch->flags |= GIT_DIFF_PATCH_DIFFABLE; - } - - return error; -} - -static int diff_patch_cb(void *priv, mmbuffer_t *bufs, int len) -{ - git_diff_patch *patch = priv; - diff_context *ctxt = patch->ctxt; - - if (len == 1) { - ctxt->error = parse_hunk_header(&ctxt->range, bufs[0].ptr); - if (ctxt->error < 0) - return ctxt->error; - - if (ctxt->hunk_cb != NULL && - ctxt->hunk_cb(patch->delta, &ctxt->range, - bufs[0].ptr, bufs[0].size, ctxt->payload)) - ctxt->error = GIT_EUSER; - } - - if (len == 2 || len == 3) { - /* expect " "/"-"/"+", then data */ - char origin = - (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION : - (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION : - GIT_DIFF_LINE_CONTEXT; - - if (ctxt->data_cb != NULL && - ctxt->data_cb(patch->delta, &ctxt->range, - origin, bufs[1].ptr, bufs[1].size, ctxt->payload)) - ctxt->error = GIT_EUSER; - } - - if (len == 3 && !ctxt->error) { - /* If we have a '+' and a third buf, then we have added a line - * without a newline and the old code had one, so DEL_EOFNL. - * If we have a '-' and a third buf, then we have removed a line - * with out a newline but added a blank line, so ADD_EOFNL. - */ - char origin = - (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL : - (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL : - GIT_DIFF_LINE_CONTEXT_EOFNL; - - if (ctxt->data_cb != NULL && - ctxt->data_cb(patch->delta, &ctxt->range, - origin, bufs[2].ptr, bufs[2].size, ctxt->payload)) - ctxt->error = GIT_EUSER; - } - - return ctxt->error; -} - -static int diff_patch_generate( - diff_context *ctxt, git_diff_patch *patch) -{ - int error = 0; - xdemitcb_t xdiff_callback; - mmfile_t old_xdiff_data, new_xdiff_data; - - if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0) - return 0; - - if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0) - if ((error = diff_patch_load(ctxt, patch)) < 0) - return error; - - if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0) - return 0; - - if (!ctxt->file_cb && !ctxt->hunk_cb) - return 0; - - patch->ctxt = ctxt; - - memset(&xdiff_callback, 0, sizeof(xdiff_callback)); - xdiff_callback.outf = diff_patch_cb; - xdiff_callback.priv = patch; - - old_xdiff_data.ptr = patch->old_data.data; - old_xdiff_data.size = patch->old_data.len; - new_xdiff_data.ptr = patch->new_data.data; - new_xdiff_data.size = patch->new_data.len; - - xdl_diff(&old_xdiff_data, &new_xdiff_data, - &ctxt->xdiff_params, &ctxt->xdiff_config, &xdiff_callback); - - error = ctxt->error; - - if (!error) - patch->flags |= GIT_DIFF_PATCH_DIFFED; - - return error; -} - -static void diff_patch_unload(git_diff_patch *patch) -{ - if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0) { - patch->flags = (patch->flags & ~GIT_DIFF_PATCH_DIFFED); - - patch->hunks_size = 0; - patch->lines_size = 0; - } - - if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0) { - patch->flags = (patch->flags & ~GIT_DIFF_PATCH_LOADED); - - release_content( - &patch->delta->old_file, &patch->old_data, patch->old_blob); - release_content( - &patch->delta->new_file, &patch->new_data, patch->new_blob); - } -} - -static void diff_patch_free(git_diff_patch *patch) -{ - diff_patch_unload(patch); - - git__free(patch->lines); - patch->lines = NULL; - patch->lines_asize = 0; - - git__free(patch->hunks); - patch->hunks = NULL; - patch->hunks_asize = 0; - - if (!(patch->flags & GIT_DIFF_PATCH_ALLOCATED)) - return; - - patch->flags = 0; - - git_diff_list_free(patch->diff); /* decrements refcount */ - - git__free(patch); -} - -#define MAX_HUNK_STEP 128 -#define MIN_HUNK_STEP 8 -#define MAX_LINE_STEP 256 -#define MIN_LINE_STEP 8 - -static int diff_patch_hunk_cb( - const git_diff_delta *delta, - const git_diff_range *range, - const char *header, - size_t header_len, - void *payload) -{ - git_diff_patch *patch = payload; - diff_patch_hunk *hunk; - - GIT_UNUSED(delta); - - if (patch->hunks_size >= patch->hunks_asize) { - size_t new_size; - diff_patch_hunk *new_hunks; - - if (patch->hunks_asize > MAX_HUNK_STEP) - new_size = patch->hunks_asize + MAX_HUNK_STEP; - else - new_size = patch->hunks_asize * 2; - if (new_size < MIN_HUNK_STEP) - new_size = MIN_HUNK_STEP; - - new_hunks = git__realloc( - patch->hunks, new_size * sizeof(diff_patch_hunk)); - if (!new_hunks) - return -1; - - patch->hunks = new_hunks; - patch->hunks_asize = new_size; - } - - hunk = &patch->hunks[patch->hunks_size++]; - - memcpy(&hunk->range, range, sizeof(hunk->range)); - - assert(header_len + 1 < sizeof(hunk->header)); - memcpy(&hunk->header, header, header_len); - hunk->header[header_len] = '\0'; - hunk->header_len = header_len; - - hunk->line_start = patch->lines_size; - hunk->line_count = 0; - - patch->oldno = range->old_start; - patch->newno = range->new_start; - - return 0; -} - -static int diff_patch_line_cb( - const git_diff_delta *delta, - const git_diff_range *range, - char line_origin, - const char *content, - size_t content_len, - void *payload) -{ - git_diff_patch *patch = payload; - diff_patch_hunk *hunk; - diff_patch_line *line; - - GIT_UNUSED(delta); - GIT_UNUSED(range); - - assert(patch->hunks_size > 0); - assert(patch->hunks != NULL); - - hunk = &patch->hunks[patch->hunks_size - 1]; - - if (patch->lines_size >= patch->lines_asize) { - size_t new_size; - diff_patch_line *new_lines; - - if (patch->lines_asize > MAX_LINE_STEP) - new_size = patch->lines_asize + MAX_LINE_STEP; - else - new_size = patch->lines_asize * 2; - if (new_size < MIN_LINE_STEP) - new_size = MIN_LINE_STEP; - - new_lines = git__realloc( - patch->lines, new_size * sizeof(diff_patch_line)); - if (!new_lines) - return -1; - - patch->lines = new_lines; - patch->lines_asize = new_size; - } - - line = &patch->lines[patch->lines_size++]; - - line->ptr = content; - line->len = content_len; - line->origin = line_origin; - - /* do some bookkeeping so we can provide old/new line numbers */ - - for (line->lines = 0; content_len > 0; --content_len) { - if (*content++ == '\n') - ++line->lines; - } - - switch (line_origin) { - case GIT_DIFF_LINE_ADDITION: - case GIT_DIFF_LINE_DEL_EOFNL: - line->oldno = -1; - line->newno = patch->newno; - patch->newno += line->lines; - break; - case GIT_DIFF_LINE_DELETION: - case GIT_DIFF_LINE_ADD_EOFNL: - line->oldno = patch->oldno; - line->newno = -1; - patch->oldno += line->lines; - break; - default: - line->oldno = patch->oldno; - line->newno = patch->newno; - patch->oldno += line->lines; - patch->newno += line->lines; - break; - } - - hunk->line_count++; - - return 0; -} - -static int diff_required(git_diff_list *diff, const char *action) -{ - if (!diff) { - giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action); - return -1; - } - - return 0; -} - -int git_diff_foreach( - git_diff_list *diff, - git_diff_file_cb file_cb, - git_diff_hunk_cb hunk_cb, - git_diff_data_cb data_cb, - void *payload) -{ - int error = 0; - diff_context ctxt; - size_t idx; - git_diff_patch patch; - - if (diff_required(diff, "git_diff_foreach") < 0) - return -1; - - if (diff_context_init( - &ctxt, diff, NULL, NULL, file_cb, hunk_cb, data_cb, payload) < 0) - return -1; - - diff_patch_init(&ctxt, &patch); - - git_vector_foreach(&diff->deltas, idx, patch.delta) { - - /* check flags against patch status */ - if (git_diff_delta__should_skip(ctxt.opts, patch.delta)) - continue; - - if (!(error = diff_patch_load(&ctxt, &patch))) { - - /* invoke file callback */ - error = diff_delta_file_callback(&ctxt, patch.delta, idx); - - /* generate diffs and invoke hunk and line callbacks */ - if (!error) - error = diff_patch_generate(&ctxt, &patch); - - diff_patch_unload(&patch); - } - - if (error < 0) - break; - } - - if (error == GIT_EUSER) - giterr_clear(); /* don't let error message leak */ - - return error; -} - -static void set_data_from_blob( - const git_blob *blob, git_map *map, git_diff_file *file) -{ - if (blob) { - file->size = git_blob_rawsize(blob); - git_oid_cpy(&file->oid, git_object_id((const git_object *)blob)); - file->mode = 0644; - - map->len = (size_t)file->size; - map->data = (char *)git_blob_rawcontent(blob); - } else { - file->size = 0; - file->flags |= GIT_DIFF_FLAG__NO_DATA; - - map->len = 0; - map->data = ""; - } -} - -static void set_data_from_buffer( - const char *buffer, size_t buffer_len, git_map *map, git_diff_file *file) -{ - file->size = (git_off_t)buffer_len; - file->mode = 0644; - map->len = buffer_len; - - if (!buffer) { - file->flags |= GIT_DIFF_FLAG__NO_DATA; - map->data = NULL; - } else { - map->data = (char *)buffer; - git_odb_hash(&file->oid, buffer, buffer_len, GIT_OBJ_BLOB); - } -} - -typedef struct { - diff_context ctxt; - git_diff_delta delta; - git_diff_patch patch; -} diff_single_data; - -static int diff_single_init( - diff_single_data *data, - git_repository *repo, - const git_diff_options *opts, - git_diff_file_cb file_cb, - git_diff_hunk_cb hunk_cb, - git_diff_data_cb data_cb, - void *payload) -{ - GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); - - memset(data, 0, sizeof(*data)); - - if (diff_context_init( - &data->ctxt, NULL, repo, opts, - file_cb, hunk_cb, data_cb, payload) < 0) - return -1; - - diff_patch_init(&data->ctxt, &data->patch); - - return 0; -} - -static int diff_single_apply(diff_single_data *data) -{ - int error; - git_diff_delta *delta = &data->delta; - bool has_old = ((delta->old_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0); - bool has_new = ((delta->new_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0); - - /* finish setting up fake git_diff_delta record and loaded data */ - - data->patch.delta = delta; - delta->flags = delta->flags & ~KNOWN_BINARY_FLAGS; - - delta->status = has_new ? - (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) : - (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED); - - if (git_oid__cmp(&delta->new_file.oid, &delta->old_file.oid) == 0) - delta->status = GIT_DELTA_UNMODIFIED; - - if ((error = diff_delta_is_binary_by_content( - &data->ctxt, delta, &delta->old_file, &data->patch.old_data)) < 0 || - (error = diff_delta_is_binary_by_content( - &data->ctxt, delta, &delta->new_file, &data->patch.new_data)) < 0) - goto cleanup; - - data->patch.flags |= GIT_DIFF_PATCH_LOADED; - - if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0 && - delta->status != GIT_DELTA_UNMODIFIED) - data->patch.flags |= GIT_DIFF_PATCH_DIFFABLE; - - /* do diffs */ - - if (!(error = diff_delta_file_callback(&data->ctxt, delta, 1))) - error = diff_patch_generate(&data->ctxt, &data->patch); - -cleanup: - if (error == GIT_EUSER) - giterr_clear(); - - diff_patch_unload(&data->patch); - - return error; -} - -int git_diff_blobs( - const git_blob *old_blob, - const git_blob *new_blob, - const git_diff_options *options, - git_diff_file_cb file_cb, - git_diff_hunk_cb hunk_cb, - git_diff_data_cb data_cb, - void *payload) -{ - int error; - diff_single_data d; - git_repository *repo = - new_blob ? git_object_owner((const git_object *)new_blob) : - old_blob ? git_object_owner((const git_object *)old_blob) : NULL; - - if (!repo) /* Hmm, given two NULL blobs, silently do no callbacks? */ - return 0; - - if ((error = diff_single_init( - &d, repo, options, file_cb, hunk_cb, data_cb, payload)) < 0) - return error; - - if (options && (options->flags & GIT_DIFF_REVERSE) != 0) { - const git_blob *swap = old_blob; - old_blob = new_blob; - new_blob = swap; - } - - set_data_from_blob(old_blob, &d.patch.old_data, &d.delta.old_file); - set_data_from_blob(new_blob, &d.patch.new_data, &d.delta.new_file); - - return diff_single_apply(&d); -} - -int git_diff_blob_to_buffer( - const git_blob *old_blob, - const char *buf, - size_t buflen, - const git_diff_options *options, - git_diff_file_cb file_cb, - git_diff_hunk_cb hunk_cb, - git_diff_data_cb data_cb, - void *payload) -{ - int error; - diff_single_data d; - git_repository *repo = - old_blob ? git_object_owner((const git_object *)old_blob) : NULL; - - if (!repo && !buf) /* Hmm, given NULLs, silently do no callbacks? */ - return 0; - - if ((error = diff_single_init( - &d, repo, options, file_cb, hunk_cb, data_cb, payload)) < 0) - return error; - - if (options && (options->flags & GIT_DIFF_REVERSE) != 0) { - set_data_from_buffer(buf, buflen, &d.patch.old_data, &d.delta.old_file); - set_data_from_blob(old_blob, &d.patch.new_data, &d.delta.new_file); - } else { - set_data_from_blob(old_blob, &d.patch.old_data, &d.delta.old_file); - set_data_from_buffer(buf, buflen, &d.patch.new_data, &d.delta.new_file); - } - - return diff_single_apply(&d); -} - -size_t git_diff_num_deltas(git_diff_list *diff) -{ - assert(diff); - return (size_t)diff->deltas.length; -} - -size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type) -{ - size_t i, count = 0; - git_diff_delta *delta; - - assert(diff); - - git_vector_foreach(&diff->deltas, i, delta) { - count += (delta->status == type); - } - - return count; -} - -int git_diff_get_patch( - git_diff_patch **patch_ptr, - const git_diff_delta **delta_ptr, - git_diff_list *diff, - size_t idx) -{ - int error; - diff_context ctxt; - git_diff_delta *delta; - git_diff_patch *patch; - - if (patch_ptr) - *patch_ptr = NULL; - if (delta_ptr) - *delta_ptr = NULL; - - if (diff_required(diff, "git_diff_get_patch") < 0) - return -1; - - if (diff_context_init( - &ctxt, diff, NULL, NULL, - NULL, diff_patch_hunk_cb, diff_patch_line_cb, NULL) < 0) - return -1; - - delta = git_vector_get(&diff->deltas, idx); - if (!delta) { - giterr_set(GITERR_INVALID, "Index out of range for delta in diff"); - return GIT_ENOTFOUND; - } - - if (delta_ptr) - *delta_ptr = delta; - - if (!patch_ptr && - ((delta->flags & KNOWN_BINARY_FLAGS) != 0 || - (diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0)) - return 0; - - if (git_diff_delta__should_skip(ctxt.opts, delta)) - return 0; - - /* Don't load the patch if the user doesn't want it */ - if (!patch_ptr) - return 0; - - patch = diff_patch_alloc(&ctxt, delta); - if (!patch) - return -1; - - if (!(error = diff_patch_load(&ctxt, patch))) { - ctxt.payload = patch; - - error = diff_patch_generate(&ctxt, patch); - - if (error == GIT_EUSER) - error = ctxt.error; - } - - if (error) - git_diff_patch_free(patch); - else if (patch_ptr) - *patch_ptr = patch; - - return error; -} - -void git_diff_patch_free(git_diff_patch *patch) -{ - if (patch) - GIT_REFCOUNT_DEC(patch, diff_patch_free); -} - -const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch) -{ - assert(patch); - return patch->delta; -} - -size_t git_diff_patch_num_hunks(git_diff_patch *patch) -{ - assert(patch); - return patch->hunks_size; -} - -int git_diff_patch_line_stats( - size_t *total_ctxt, - size_t *total_adds, - size_t *total_dels, - const git_diff_patch *patch) -{ - size_t totals[3], idx; - - memset(totals, 0, sizeof(totals)); - - for (idx = 0; idx < patch->lines_size; ++idx) { - switch (patch->lines[idx].origin) { - case GIT_DIFF_LINE_CONTEXT: totals[0]++; break; - case GIT_DIFF_LINE_ADDITION: totals[1]++; break; - case GIT_DIFF_LINE_DELETION: totals[2]++; break; - default: - /* diff --stat and --numstat don't count EOFNL marks because - * they will always be paired with a ADDITION or DELETION line. - */ - break; - } - } - - if (total_ctxt) - *total_ctxt = totals[0]; - if (total_adds) - *total_adds = totals[1]; - if (total_dels) - *total_dels = totals[2]; - - return 0; -} - -static int diff_error_outofrange(const char *thing) -{ - giterr_set(GITERR_INVALID, "Diff patch %s index out of range", thing); - return GIT_ENOTFOUND; -} - -int git_diff_patch_get_hunk( - const git_diff_range **range, - const char **header, - size_t *header_len, - size_t *lines_in_hunk, - git_diff_patch *patch, - size_t hunk_idx) -{ - diff_patch_hunk *hunk; - - assert(patch); - - if (hunk_idx >= patch->hunks_size) { - if (range) *range = NULL; - if (header) *header = NULL; - if (header_len) *header_len = 0; - if (lines_in_hunk) *lines_in_hunk = 0; - - return diff_error_outofrange("hunk"); - } - - hunk = &patch->hunks[hunk_idx]; - - if (range) *range = &hunk->range; - if (header) *header = hunk->header; - if (header_len) *header_len = hunk->header_len; - if (lines_in_hunk) *lines_in_hunk = hunk->line_count; - - return 0; -} - -int git_diff_patch_num_lines_in_hunk( - git_diff_patch *patch, - size_t hunk_idx) -{ - assert(patch); - - if (hunk_idx >= patch->hunks_size) - return diff_error_outofrange("hunk"); - else - return (int)patch->hunks[hunk_idx].line_count; -} - -int git_diff_patch_get_line_in_hunk( - char *line_origin, - const char **content, - size_t *content_len, - int *old_lineno, - int *new_lineno, - git_diff_patch *patch, - size_t hunk_idx, - size_t line_of_hunk) -{ - diff_patch_hunk *hunk; - diff_patch_line *line; - const char *thing; - - assert(patch); - - if (hunk_idx >= patch->hunks_size) { - thing = "hunk"; - goto notfound; - } - hunk = &patch->hunks[hunk_idx]; - - if (line_of_hunk >= hunk->line_count) { - thing = "link"; - goto notfound; - } - - line = &patch->lines[hunk->line_start + line_of_hunk]; - - if (line_origin) *line_origin = line->origin; - if (content) *content = line->ptr; - if (content_len) *content_len = line->len; - if (old_lineno) *old_lineno = (int)line->oldno; - if (new_lineno) *new_lineno = (int)line->newno; - - return 0; - -notfound: - if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT; - if (content) *content = NULL; - if (content_len) *content_len = 0; - if (old_lineno) *old_lineno = -1; - if (new_lineno) *new_lineno = -1; - - return diff_error_outofrange(thing); -} - -int git_diff__paired_foreach( - git_diff_list *idx2head, - git_diff_list *wd2idx, - int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload), - void *payload) -{ - int cmp; - git_diff_delta *i2h, *w2i; - size_t i, j, i_max, j_max; - int (*strcomp)(const char *, const char *); - - i_max = idx2head ? idx2head->deltas.length : 0; - j_max = wd2idx ? wd2idx->deltas.length : 0; - - /* Get appropriate strcmp function */ - strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL; - - /* Assert both iterators use matching ignore-case. If this function ever - * supports merging diffs that are not sorted by the same function, then - * it will need to spool and sort on one of the results before merging - */ - if (idx2head && wd2idx) { - assert(idx2head->strcomp == wd2idx->strcomp); - } - - for (i = 0, j = 0; i < i_max || j < j_max; ) { - i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL; - w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL; - - cmp = !w2i ? -1 : !i2h ? 1 : - strcomp(i2h->old_file.path, w2i->old_file.path); - - if (cmp < 0) { - if (cb(i2h, NULL, payload)) - return GIT_EUSER; - i++; - } else if (cmp > 0) { - if (cb(NULL, w2i, payload)) - return GIT_EUSER; - j++; - } else { - if (cb(i2h, w2i, payload)) - return GIT_EUSER; - i++; j++; - } - } - - return 0; -} diff --git a/src/diff_output.h b/src/diff_output.h deleted file mode 100644 index 083355676..000000000 --- a/src/diff_output.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ -#ifndef INCLUDE_diff_output_h__ -#define INCLUDE_diff_output_h__ - -#include "git2/blob.h" -#include "diff.h" -#include "map.h" -#include "xdiff/xdiff.h" - -#define MAX_DIFF_FILESIZE 0x20000000 - -enum { - GIT_DIFF_PATCH_ALLOCATED = (1 << 0), - GIT_DIFF_PATCH_PREPPED = (1 << 1), - GIT_DIFF_PATCH_LOADED = (1 << 2), - GIT_DIFF_PATCH_DIFFABLE = (1 << 3), - GIT_DIFF_PATCH_DIFFED = (1 << 4), -}; - -/* context for performing diffs */ -typedef struct { - git_repository *repo; - git_diff_list *diff; - const git_diff_options *opts; - git_diff_file_cb file_cb; - git_diff_hunk_cb hunk_cb; - git_diff_data_cb data_cb; - void *payload; - int error; - git_diff_range range; - xdemitconf_t xdiff_config; - xpparam_t xdiff_params; -} diff_context; - -/* cached information about a single span in a diff */ -typedef struct diff_patch_line diff_patch_line; -struct diff_patch_line { - const char *ptr; - size_t len; - size_t lines, oldno, newno; - char origin; -}; - -/* cached information about a hunk in a diff */ -typedef struct diff_patch_hunk diff_patch_hunk; -struct diff_patch_hunk { - git_diff_range range; - char header[128]; - size_t header_len; - size_t line_start; - size_t line_count; -}; - -struct git_diff_patch { - git_refcount rc; - git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */ - git_diff_delta *delta; - diff_context *ctxt; /* only valid while generating patch */ - git_iterator_type_t old_src; - git_iterator_type_t new_src; - git_blob *old_blob; - git_blob *new_blob; - git_map old_data; - git_map new_data; - uint32_t flags; - diff_patch_hunk *hunks; - size_t hunks_asize, hunks_size; - diff_patch_line *lines; - size_t lines_asize, lines_size; - size_t oldno, newno; -}; - -/* context for performing diff on a single delta */ -typedef struct { - git_diff_patch *patch; - uint32_t prepped : 1; - uint32_t loaded : 1; - uint32_t diffable : 1; - uint32_t diffed : 1; -} diff_delta_context; - -extern int git_diff__paired_foreach( - git_diff_list *idx2head, - git_diff_list *wd2idx, - int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload), - void *payload); - -#endif diff --git a/src/diff_patch.c b/src/diff_patch.c new file mode 100644 index 000000000..d7eb69db6 --- /dev/null +++ b/src/diff_patch.c @@ -0,0 +1,723 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#include "common.h" +#include "diff.h" +#include "diff_file.h" +#include "diff_driver.h" +#include "diff_patch.h" +#include "diff_xdiff.h" + +static void diff_output_init(git_diff_output*, const git_diff_options*, + git_diff_file_cb, git_diff_hunk_cb, git_diff_data_cb, void*); + +static void diff_output_to_patch(git_diff_output *, git_diff_patch *); + +static void diff_patch_update_binary(git_diff_patch *patch) +{ + if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0) + return; + + if ((patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0 || + (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) + patch->delta->flags |= GIT_DIFF_FLAG_BINARY; + + else if ((patch->ofile.file.flags & DIFF_FLAGS_NOT_BINARY) != 0 && + (patch->nfile.file.flags & DIFF_FLAGS_NOT_BINARY) != 0) + patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY; +} + +static void diff_patch_init_common(git_diff_patch *patch) +{ + diff_patch_update_binary(patch); + + if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) + patch->flags |= GIT_DIFF_PATCH_LOADED; /* set LOADED but not DIFFABLE */ + + patch->flags |= GIT_DIFF_PATCH_INITIALIZED; + + if (patch->diff) + git_diff_list_addref(patch->diff); +} + +static int diff_patch_init_from_diff( + git_diff_patch *patch, git_diff_list *diff, size_t delta_index) +{ + int error = 0; + + memset(patch, 0, sizeof(*patch)); + patch->diff = diff; + patch->delta = git_vector_get(&diff->deltas, delta_index); + patch->delta_index = delta_index; + + if ((error = diff_file_content_init_from_diff( + &patch->ofile, diff, delta_index, true)) < 0 || + (error = diff_file_content_init_from_diff( + &patch->nfile, diff, delta_index, false)) < 0) + return error; + + diff_patch_init_common(patch); + + return 0; +} + +static int diff_patch_alloc_from_diff( + git_diff_patch **out, + git_diff_list *diff, + size_t delta_index) +{ + int error; + git_diff_patch *patch = git__calloc(1, sizeof(git_diff_patch)); + GITERR_CHECK_ALLOC(patch); + + if (!(error = diff_patch_init_from_diff(patch, diff, delta_index))) { + patch->flags |= GIT_DIFF_PATCH_ALLOCATED; + GIT_REFCOUNT_INC(patch); + } else { + git__free(patch); + patch = NULL; + } + + *out = patch; + return error; +} + +static int diff_patch_load(git_diff_patch *patch, git_diff_output *output) +{ + int error = 0; + bool incomplete_data; + + if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0) + return 0; + + /* if no hunk and data callbacks and user doesn't care if data looks + * binary, then there is no need to actually load the data + */ + if (patch->ofile.opts && + (patch->ofile.opts->flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0 && + output && !output->hunk_cb && !output->data_cb) + return 0; + +#define DIFF_FLAGS_KNOWN_DATA (GIT_DIFF_FLAG__NO_DATA|GIT_DIFF_FLAG_VALID_OID) + + incomplete_data = + ((patch->ofile.file.flags & DIFF_FLAGS_KNOWN_DATA) != 0 && + (patch->nfile.file.flags & DIFF_FLAGS_KNOWN_DATA) != 0); + + /* always try to load workdir content first because filtering may + * need 2x data size and this minimizes peak memory footprint + */ + if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) { + if ((error = diff_file_content_load(&patch->ofile)) < 0 || + (patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) + goto cleanup; + } + if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) { + if ((error = diff_file_content_load(&patch->nfile)) < 0 || + (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) + goto cleanup; + } + + /* once workdir has been tried, load other data as needed */ + if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) { + if ((error = diff_file_content_load(&patch->ofile)) < 0 || + (patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) + goto cleanup; + } + if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) { + if ((error = diff_file_content_load(&patch->nfile)) < 0 || + (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) + goto cleanup; + } + + /* if we were previously missing an oid, reassess UNMODIFIED state */ + if (incomplete_data && + patch->ofile.file.mode == patch->nfile.file.mode && + git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid)) + patch->delta->status = GIT_DELTA_UNMODIFIED; + +cleanup: + diff_patch_update_binary(patch); + + if (!error) { + /* patch is diffable only for non-binary, modified files where + * at least one side has data and the data actually changed + */ + if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) == 0 && + patch->delta->status != GIT_DELTA_UNMODIFIED && + (patch->ofile.map.len || patch->nfile.map.len) && + (patch->ofile.map.len != patch->nfile.map.len || + !git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid))) + patch->flags |= GIT_DIFF_PATCH_DIFFABLE; + + patch->flags |= GIT_DIFF_PATCH_LOADED; + } + + return error; +} + +static int diff_patch_file_callback( + git_diff_patch *patch, git_diff_output *output) +{ + float progress; + + if (!output->file_cb) + return 0; + + progress = patch->diff ? + ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f; + + if (output->file_cb(patch->delta, progress, output->payload) != 0) + output->error = GIT_EUSER; + + return output->error; +} + +static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output) +{ + int error = 0; + + if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0) + return 0; + + if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0 && + (error = diff_patch_load(patch, output)) < 0) + return error; + + if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0) + return 0; + + if (output->diff_cb != NULL && + !(error = output->diff_cb(output, patch))) + patch->flags |= GIT_DIFF_PATCH_DIFFED; + + return error; +} + +static void diff_patch_free(git_diff_patch *patch) +{ + diff_file_content_clear(&patch->ofile); + diff_file_content_clear(&patch->nfile); + + git_array_clear(patch->lines); + git_array_clear(patch->hunks); + + git_diff_list_free(patch->diff); /* decrements refcount */ + patch->diff = NULL; + + git_pool_clear(&patch->flattened); + + if (patch->flags & GIT_DIFF_PATCH_ALLOCATED) + git__free(patch); +} + +static int diff_required(git_diff_list *diff, const char *action) +{ + if (diff) + return 0; + giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action); + return -1; +} + +int git_diff_foreach( + git_diff_list *diff, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_data_cb data_cb, + void *payload) +{ + int error = 0; + git_xdiff_output xo; + size_t idx; + git_diff_patch patch; + + if (diff_required(diff, "git_diff_foreach") < 0) + return -1; + + diff_output_init((git_diff_output *)&xo, + &diff->opts, file_cb, hunk_cb, data_cb, payload); + git_xdiff_init(&xo, &diff->opts); + + git_vector_foreach(&diff->deltas, idx, patch.delta) { + /* check flags against patch status */ + if (git_diff_delta__should_skip(&diff->opts, patch.delta)) + continue; + + if (!(error = diff_patch_init_from_diff(&patch, diff, idx))) { + + error = diff_patch_file_callback(&patch, (git_diff_output *)&xo); + + if (!error) + error = diff_patch_generate(&patch, (git_diff_output *)&xo); + + git_diff_patch_free(&patch); + } + + if (error < 0) + break; + } + + if (error == GIT_EUSER) + giterr_clear(); /* don't leave error message set invalidly */ + return error; +} + +typedef struct { + git_xdiff_output xo; + git_diff_patch patch; + git_diff_delta delta; +} diff_single_info; + +static int diff_single_generate(diff_single_info *info) +{ + int error = 0; + git_diff_patch *patch = &info->patch; + bool has_old = ((patch->ofile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0); + bool has_new = ((patch->nfile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0); + + info->delta.status = has_new ? + (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) : + (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED); + + if (git_oid_equal(&patch->nfile.file.oid, &patch->ofile.file.oid)) + info->delta.status = GIT_DELTA_UNMODIFIED; + + patch->delta = &info->delta; + + diff_patch_init_common(patch); + + error = diff_patch_file_callback(patch, (git_diff_output *)&info->xo); + + if (!error) + error = diff_patch_generate(patch, (git_diff_output *)&info->xo); + + if (error == GIT_EUSER) + giterr_clear(); /* don't leave error message set invalidly */ + + return error; +} + +int git_diff_blobs( + const git_blob *old_blob, + const git_blob *new_blob, + const git_diff_options *opts, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_data_cb data_cb, + void *payload) +{ + int error = 0; + diff_single_info info; + git_repository *repo = + new_blob ? git_object_owner((const git_object *)new_blob) : + old_blob ? git_object_owner((const git_object *)old_blob) : NULL; + + GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); + + if (!repo) /* Hmm, given two NULL blobs, silently do no callbacks? */ + return 0; + + if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { + const git_blob *swap = old_blob; + old_blob = new_blob; + new_blob = swap; + } + + memset(&info, 0, sizeof(info)); + + diff_output_init((git_diff_output *)&info.xo, + opts, file_cb, hunk_cb, data_cb, payload); + git_xdiff_init(&info.xo, opts); + + if (!(error = diff_file_content_init_from_blob( + &info.patch.ofile, repo, opts, old_blob)) && + !(error = diff_file_content_init_from_blob( + &info.patch.nfile, repo, opts, new_blob))) + error = diff_single_generate(&info); + + git_diff_patch_free(&info.patch); + + return error; +} + +int git_diff_blob_to_buffer( + const git_blob *old_blob, + const char *buf, + size_t buflen, + const git_diff_options *opts, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_data_cb data_cb, + void *payload) +{ + int error = 0; + diff_single_info info; + git_repository *repo = + old_blob ? git_object_owner((const git_object *)old_blob) : NULL; + + GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); + + if (!repo && !buf) /* Hmm, given NULLs, silently do no callbacks? */ + return 0; + + memset(&info, 0, sizeof(info)); + + diff_output_init((git_diff_output *)&info.xo, + opts, file_cb, hunk_cb, data_cb, payload); + git_xdiff_init(&info.xo, opts); + + if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { + if (!(error = diff_file_content_init_from_raw( + &info.patch.ofile, repo, opts, buf, buflen))) + error = diff_file_content_init_from_blob( + &info.patch.nfile, repo, opts, old_blob); + } else { + if (!(error = diff_file_content_init_from_blob( + &info.patch.ofile, repo, opts, old_blob))) + error = diff_file_content_init_from_raw( + &info.patch.nfile, repo, opts, buf, buflen); + } + + error = diff_single_generate(&info); + + git_diff_patch_free(&info.patch); + + return error; +} + +int git_diff_get_patch( + git_diff_patch **patch_ptr, + const git_diff_delta **delta_ptr, + git_diff_list *diff, + size_t idx) +{ + int error = 0; + git_xdiff_output xo; + git_diff_delta *delta = NULL; + git_diff_patch *patch = NULL; + + if (patch_ptr) *patch_ptr = NULL; + if (delta_ptr) *delta_ptr = NULL; + + if (diff_required(diff, "git_diff_get_patch") < 0) + return -1; + + delta = git_vector_get(&diff->deltas, idx); + if (!delta) { + giterr_set(GITERR_INVALID, "Index out of range for delta in diff"); + return GIT_ENOTFOUND; + } + + if (delta_ptr) + *delta_ptr = delta; + + if (git_diff_delta__should_skip(&diff->opts, delta)) + return 0; + + /* don't load the patch data unless we need it for binary check */ + if (!patch_ptr && + ((delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0 || + (diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0)) + return 0; + + if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0) + return error; + + diff_output_to_patch((git_diff_output *)&xo, patch); + git_xdiff_init(&xo, &diff->opts); + + error = diff_patch_file_callback(patch, (git_diff_output *)&xo); + + if (!error) + error = diff_patch_generate(patch, (git_diff_output *)&xo); + + if (!error) { + /* if cumulative diff size is < 0.5 total size, flatten the patch */ + /* unload the file content */ + } + + if (error || !patch_ptr) + git_diff_patch_free(patch); + else + *patch_ptr = patch; + + if (error == GIT_EUSER) + giterr_clear(); /* don't leave error message set invalidly */ + return error; +} + +void git_diff_patch_free(git_diff_patch *patch) +{ + if (patch) + GIT_REFCOUNT_DEC(patch, diff_patch_free); +} + +const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch) +{ + assert(patch); + return patch->delta; +} + +size_t git_diff_patch_num_hunks(git_diff_patch *patch) +{ + assert(patch); + return git_array_size(patch->hunks); +} + +int git_diff_patch_line_stats( + size_t *total_ctxt, + size_t *total_adds, + size_t *total_dels, + const git_diff_patch *patch) +{ + size_t totals[3], idx; + + memset(totals, 0, sizeof(totals)); + + for (idx = 0; idx < git_array_size(patch->lines); ++idx) { + diff_patch_line *line = git_array_get(patch->lines, idx); + if (!line) + continue; + + switch (line->origin) { + case GIT_DIFF_LINE_CONTEXT: totals[0]++; break; + case GIT_DIFF_LINE_ADDITION: totals[1]++; break; + case GIT_DIFF_LINE_DELETION: totals[2]++; break; + default: + /* diff --stat and --numstat don't count EOFNL marks because + * they will always be paired with a ADDITION or DELETION line. + */ + break; + } + } + + if (total_ctxt) + *total_ctxt = totals[0]; + if (total_adds) + *total_adds = totals[1]; + if (total_dels) + *total_dels = totals[2]; + + return 0; +} + +static int diff_error_outofrange(const char *thing) +{ + giterr_set(GITERR_INVALID, "Diff patch %s index out of range", thing); + return GIT_ENOTFOUND; +} + +int git_diff_patch_get_hunk( + const git_diff_range **range, + const char **header, + size_t *header_len, + size_t *lines_in_hunk, + git_diff_patch *patch, + size_t hunk_idx) +{ + diff_patch_hunk *hunk; + assert(patch); + + hunk = git_array_get(patch->hunks, hunk_idx); + + if (!hunk) { + if (range) *range = NULL; + if (header) *header = NULL; + if (header_len) *header_len = 0; + if (lines_in_hunk) *lines_in_hunk = 0; + return diff_error_outofrange("hunk"); + } + + if (range) *range = &hunk->range; + if (header) *header = hunk->header; + if (header_len) *header_len = hunk->header_len; + if (lines_in_hunk) *lines_in_hunk = hunk->line_count; + return 0; +} + +int git_diff_patch_num_lines_in_hunk(git_diff_patch *patch, size_t hunk_idx) +{ + diff_patch_hunk *hunk; + assert(patch); + + if (!(hunk = git_array_get(patch->hunks, hunk_idx))) + return diff_error_outofrange("hunk"); + return (int)hunk->line_count; +} + +int git_diff_patch_get_line_in_hunk( + char *line_origin, + const char **content, + size_t *content_len, + int *old_lineno, + int *new_lineno, + git_diff_patch *patch, + size_t hunk_idx, + size_t line_of_hunk) +{ + diff_patch_hunk *hunk; + diff_patch_line *line; + const char *thing; + + assert(patch); + + if (!(hunk = git_array_get(patch->hunks, hunk_idx))) { + thing = "hunk"; + goto notfound; + } + + if (line_of_hunk >= hunk->line_count || + !(line = git_array_get( + patch->lines, hunk->line_start + line_of_hunk))) { + thing = "line"; + goto notfound; + } + + if (line_origin) *line_origin = line->origin; + if (content) *content = line->ptr; + if (content_len) *content_len = line->len; + if (old_lineno) *old_lineno = (int)line->oldno; + if (new_lineno) *new_lineno = (int)line->newno; + + return 0; + +notfound: + if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT; + if (content) *content = NULL; + if (content_len) *content_len = 0; + if (old_lineno) *old_lineno = -1; + if (new_lineno) *new_lineno = -1; + + return diff_error_outofrange(thing); +} + + +static int diff_patch_file_cb( + const git_diff_delta *delta, + float progress, + void *payload) +{ + GIT_UNUSED(delta); + GIT_UNUSED(progress); + GIT_UNUSED(payload); + return 0; +} + +static int diff_patch_hunk_cb( + const git_diff_delta *delta, + const git_diff_range *range, + const char *header, + size_t header_len, + void *payload) +{ + git_diff_patch *patch = payload; + diff_patch_hunk *hunk; + + GIT_UNUSED(delta); + + git_array_alloc(patch->hunks, hunk); + GITERR_CHECK_ALLOC(hunk); + + memcpy(&hunk->range, range, sizeof(hunk->range)); + + assert(header_len + 1 < sizeof(hunk->header)); + memcpy(&hunk->header, header, header_len); + hunk->header[header_len] = '\0'; + hunk->header_len = header_len; + + hunk->line_start = git_array_size(patch->lines); + hunk->line_count = 0; + + patch->oldno = range->old_start; + patch->newno = range->new_start; + + return 0; +} + +static int diff_patch_line_cb( + const git_diff_delta *delta, + const git_diff_range *range, + char line_origin, + const char *content, + size_t content_len, + void *payload) +{ + git_diff_patch *patch = payload; + diff_patch_hunk *hunk; + diff_patch_line *line; + + GIT_UNUSED(delta); + GIT_UNUSED(range); + + hunk = git_array_last(patch->hunks); + GITERR_CHECK_ALLOC(hunk); + + git_array_alloc(patch->lines, line); + GITERR_CHECK_ALLOC(line); + + line->ptr = content; + line->len = content_len; + line->origin = line_origin; + + patch->content_size += content_len; + + /* do some bookkeeping so we can provide old/new line numbers */ + + for (line->lines = 0; content_len > 0; --content_len) { + if (*content++ == '\n') + ++line->lines; + } + + switch (line_origin) { + case GIT_DIFF_LINE_ADDITION: + case GIT_DIFF_LINE_DEL_EOFNL: + line->oldno = -1; + line->newno = patch->newno; + patch->newno += line->lines; + break; + case GIT_DIFF_LINE_DELETION: + case GIT_DIFF_LINE_ADD_EOFNL: + line->oldno = patch->oldno; + line->newno = -1; + patch->oldno += line->lines; + break; + default: + line->oldno = patch->oldno; + line->newno = patch->newno; + patch->oldno += line->lines; + patch->newno += line->lines; + break; + } + + hunk->line_count++; + + return 0; +} + +static void diff_output_init( + git_diff_output *out, + const git_diff_options *opts, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_data_cb data_cb, + void *payload) +{ + GIT_UNUSED(opts); + + memset(out, 0, sizeof(*out)); + + out->file_cb = file_cb; + out->hunk_cb = hunk_cb; + out->data_cb = data_cb; + out->payload = payload; +} + +static void diff_output_to_patch(git_diff_output *out, git_diff_patch *patch) +{ + diff_output_init( + out, patch->ofile.opts, + diff_patch_file_cb, diff_patch_hunk_cb, diff_patch_line_cb, patch); +} diff --git a/src/diff_patch.h b/src/diff_patch.h new file mode 100644 index 000000000..7de6e1e5b --- /dev/null +++ b/src/diff_patch.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_diff_patch_h__ +#define INCLUDE_diff_patch_h__ + +#include "common.h" +#include "diff.h" +#include "diff_file.h" +#include "array.h" + +/* cached information about a single span in a diff */ +typedef struct diff_patch_line diff_patch_line; +struct diff_patch_line { + const char *ptr; + size_t len; + size_t lines, oldno, newno; + char origin; +}; + +/* cached information about a hunk in a diff */ +typedef struct diff_patch_hunk diff_patch_hunk; +struct diff_patch_hunk { + git_diff_range range; + char header[128]; + size_t header_len; + size_t line_start; + size_t line_count; +}; + +struct git_diff_patch { + git_refcount rc; + git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */ + git_diff_delta *delta; + size_t delta_index; + git_diff_file_content ofile; + git_diff_file_content nfile; + uint32_t flags; + git_array_t(diff_patch_hunk) hunks; + git_array_t(diff_patch_line) lines; + size_t oldno, newno; + size_t content_size; + git_pool flattened; +}; + +enum { + GIT_DIFF_PATCH_ALLOCATED = (1 << 0), + GIT_DIFF_PATCH_INITIALIZED = (1 << 1), + GIT_DIFF_PATCH_LOADED = (1 << 2), + GIT_DIFF_PATCH_DIFFABLE = (1 << 3), + GIT_DIFF_PATCH_DIFFED = (1 << 4), + GIT_DIFF_PATCH_FLATTENED = (1 << 5), +}; + +typedef struct git_diff_output git_diff_output; +struct git_diff_output { + /* these callbacks are issued with the diff data */ + git_diff_file_cb file_cb; + git_diff_hunk_cb hunk_cb; + git_diff_data_cb data_cb; + void *payload; + + /* this records the actual error in cases where it may be obscured */ + int error; + + /* this callback is used to do the diff and drive the other callbacks. + * see diff_xdiff.h for how to use this in practice for now. + */ + int (*diff_cb)(git_diff_output *output, git_diff_patch *patch); +}; + +#endif diff --git a/src/diff_print.c b/src/diff_print.c index b6fbec829..860876531 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -6,7 +6,8 @@ */ #include "common.h" #include "diff.h" -#include "diff_output.h" +#include "diff_patch.h" +#include "buffer.h" typedef struct { git_diff_list *diff; @@ -390,14 +391,15 @@ int git_diff_patch_print( &pi, &temp, patch->diff, print_cb, payload))) error = print_patch_file(patch->delta, 0, &pi); - for (h = 0; h < patch->hunks_size && !error; ++h) { - diff_patch_hunk *hunk = &patch->hunks[h]; + for (h = 0; h < git_array_size(patch->hunks) && !error; ++h) { + diff_patch_hunk *hunk = git_array_get(patch->hunks, h); error = print_patch_hunk( patch->delta, &hunk->range, hunk->header, hunk->header_len, &pi); for (l = 0; l < hunk->line_count && !error; ++l) { - diff_patch_line *line = &patch->lines[hunk->line_start + l]; + diff_patch_line *line = + git_array_get(patch->lines, hunk->line_start + l); error = print_patch_line( patch->delta, &hunk->range, diff --git a/src/diff_tform.c b/src/diff_tform.c index bc3acae1d..597c240ae 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -5,10 +5,14 @@ * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" -#include "diff.h" + #include "git2/config.h" #include "git2/blob.h" + +#include "diff.h" #include "hashsig.h" +#include "path.h" +#include "fileops.h" static git_diff_delta *diff_delta__dup( const git_diff_delta *d, git_pool *pool) diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c new file mode 100644 index 000000000..1d1c2d54c --- /dev/null +++ b/src/diff_xdiff.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#include "common.h" +#include "diff.h" +#include "diff_driver.h" +#include "diff_patch.h" +#include "diff_xdiff.h" + +static int git_xdiff_scan_int(const char **str, int *value) +{ + const char *scan = *str; + int v = 0, digits = 0; + /* find next digit */ + for (scan = *str; *scan && !git__isdigit(*scan); scan++); + /* parse next number */ + for (; git__isdigit(*scan); scan++, digits++) + v = (v * 10) + (*scan - '0'); + *str = scan; + *value = v; + return (digits > 0) ? 0 : -1; +} + +static int git_xdiff_parse_hunk(git_diff_range *range, const char *header) +{ + /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ + if (*header != '@') + return -1; + if (git_xdiff_scan_int(&header, &range->old_start) < 0) + return -1; + if (*header == ',') { + if (git_xdiff_scan_int(&header, &range->old_lines) < 0) + return -1; + } else + range->old_lines = 1; + if (git_xdiff_scan_int(&header, &range->new_start) < 0) + return -1; + if (*header == ',') { + if (git_xdiff_scan_int(&header, &range->new_lines) < 0) + return -1; + } else + range->new_lines = 1; + if (range->old_start < 0 || range->new_start < 0) + return -1; + + return 0; +} + +typedef struct { + git_xdiff_output *xo; + git_diff_patch *patch; + git_diff_range range; +} git_xdiff_info; + +static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) +{ + git_xdiff_info *info = priv; + git_diff_patch *patch = info->patch; + git_diff_output *output = &info->xo->output; + + if (len == 1) { + output->error = git_xdiff_parse_hunk(&info->range, bufs[0].ptr); + if (output->error < 0) + return output->error; + + if (output->hunk_cb != NULL && + output->hunk_cb(patch->delta, &info->range, + bufs[0].ptr, bufs[0].size, output->payload)) + output->error = GIT_EUSER; + } + + if (len == 2 || len == 3) { + /* expect " "/"-"/"+", then data */ + char origin = + (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION : + (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION : + GIT_DIFF_LINE_CONTEXT; + + if (output->data_cb != NULL && + output->data_cb(patch->delta, &info->range, + origin, bufs[1].ptr, bufs[1].size, output->payload)) + output->error = GIT_EUSER; + } + + if (len == 3 && !output->error) { + /* If we have a '+' and a third buf, then we have added a line + * without a newline and the old code had one, so DEL_EOFNL. + * If we have a '-' and a third buf, then we have removed a line + * with out a newline but added a blank line, so ADD_EOFNL. + */ + char origin = + (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL : + (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL : + GIT_DIFF_LINE_CONTEXT_EOFNL; + + if (output->data_cb != NULL && + output->data_cb(patch->delta, &info->range, + origin, bufs[2].ptr, bufs[2].size, output->payload)) + output->error = GIT_EUSER; + } + + return output->error; +} + +static int git_xdiff(git_diff_output *output, git_diff_patch *patch) +{ + git_xdiff_output *xo = (git_xdiff_output *)output; + git_xdiff_info info; + mmfile_t old_xdiff_data, new_xdiff_data; + + memset(&info, 0, sizeof(info)); + info.patch = patch; + info.xo = xo; + + xo->callback.priv = &info; + + xo->config.find_func_priv = patch->ofile.driver; + xo->config.find_func = patch->ofile.driver ? + git_diff_driver_find_content_fn(patch->ofile.driver) : NULL; + + if (xo->config.find_func != NULL) + xo->config.flags |= XDL_EMIT_FUNCNAMES; + else + xo->config.flags &= ~XDL_EMIT_FUNCNAMES; + + + old_xdiff_data.ptr = patch->ofile.map.data; + old_xdiff_data.size = patch->ofile.map.len; + new_xdiff_data.ptr = patch->nfile.map.data; + new_xdiff_data.size = patch->nfile.map.len; + + xdl_diff(&old_xdiff_data, &new_xdiff_data, + &xo->params, &xo->config, &xo->callback); + + return xo->output.error; +} + +void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) +{ + uint32_t flags = opts ? opts->flags : GIT_DIFF_NORMAL; + + xo->output.diff_cb = git_xdiff; + + memset(&xo->config, 0, sizeof(xo->config)); + xo->config.ctxlen = opts ? opts->context_lines : 3; + xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0; + + memset(&xo->params, 0, sizeof(xo->params)); + if (flags & GIT_DIFF_IGNORE_WHITESPACE) + xo->params.flags |= XDF_WHITESPACE_FLAGS; + if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE) + xo->params.flags |= XDF_IGNORE_WHITESPACE_CHANGE; + if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL) + xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; + + memset(&xo->callback, 0, sizeof(xo->callback)); + xo->callback.outf = git_xdiff_cb; +} diff --git a/src/diff_xdiff.h b/src/diff_xdiff.h new file mode 100644 index 000000000..c547b00cf --- /dev/null +++ b/src/diff_xdiff.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_diff_xdiff_h__ +#define INCLUDE_diff_xdiff_h__ + +#include "diff.h" +#include "diff_patch.h" +#include "xdiff/xdiff.h" + +/* A git_xdiff_output is a git_diff_output with extra fields necessary + * to use libxdiff. Calling git_xdiff_init() will set the diff_cb field + * of the output to use xdiff to generate the diffs. + */ +typedef struct { + git_diff_output output; + + xdemitconf_t config; + xpparam_t params; + xdemitcb_t callback; +} git_xdiff_output; + +void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts); + +#endif diff --git a/src/fetch.c b/src/fetch.c index b5ec69777..03fad5fec 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -16,6 +16,8 @@ #include "pack.h" #include "fetch.h" #include "netops.h" +#include "repository.h" +#include "refs.h" struct filter_payload { git_remote *remote; diff --git a/src/iterator.c b/src/iterator.c index 4360b99ad..76b0e41d0 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -7,6 +7,7 @@ #include "iterator.h" #include "tree.h" +#include "index.h" #include "ignore.h" #include "buffer.h" #include "git2/submodule.h" diff --git a/src/merge.c b/src/merge.c index 047d96013..82d2e6f37 100644 --- a/src/merge.c +++ b/src/merge.c @@ -24,6 +24,8 @@ #include "blob.h" #include "hashsig.h" #include "oid.h" +#include "index.h" +#include "filebuf.h" #include "git2/types.h" #include "git2/repository.h" diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 4083ba9e5..b9e283ac5 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -9,6 +9,7 @@ #include "hash.h" #include "repository.h" #include "fileops.h" +#include "filebuf.h" #include "pack.h" #include "reflog.h" #include "refdb.h" diff --git a/src/refs.c b/src/refs.c index 2b545954d..c0e460cc3 100644 --- a/src/refs.c +++ b/src/refs.c @@ -9,6 +9,7 @@ #include "hash.h" #include "repository.h" #include "fileops.h" +#include "filebuf.h" #include "pack.h" #include "reflog.h" #include "refdb.h" diff --git a/src/remote.h b/src/remote.h index c9c26b77d..dce4803ed 100644 --- a/src/remote.h +++ b/src/remote.h @@ -11,7 +11,7 @@ #include "git2/transport.h" #include "refspec.h" -#include "repository.h" +#include "vector.h" #define GIT_REMOTE_ORIGIN "origin" diff --git a/src/repository.c b/src/repository.c index 2e7a334c9..4514fee23 100644 --- a/src/repository.c +++ b/src/repository.c @@ -17,12 +17,15 @@ #include "tag.h" #include "blob.h" #include "fileops.h" +#include "filebuf.h" +#include "index.h" #include "config.h" #include "refs.h" #include "filter.h" #include "odb.h" #include "remote.h" #include "merge.h" +#include "diff_driver.h" #define GIT_FILE_CONTENT_PREFIX "gitdir:" @@ -108,6 +111,7 @@ void git_repository_free(git_repository *repo) git_cache_free(&repo->objects); git_submodule_config_free(repo); + git_diff_driver_registry_free(repo->diff_drivers); git__free(repo->path_repository); git__free(repo->workdir); diff --git a/src/repository.h b/src/repository.h index bd5f63dac..12dc50d51 100644 --- a/src/repository.h +++ b/src/repository.h @@ -14,15 +14,13 @@ #include "git2/object.h" #include "git2/config.h" -#include "index.h" #include "cache.h" #include "refs.h" #include "buffer.h" -#include "odb.h" #include "object.h" #include "attrcache.h" #include "strmap.h" -#include "refdb.h" +#include "diff_driver.h" #define DOT_GIT ".git" #define GIT_DIR DOT_GIT "/" @@ -108,6 +106,7 @@ struct git_repository { git_cache objects; git_attr_cache attrcache; git_strmap *submodules; + git_diff_driver_registry *diff_drivers; char *path_repository; char *workdir; diff --git a/src/signature.c b/src/signature.c index cd6167fb4..4b8f03a21 100644 --- a/src/signature.c +++ b/src/signature.c @@ -9,6 +9,7 @@ #include "signature.h" #include "repository.h" #include "git2/common.h" +#include "posix.h" void git_signature_free(git_signature *sig) { diff --git a/src/stash.c b/src/stash.c index 19b29be77..1222634d5 100644 --- a/src/stash.c +++ b/src/stash.c @@ -14,6 +14,7 @@ #include "git2/stash.h" #include "git2/status.h" #include "git2/checkout.h" +#include "git2/index.h" #include "signature.h" static int create_error(int error, const char *msg) diff --git a/src/status.c b/src/status.c index 89f3eedb5..712e0d515 100644 --- a/src/status.c +++ b/src/status.c @@ -14,10 +14,10 @@ #include "git2/status.h" #include "repository.h" #include "ignore.h" +#include "index.h" #include "git2/diff.h" #include "diff.h" -#include "diff_output.h" static unsigned int index_delta2status(git_delta_t index_status) { diff --git a/src/submodule.c b/src/submodule.c index 16114d8ac..af488b7f3 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -22,6 +22,8 @@ #include "submodule.h" #include "tree.h" #include "iterator.h" +#include "path.h" +#include "index.h" #define GIT_MODULES_FILE ".gitmodules" diff --git a/src/thread-utils.h b/src/thread-utils.h index 49b5f3b5e..f56f61b50 100644 --- a/src/thread-utils.h +++ b/src/thread-utils.h @@ -7,8 +7,6 @@ #ifndef INCLUDE_thread_utils_h__ #define INCLUDE_thread_utils_h__ -#include "common.h" - /* Common operations even if threading has been disabled */ typedef struct { #if defined(GIT_WIN32) diff --git a/src/tree.c b/src/tree.c index 10d131438..65d01b4d5 100644 --- a/src/tree.c +++ b/src/tree.c @@ -10,6 +10,9 @@ #include "tree.h" #include "git2/repository.h" #include "git2/object.h" +#include "path.h" +#include "tree-cache.h" +#include "index.h" #define DEFAULT_TREE_SIZE 16 #define MAX_FILEMODE_BYTES 6 diff --git a/src/util.h b/src/util.h index 5ae87ac10..43ba79240 100644 --- a/src/util.h +++ b/src/util.h @@ -194,6 +194,8 @@ extern int git__strcasecmp(const char *a, const char *b); extern int git__strncmp(const char *a, const char *b, size_t sz); extern int git__strncasecmp(const char *a, const char *b, size_t sz); +#include "thread-utils.h" + typedef struct { git_atomic refcount; void *owner; diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c index 78ff5ac62..a3a0f8fda 100644 --- a/tests-clar/checkout/index.c +++ b/tests-clar/checkout/index.c @@ -2,6 +2,7 @@ #include "checkout_helpers.h" #include "git2/checkout.h" +#include "fileops.h" #include "repository.h" static git_repository *g_repo; diff --git a/tests-clar/clar.c b/tests-clar/clar.c index fed87c30d..0eae81bf5 100644 --- a/tests-clar/clar.c +++ b/tests-clar/clar.c @@ -183,10 +183,10 @@ clar_run_test( } static void -clar_run_suite(const struct clar_suite *suite) +clar_run_suite(const struct clar_suite *suite, const char *name) { const struct clar_func *test = suite->tests; - size_t i; + size_t i, namelen; if (!suite->enabled) return; @@ -200,7 +200,23 @@ clar_run_suite(const struct clar_suite *suite) _clar.active_suite = suite->name; _clar.suite_errors = 0; + if (name) { + size_t suitelen = strlen(suite->name); + namelen = strlen(name); + if (namelen <= suitelen) { + name = NULL; + } else { + name += suitelen; + while (*name == ':') + ++name; + namelen = strlen(name); + } + } + for (i = 0; i < suite->test_count; ++i) { + if (name && strncmp(test[i].name, name, namelen)) + continue; + _clar.active_test = test[i].name; clar_run_test(&test[i], &suite->initialize, &suite->cleanup); @@ -240,7 +256,7 @@ clar_parse_args(int argc, char **argv) case 'x': { /* given suite name */ int offset = (argument[2] == '=') ? 3 : 2, found = 0; char action = argument[1]; - size_t j, len; + size_t j, len, cmplen; argument += offset; len = strlen(argument); @@ -249,7 +265,11 @@ clar_parse_args(int argc, char **argv) clar_usage(argv[0]); for (j = 0; j < _clar_suite_count; ++j) { - if (strncmp(argument, _clar_suites[j].name, len) == 0) { + cmplen = strlen(_clar_suites[j].name); + if (cmplen > len) + cmplen = len; + + if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) { int exact = !strcmp(argument, _clar_suites[j].name); ++found; @@ -258,9 +278,9 @@ clar_parse_args(int argc, char **argv) _clar.report_suite_names = 1; switch (action) { - case 's': clar_run_suite(&_clar_suites[j]); break; - case 'i': _clar_suites[j].enabled = 1; break; - case 'x': _clar_suites[j].enabled = 0; break; + case 's': clar_run_suite(&_clar_suites[j], argument); break; + case 'i': _clar_suites[j].enabled = 1; break; + case 'x': _clar_suites[j].enabled = 0; break; } if (exact) @@ -318,7 +338,7 @@ clar_test(int argc, char **argv) if (!_clar.suites_ran) { size_t i; for (i = 0; i < _clar_suite_count; ++i) - clar_run_suite(&_clar_suites[i]); + clar_run_suite(&_clar_suites[i], NULL); } clar_print_shutdown( diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c index 8aae1fb52..339b1e70d 100644 --- a/tests-clar/clone/nonetwork.c +++ b/tests-clar/clone/nonetwork.c @@ -1,8 +1,9 @@ #include "clar_libgit2.h" #include "git2/clone.h" -#include "repository.h" #include "remote.h" +#include "fileops.h" +#include "repository.h" #define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository" diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c index f9e913a74..6390957c9 100644 --- a/tests-clar/diff/patch.c +++ b/tests-clar/diff/patch.c @@ -543,7 +543,7 @@ void test_diff_patch__line_counts_with_eofnl(void) "index 378a7d9..3d0154e 100644\n" "--- a/songof7cities.txt\n" "+++ b/songof7cities.txt\n" - "@@ -42,7 +42,7 @@\n" + "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood\n" " \n" " To the sound of trumpets shall their seed restore my Cities\n" " Wealthy and well-weaponed, that once more may I behold\n" diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 8bff96cf2..a9f1b4e20 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -558,7 +558,7 @@ void test_diff_rename__patch(void) git_diff_patch *patch; const git_diff_delta *delta; char *text; - const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n"; + const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@ She sends'em abroad on her own affairs\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n"; old_tree = resolve_commit_oid_to_tree(g_repo, sha0); new_tree = resolve_commit_oid_to_tree(g_repo, sha1); diff --git a/tests-clar/diff/submodules.c b/tests-clar/diff/submodules.c index f152af46f..6e52a6319 100644 --- a/tests-clar/diff/submodules.c +++ b/tests-clar/diff/submodules.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "repository.h" +#include "posix.h" #include "../submodule/submodule_helpers.h" static git_repository *g_repo = NULL; diff --git a/tests-clar/fetchhead/nonetwork.c b/tests-clar/fetchhead/nonetwork.c index ef30679f9..a68ebb0b7 100644 --- a/tests-clar/fetchhead/nonetwork.c +++ b/tests-clar/fetchhead/nonetwork.c @@ -1,6 +1,6 @@ #include "clar_libgit2.h" -#include "repository.h" +#include "fileops.h" #include "fetchhead.h" #include "fetchhead_data.h" diff --git a/tests-clar/merge/merge_helpers.c b/tests-clar/merge/merge_helpers.c index bc31b1f44..e4092787c 100644 --- a/tests-clar/merge/merge_helpers.c +++ b/tests-clar/merge/merge_helpers.c @@ -1,5 +1,5 @@ #include "clar_libgit2.h" -#include "buffer.h" +#include "fileops.h" #include "refs.h" #include "tree.h" #include "merge_helpers.h" diff --git a/tests-clar/odb/alternates.c b/tests-clar/odb/alternates.c index be7bfa9cd..4e876c2b3 100644 --- a/tests-clar/odb/alternates.c +++ b/tests-clar/odb/alternates.c @@ -1,6 +1,6 @@ #include "clar_libgit2.h" #include "odb.h" -#include "repository.h" +#include "filebuf.h" static git_buf destpath, filepath; static const char *paths[] = { diff --git a/tests-clar/online/clone.c b/tests-clar/online/clone.c index aa12e47c9..bc4285a00 100644 --- a/tests-clar/online/clone.c +++ b/tests-clar/online/clone.c @@ -2,8 +2,9 @@ #include "git2/clone.h" #include "git2/cred_helpers.h" -#include "repository.h" #include "remote.h" +#include "fileops.h" +#include "refs.h" #define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository" #define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository" diff --git a/tests-clar/online/fetchhead.c b/tests-clar/online/fetchhead.c index e14ae0926..58717eef8 100644 --- a/tests-clar/online/fetchhead.c +++ b/tests-clar/online/fetchhead.c @@ -1,6 +1,6 @@ #include "clar_libgit2.h" -#include "repository.h" +#include "fileops.h" #include "fetchhead.h" #include "../fetchhead/fetchhead_data.h" #include "git2/clone.h" diff --git a/tests-clar/refs/delete.c b/tests-clar/refs/delete.c index 053f41229..973768aeb 100644 --- a/tests-clar/refs/delete.c +++ b/tests-clar/refs/delete.c @@ -1,7 +1,8 @@ #include "clar_libgit2.h" -#include "repository.h" +#include "fileops.h" #include "git2/reflog.h" +#include "git2/refdb.h" #include "reflog.h" #include "ref_helpers.h" @@ -31,7 +32,7 @@ void test_refs_delete__packed_loose(void) git_buf temp_path = GIT_BUF_INIT; /* Ensure the loose reference exists on the file system */ - cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, packed_test_head_name)); + cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name)); cl_assert(git_path_exists(temp_path.ptr)); /* Lookup the reference */ diff --git a/tests-clar/refs/pack.c b/tests-clar/refs/pack.c index 412c4c5fd..d8d5cc6d0 100644 --- a/tests-clar/refs/pack.c +++ b/tests-clar/refs/pack.c @@ -1,8 +1,10 @@ #include "clar_libgit2.h" -#include "repository.h" +#include "fileops.h" #include "git2/reflog.h" +#include "git2/refdb.h" #include "reflog.h" +#include "refs.h" #include "ref_helpers.h" static const char *loose_tag_ref_name = "refs/tags/e90810b"; @@ -33,7 +35,7 @@ void test_refs_pack__empty(void) // create a packfile for an empty folder git_buf temp_path = GIT_BUF_INIT; - cl_git_pass(git_buf_join_n(&temp_path, '/', 3, g_repo->path_repository, GIT_REFS_HEADS_DIR, "empty_dir")); + cl_git_pass(git_buf_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir")); cl_git_pass(git_futils_mkdir_r(temp_path.ptr, NULL, GIT_REFS_DIR_MODE)); git_buf_free(&temp_path); @@ -60,7 +62,7 @@ void test_refs_pack__loose(void) packall(); /* Ensure the packed-refs file exists */ - cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, GIT_PACKEDREFS_FILE)); + cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), GIT_PACKEDREFS_FILE)); cl_assert(git_path_exists(temp_path.ptr)); /* Ensure the known ref can still be looked up but is now packed */ @@ -69,7 +71,7 @@ void test_refs_pack__loose(void) cl_assert_equal_s(reference->name, loose_tag_ref_name); /* Ensure the known ref has been removed from the loose folder structure */ - cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, loose_tag_ref_name)); + cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), loose_tag_ref_name)); cl_assert(!git_path_exists(temp_path.ptr)); git_reference_free(reference); diff --git a/tests-clar/refs/reflog/reflog.c b/tests-clar/refs/reflog/reflog.c index 1cd0ddd92..095cabf04 100644 --- a/tests-clar/refs/reflog/reflog.c +++ b/tests-clar/refs/reflog/reflog.c @@ -1,6 +1,6 @@ #include "clar_libgit2.h" -#include "repository.h" +#include "fileops.h" #include "git2/reflog.h" #include "reflog.h" diff --git a/tests-clar/refs/rename.c b/tests-clar/refs/rename.c index 5ab84c48e..543bc4d62 100644 --- a/tests-clar/refs/rename.c +++ b/tests-clar/refs/rename.c @@ -1,8 +1,9 @@ #include "clar_libgit2.h" -#include "repository.h" +#include "fileops.h" #include "git2/reflog.h" #include "reflog.h" +#include "refs.h" #include "ref_helpers.h" static const char *loose_tag_ref_name = "refs/tags/e90810b"; @@ -38,7 +39,7 @@ void test_refs_rename__loose(void) const char *new_name = "refs/tags/Nemo/knows/refs.kung-fu"; /* Ensure the ref doesn't exist on the file system */ - cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, new_name)); + cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), new_name)); cl_assert(!git_path_exists(temp_path.ptr)); /* Retrieval of the reference to rename */ @@ -64,7 +65,7 @@ void test_refs_rename__loose(void) cl_assert(reference_is_packed(new_ref) == 0); /* ...and the ref can be found in the file system */ - cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, new_name)); + cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), new_name)); cl_assert(git_path_exists(temp_path.ptr)); git_reference_free(new_ref); @@ -80,7 +81,7 @@ void test_refs_rename__packed(void) const char *brand_new_name = "refs/heads/brand_new_name"; /* Ensure the ref doesn't exist on the file system */ - cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, packed_head_name)); + cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), packed_head_name)); cl_assert(!git_path_exists(temp_path.ptr)); /* The reference can however be looked-up... */ @@ -106,7 +107,7 @@ void test_refs_rename__packed(void) cl_assert(reference_is_packed(new_ref) == 0); /* ...and the ref now happily lives in the file system */ - cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, brand_new_name)); + cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), brand_new_name)); cl_assert(git_path_exists(temp_path.ptr)); git_reference_free(new_ref); @@ -122,7 +123,7 @@ void test_refs_rename__packed_doesnt_pack_others(void) const char *brand_new_name = "refs/heads/brand_new_name"; /* Ensure the other reference exists on the file system */ - cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, packed_test_head_name)); + cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name)); cl_assert(git_path_exists(temp_path.ptr)); /* Lookup the other reference */ diff --git a/tests-clar/repo/discover.c b/tests-clar/repo/discover.c index 3d9aeedd7..f93ff2462 100644 --- a/tests-clar/repo/discover.c +++ b/tests-clar/repo/discover.c @@ -1,9 +1,9 @@ #include "clar_libgit2.h" #include "odb.h" +#include "fileops.h" #include "repository.h" - #define TEMP_REPO_FOLDER "temprepo/" #define DISCOVER_FOLDER TEMP_REPO_FOLDER "discover.git" diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c index 2d3898ba4..4f6879cfc 100644 --- a/tests-clar/status/ignore.c +++ b/tests-clar/status/ignore.c @@ -169,7 +169,7 @@ void test_status_ignore__ignore_pattern_ignorecase(void) cl_git_mkfile("empty_standard_repo/A.txt", "Differs in case"); cl_git_pass(git_repository_index(&index, g_repo)); - ignore_case = index->ignore_case; + ignore_case = (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0; git_index_free(index); cl_git_pass(git_status_file(&flags, g_repo, "A.txt")); diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index 062a09aeb..13335843b 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -105,7 +105,7 @@ void test_status_worktree__swap_subdir_and_file(void) bool ignore_case; cl_git_pass(git_repository_index(&index, repo)); - ignore_case = index->ignore_case; + ignore_case = (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0; git_index_free(index); /* first alter the contents of the worktree */ From bda3fbb1aca1a3259f00effdb809a27839b4cced Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 10 Jun 2013 14:17:54 -0500 Subject: [PATCH 317/384] failing unit test for similar renames --- tests-clar/diff/rename.c | 93 ++++++++++++++++++ .../35/92953ff3ea5e8ba700c429f3aefe33c8806754 | Bin 0 -> 80 bytes .../44/4a76ed3e45b183753f49376af30da8c3fe276a | Bin 0 -> 135 bytes .../47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 | Bin 0 -> 229 bytes .../93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 | Bin 0 -> 229 bytes .../.gitted/refs/heads/renames_similar | 1 + 6 files changed, 94 insertions(+) create mode 100644 tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754 create mode 100644 tests-clar/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a create mode 100644 tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 create mode 100644 tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 create mode 100644 tests-clar/resources/renames/.gitted/refs/heads/renames_similar diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 8bff96cf2..2ec1e20eb 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -811,3 +811,96 @@ void test_diff_rename__from_deleted_to_split(void) git_buf_free(&c1); } + +struct rename_expected +{ + size_t len; + const char **sources; + const char **targets; + + size_t idx; +}; + +int test_names_expected(const git_diff_delta *delta, float progress, void *p) +{ + struct rename_expected *expected = p; + + cl_assert(expected->idx < expected->len); + + cl_assert_equal_i(delta->status, GIT_DELTA_RENAMED); + + cl_assert(git__strcmp(expected->sources[expected->idx], + delta->old_file.path) == 0); + cl_assert(git__strcmp(expected->targets[expected->idx], + delta->new_file.path) == 0); + + expected->idx++; + + return 0; +} + +void test_diff_rename__rejected_match_can_match_others(void) +{ + git_reference *head, *selfsimilar; + git_index *index; + git_tree *tree; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; + git_buf one = GIT_BUF_INIT, two = GIT_BUF_INIT; + const char *sources[] = { "Class1.cs", "Class2.cs" }; + const char *targets[] = { "ClassA.cs", "ClassB.cs" }; + struct rename_expected expect = { 2, sources, targets }; + char *ptr; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); + cl_git_pass(git_reference_symbolic_set_target( + &selfsimilar, head, "refs/heads/renames_similar")); + cl_git_pass(git_checkout_head(g_repo, &opts)); + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_futils_readbuffer(&one, "renames/Class1.cs")); + cl_git_pass(git_futils_readbuffer(&two, "renames/Class2.cs")); + + cl_git_pass(p_unlink("renames/Class1.cs")); + cl_git_pass(p_unlink("renames/Class2.cs")); + + cl_git_pass(git_index_remove_bypath(index, "Class1.cs")); + cl_git_pass(git_index_remove_bypath(index, "Class2.cs")); + + cl_assert(ptr = strstr(one.ptr, "Class1")); + ptr[5] = 'A'; + + cl_assert(ptr = strstr(two.ptr, "Class2")); + ptr[5] = 'B'; + + cl_git_pass( + git_futils_writebuffer(&one, "renames/ClassA.cs", O_RDWR|O_CREAT, 0777)); + cl_git_pass( + git_futils_writebuffer(&two, "renames/ClassB.cs", O_RDWR|O_CREAT, 0777)); + + cl_git_pass(git_index_add_bypath(index, "ClassA.cs")); + cl_git_pass(git_index_add_bypath(index, "ClassB.cs")); + + cl_git_pass(git_index_write(index)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass( + git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + cl_git_pass(git_diff_find_similar(diff, &findopts)); + + cl_git_pass( + git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + + git_tree_free(tree); + git_index_free(index); + git_reference_free(head); + git_reference_free(selfsimilar); + git_buf_free(&one); + git_buf_free(&two); +} diff --git a/tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754 b/tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754 new file mode 100644 index 0000000000000000000000000000000000000000..886271d32ec6291c081a6567f3c59ac813648898 GIT binary patch literal 80 zcmV-W0I&ae0V^p=O;s>AVK6i>Ff%bxaL!3AE;iIlE@p6-@R6(Aq_t4FVw$0DV)%HlW#K4-XP!{MdmFAh+a5ZrubhwlCJ;Z!(XWq#~um z6zri_W*`@AaFjD~bnKsTK}@p^nIdNi>{ pYSY|LN#zO?E$Dq1H8`R}G{cT@=RXpr6)AhHkju`^d;xA6KraBJJyZYy literal 0 HcmV?d00001 diff --git a/tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 b/tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 new file mode 100644 index 0000000000000000000000000000000000000000..aa9192699281b967cb0217cb7f5c68fa2e4ed1cd GIT binary patch literal 229 zcmVz0bP&5PQx$^hB^BweBL3WLY#I&mEbaQp)cUI?rPyCB@P-B+PmXWA))4w z$Y1>Fw@;c*`0&`=eVW8GzFn>Ge1QK|Xp<`UMN+iz!jZG|n~tNzj}7Yht8CGfnFfyu z9of4b0QNh;9d?r#j-rhIN-P{}7O7s*WEQ22B_YLBCeBspmCW!WWkSM1#*wo-7-?p- zgk_@Q7o$^0)fl*}8zAX9WjAN{RXsKiwRiRp{fi5(>;bB53-yexubz~jmPMtfI$d)g fUitEzbl}{;H=v(&thNr<{DKwV;0Evm{E>Pv3uksE literal 0 HcmV?d00001 diff --git a/tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 b/tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 new file mode 100644 index 0000000000000000000000000000000000000000..39105f94de392e169ed8bdac792304731bf62588 GIT binary patch literal 229 zcmVz0bP%=PQ)+_hMD~oo^R+;5YtVl5-f;?zJRZFuNF>H;-KS%?%i>ykWe!u z@)v*l?W3k6-aoWApC<8yZx<^Z4)DKfx}?fOkrb_Y=EzxwO~;GGj}7Yit8CGnnMRLk zda`#r0PJ^wJM3pMoJ1M@l~_2|EK@>R#%6+`voh$!WG~L{Gxg>LvnH* literal 0 HcmV?d00001 diff --git a/tests-clar/resources/renames/.gitted/refs/heads/renames_similar b/tests-clar/resources/renames/.gitted/refs/heads/renames_similar new file mode 100644 index 000000000..8ffb6d94b --- /dev/null +++ b/tests-clar/resources/renames/.gitted/refs/heads/renames_similar @@ -0,0 +1 @@ +444a76ed3e45b183753f49376af30da8c3fe276a From 690bf41ce5f700de787b019ed489cbdbd7f2b697 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 10 Jun 2013 15:16:44 -0500 Subject: [PATCH 318/384] keep source similarity in rename detection --- src/diff_tform.c | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/diff_tform.c b/src/diff_tform.c index bc3acae1d..b1eaf448e 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -684,7 +684,7 @@ int git_diff_find_similar( git_diff_find_options opts; size_t num_rewrites = 0, num_updates = 0; void **cache; /* cache of similarity metric file signatures */ - diff_find_match *matches; /* cache of best matches */ + diff_find_match *match_sources, *match_targets; /* cache of best matches */ if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0) return error; @@ -697,16 +697,18 @@ int git_diff_find_similar( cache = git__calloc(cache_size, sizeof(void *)); GITERR_CHECK_ALLOC(cache); - matches = git__calloc(diff->deltas.length, sizeof(diff_find_match)); - GITERR_CHECK_ALLOC(matches); + match_sources = git__calloc(diff->deltas.length, sizeof(diff_find_match)); + match_targets = git__calloc(diff->deltas.length, sizeof(diff_find_match)); + GITERR_CHECK_ALLOC(match_sources); + GITERR_CHECK_ALLOC(match_targets); /* next find the most similar delta for each rename / copy candidate */ git_vector_foreach(&diff->deltas, i, to) { size_t tried_sources = 0; - matches[i].idx = i; - matches[i].similarity = 0; + match_targets[i].idx = i; + match_targets[i].similarity = 0; /* skip things that are not rename targets */ if (!is_rename_target(diff, &opts, i, cache)) @@ -734,9 +736,12 @@ int git_diff_find_similar( continue; } - if (matches[i].similarity < (uint32_t)similarity) { - matches[i].similarity = (uint32_t)similarity; - matches[i].idx = j; + if (match_targets[i].similarity < (uint32_t)similarity && + match_sources[j].similarity < (uint32_t)similarity) { + match_targets[i].similarity = (uint32_t)similarity; + match_sources[j].similarity = (uint32_t)similarity; + match_targets[i].idx = j; + match_sources[j].idx = i; } } } @@ -744,13 +749,13 @@ int git_diff_find_similar( /* next rewrite the diffs with renames / copies */ git_vector_foreach(&diff->deltas, i, to) { - - /* check if this delta was matched to another one */ - if ((similarity = (int)matches[i].similarity) <= 0) + /* check if this delta was the target of a similarity */ + if ((similarity = (int)match_targets[i].similarity) <= 0) continue; + assert(to && (to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) != 0); - from = GIT_VECTOR_GET(&diff->deltas, matches[i].idx); + from = GIT_VECTOR_GET(&diff->deltas, match_targets[i].idx); assert(from && (from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) != 0); /* possible scenarios: @@ -847,14 +852,14 @@ int git_diff_find_similar( /* in the off chance that we've just swapped the new * element into the correct place, clear the SPLIT flag */ - if (matches[matches[i].idx].idx == i && - matches[matches[i].idx].similarity > + if (match_targets[match_targets[i].idx].idx == i && + match_targets[match_targets[i].idx].similarity > opts.rename_from_rewrite_threshold) { from->status = GIT_DELTA_RENAMED; from->similarity = - (uint32_t)matches[matches[i].idx].similarity; - matches[matches[i].idx].similarity = 0; + (uint32_t)match_targets[match_targets[i].idx].similarity; + match_targets[match_targets[i].idx].similarity = 0; from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; num_rewrites--; } @@ -882,7 +887,8 @@ int git_diff_find_similar( FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES)); cleanup: - git__free(matches); + git__free(match_sources); + git__free(match_targets); for (i = 0; i < cache_size; ++i) { if (cache[i] != NULL) From 596b121ae46554cf48b4e561541e1ab975dbef78 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 10 Jun 2013 14:16:49 -0700 Subject: [PATCH 319/384] fix missing file and bad prototype --- src/array.h | 41 +++++++++++++++++++++++++++++++++++++++++ src/diff_driver.h | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/array.h diff --git a/src/array.h b/src/array.h new file mode 100644 index 000000000..aadd021f1 --- /dev/null +++ b/src/array.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_array_h__ +#define INCLUDE_array_h__ + +#include "util.h" + +#define git_array_t(type) struct { type *ptr; size_t size, asize; } + +#define git_array_init(a) \ + do { (a).size = (a).asize = 0; (a).ptr = NULL; } while (0) + +#define git_array_clear(a) \ + do { git__free((a).ptr); git_array_init(a); } while (0) + +#define git_array_grow(a) do { \ + void *new_array; size_t new_size = \ + ((a).asize >= 256) ? (a).asize + 256 : ((a).asize >= 8) ? (a).asize * 2 : 8; \ + new_array = git__realloc((a).ptr, new_size * sizeof(*(a).ptr)); \ + if (!new_array) { git_array_clear(a); } \ + else { (a).ptr = new_array; (a).asize = new_size; } \ + } while (0) + +#define GITERR_CHECK_ARRAY(a) GITERR_CHECK_ALLOC((a).ptr) + +#define git_array_alloc(a, el) do { \ + if ((a).size >= (a).asize) git_array_grow(a); \ + (el) = (a).ptr ? &(a).ptr[(a).size++] : NULL; \ + } while (0) + +#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL) + +#define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : NULL) + +#define git_array_size(a) (a).size + +#endif diff --git a/src/diff_driver.h b/src/diff_driver.h index b9881a7ed..af9fa073e 100644 --- a/src/diff_driver.h +++ b/src/diff_driver.h @@ -11,7 +11,7 @@ typedef struct git_diff_driver_registry git_diff_driver_registry; -git_diff_driver_registry *git_diff_driver_registry_new(); +git_diff_driver_registry *git_diff_driver_registry_new(void); void git_diff_driver_registry_free(git_diff_driver_registry *); typedef struct git_diff_driver git_diff_driver; From 2f77d8f15d7c71a92f01850025c91dffe3bcafd4 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 10 Jun 2013 14:16:56 -0700 Subject: [PATCH 320/384] Fix some memory leaks --- src/index.c | 2 ++ src/remote.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/index.c b/src/index.c index 25c38b026..fd55616b8 100644 --- a/src/index.c +++ b/src/index.c @@ -2030,6 +2030,8 @@ int git_index_read_tree(git_index *index, const git_tree *tree) error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data); index_entries_free(&entries); + git_vector_free(&entries); + git_vector_sort(&index->entries); return error; diff --git a/src/remote.c b/src/remote.c index 943b72bb7..0e8354a11 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1267,8 +1267,10 @@ static int rename_remote_references( return -1; while ((error = git_reference_next(&ref, iter)) == 0) { - if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) + if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) { + git_reference_free(ref); continue; + } if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) { git_reference_iterator_free(iter); From 3eadfecd325d355d3f8a9631d9c89b7e8eede98b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 10 Jun 2013 15:24:20 -0700 Subject: [PATCH 321/384] start implementing diff driver registry --- src/diff_driver.c | 44 ++++++++++++++++++++++++++++++++++++++++---- src/repository.c | 2 ++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/diff_driver.c b/src/diff_driver.c index 5438afc67..58a903261 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -16,6 +16,8 @@ #include "map.h" #include "buf_text.h" +GIT__USE_STRMAP; + typedef enum { DIFF_DRIVER_AUTO = 0, DIFF_DRIVER_FALSE = 1, @@ -33,7 +35,7 @@ enum { struct git_diff_driver { git_diff_driver_t type; git_strarray fn_patterns; - int binary; + int binary; /* 0 => treat as text, 1 => treat as binary, -1 => auto */ }; struct git_diff_driver_registry { @@ -49,17 +51,45 @@ static git_diff_driver global_drivers[3] = { git_diff_driver_registry *git_diff_driver_registry_new() { - return git__calloc(1, sizeof(git_diff_driver_registry)); + git_diff_driver_registry *reg = + git__calloc(1, sizeof(git_diff_driver_registry)); + if (!reg) + return NULL; + + if (git_pool_init(®->strings, 1, 0) < 0 || + (reg->drivers = git_strmap_alloc()) == NULL) + { + git_diff_driver_registry_free(reg); + return NULL; + } + + return reg; } void git_diff_driver_registry_free(git_diff_driver_registry *reg) { + if (!reg) + return; + + git_strmap_free(reg->drivers); + git_pool_clear(®->strings); git__free(reg); } +static int git_diff_driver_load( + git_diff_driver **out, git_repository *repo, const char *name) +{ + GIT_UNUSED(out); + GIT_UNUSED(repo); + GIT_UNUSED(name); + + return GIT_ENOTFOUND; +} + int git_diff_driver_lookup( git_diff_driver **out, git_repository *repo, const char *path) { + int error = 0; const char *value; assert(out); @@ -67,8 +97,8 @@ int git_diff_driver_lookup( if (!repo || !path || !strlen(path)) goto use_auto; - if (git_attr_get(&value, repo, 0, path, "diff") < 0) - return -1; + if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0) + return error; if (GIT_ATTR_FALSE(value)) { *out = &global_drivers[DIFF_DRIVER_FALSE]; @@ -81,6 +111,12 @@ int git_diff_driver_lookup( } /* otherwise look for driver information in config and build driver */ + if ((error = git_diff_driver_load(out, repo, value)) < 0) { + if (error != GIT_ENOTFOUND) + return error; + else + giterr_clear(); + } use_auto: *out = &global_drivers[DIFF_DRIVER_AUTO]; diff --git a/src/repository.c b/src/repository.c index 4514fee23..e4451499c 100644 --- a/src/repository.c +++ b/src/repository.c @@ -111,7 +111,9 @@ void git_repository_free(git_repository *repo) git_cache_free(&repo->objects); git_submodule_config_free(repo); + git_diff_driver_registry_free(repo->diff_drivers); + repo->diff_drivers = NULL; git__free(repo->path_repository); git__free(repo->workdir); From 24ec69998db55478af24b471b9000bd56113abd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 11 Jun 2013 10:48:00 +0200 Subject: [PATCH 322/384] signature: extend trimming to more whitespace There are all sorts of misconfiguration in the wild. We already rely on the signature constructor to trim SP. Extend the logic to use `isspace` to decide whether a character should be trimmed. --- src/signature.c | 4 ++-- tests-clar/commit/signature.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/signature.c b/src/signature.c index cd6167fb4..e338f0802 100644 --- a/src/signature.c +++ b/src/signature.c @@ -35,11 +35,11 @@ static bool contains_angle_brackets(const char *input) static char *extract_trimmed(const char *ptr, size_t len) { - while (len && ptr[0] == ' ') { + while (len && git__isspace(ptr[0])) { ptr++; len--; } - while (len && ptr[len - 1] == ' ') { + while (len && git__isspace(ptr[len - 1])) { len--; } diff --git a/tests-clar/commit/signature.c b/tests-clar/commit/signature.c index aef72a80d..e9dcfab41 100644 --- a/tests-clar/commit/signature.c +++ b/tests-clar/commit/signature.c @@ -31,6 +31,8 @@ static void assert_name_and_email( void test_commit_signature__leading_and_trailing_spaces_are_trimmed(void) { assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " nulltoken ", " emeric.fermas@gmail.com "); + assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " nulltoken ", " emeric.fermas@gmail.com \n"); + assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " \t nulltoken \n", " \n emeric.fermas@gmail.com \n"); } void test_commit_signature__angle_brackets_in_names_are_not_supported(void) From 5dc98298a14a9adae3cf8b21fb01f682791c29c7 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 11 Jun 2013 11:22:22 -0700 Subject: [PATCH 323/384] Implement regex pattern diff driver This implements the loading of regular expression pattern lists for diff drivers that search for function context in that way. This also changes the way that diff drivers update options and interface with xdiff APIs to make them a little more flexible. --- docs/diff-internals.md | 1 - include/git2/diff.h | 3 + src/diff_driver.c | 286 ++++++++++++++++++++++++++++++++------- src/diff_driver.h | 21 ++- src/diff_file.c | 51 ++++--- src/diff_file.h | 3 +- src/diff_patch.c | 5 +- src/diff_xdiff.c | 12 +- tests-clar/diff/patch.c | 2 +- tests-clar/diff/rename.c | 2 +- 10 files changed, 299 insertions(+), 87 deletions(-) diff --git a/docs/diff-internals.md b/docs/diff-internals.md index 1983b7939..53e71f5b5 100644 --- a/docs/diff-internals.md +++ b/docs/diff-internals.md @@ -86,4 +86,3 @@ Internal Objects for hunk headers ** At some point, the logic for getting a filtered version of file content or calculating the OID of a file may be moved into the driver. - diff --git a/include/git2/diff.h b/include/git2/diff.h index d26456cb0..40e65b1e4 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -148,6 +148,9 @@ typedef enum { * Of course, ignore rules are still checked for the directory itself. */ GIT_DIFF_FAST_UNTRACKED_DIRS = (1 << 19), + + /** Treat all files as binary, disabling text diffs */ + GIT_DIFF_FORCE_BINARY = (1 << 20), } git_diff_option_t; /** diff --git a/src/diff_driver.c b/src/diff_driver.c index 58a903261..9d2508024 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -12,17 +12,17 @@ #include "diff_patch.h" #include "diff_driver.h" #include "strmap.h" -#include "pool.h" #include "map.h" #include "buf_text.h" +#include "repository.h" GIT__USE_STRMAP; typedef enum { DIFF_DRIVER_AUTO = 0, - DIFF_DRIVER_FALSE = 1, - DIFF_DRIVER_TRUE = 2, - DIFF_DRIVER_NAMED = 3, + DIFF_DRIVER_BINARY = 1, + DIFF_DRIVER_TEXT = 2, + DIFF_DRIVER_PATTERNLIST = 3, } git_diff_driver_t; enum { @@ -34,19 +34,22 @@ enum { /* data for finding function context for a given file type */ struct git_diff_driver { git_diff_driver_t type; - git_strarray fn_patterns; - int binary; /* 0 => treat as text, 1 => treat as binary, -1 => auto */ + uint32_t binary_flags; + uint32_t other_flags; + git_array_t(regex_t) fn_patterns; + regex_t word_pattern; }; struct git_diff_driver_registry { git_strmap *drivers; - git_pool strings; }; +#define FORCE_DIFFABLE (GIT_DIFF_FORCE_TEXT | GIT_DIFF_FORCE_BINARY) + static git_diff_driver global_drivers[3] = { - { DIFF_DRIVER_AUTO, { NULL, 0 }, -1 }, - { DIFF_DRIVER_FALSE, { NULL, 0 }, 1 }, - { DIFF_DRIVER_TRUE, { NULL, 0 }, 0 }, + { DIFF_DRIVER_AUTO, 0, 0, }, + { DIFF_DRIVER_BINARY, GIT_DIFF_FORCE_BINARY, 0 }, + { DIFF_DRIVER_TEXT, GIT_DIFF_FORCE_TEXT, 0 }, }; git_diff_driver_registry *git_diff_driver_registry_new() @@ -56,9 +59,7 @@ git_diff_driver_registry *git_diff_driver_registry_new() if (!reg) return NULL; - if (git_pool_init(®->strings, 1, 0) < 0 || - (reg->drivers = git_strmap_alloc()) == NULL) - { + if ((reg->drivers = git_strmap_alloc()) == NULL) { git_diff_driver_registry_free(reg); return NULL; } @@ -68,22 +69,165 @@ git_diff_driver_registry *git_diff_driver_registry_new() void git_diff_driver_registry_free(git_diff_driver_registry *reg) { + git_diff_driver *drv; + if (!reg) return; + git_strmap_foreach_value(reg->drivers, drv, git_diff_driver_free(drv)); git_strmap_free(reg->drivers); - git_pool_clear(®->strings); git__free(reg); } -static int git_diff_driver_load( - git_diff_driver **out, git_repository *repo, const char *name) +static int diff_driver_add_funcname( + git_diff_driver *drv, const char *name, int regex_flags) { - GIT_UNUSED(out); - GIT_UNUSED(repo); - GIT_UNUSED(name); + int error; + regex_t re, *re_ptr; - return GIT_ENOTFOUND; + if ((error = regcomp(&re, name, regex_flags)) != 0) { + /* TODO: warning about bad regex instead of failure */ + error = giterr_set_regex(&re, error); + regfree(&re); + return error; + } + + git_array_alloc(drv->fn_patterns, re_ptr); + GITERR_CHECK_ALLOC(re_ptr); + + memcpy(re_ptr, &re, sizeof(re)); + return 0; +} + +static int diff_driver_xfuncname(const git_config_entry *entry, void *payload) +{ + return diff_driver_add_funcname(payload, entry->value, REG_EXTENDED); +} + +static int diff_driver_funcname(const git_config_entry *entry, void *payload) +{ + return diff_driver_add_funcname(payload, entry->value, 0); +} + +static git_diff_driver_registry *git_repository_driver_registry( + git_repository *repo) +{ + if (!repo->diff_drivers) { + git_diff_driver_registry *reg = git_diff_driver_registry_new(); + reg = git__compare_and_swap(&repo->diff_drivers, NULL, reg); + + if (reg != NULL) /* if we race, free losing allocation */ + git_diff_driver_registry_free(reg); + } + + if (!repo->diff_drivers) + giterr_set(GITERR_REPOSITORY, "Unable to create diff driver registry"); + + return repo->diff_drivers; +} + +static int git_diff_driver_load( + git_diff_driver **out, git_repository *repo, const char *driver_name) +{ + int error = 0, bval; + git_diff_driver_registry *reg; + git_diff_driver *drv; + git_config *cfg; + git_buf name = GIT_BUF_INIT; + const char *val; + + reg = git_repository_driver_registry(repo); + if (!reg) + return -1; + else { + khiter_t pos = git_strmap_lookup_index(reg->drivers, driver_name); + if (git_strmap_valid_index(reg->drivers, pos)) { + *out = git_strmap_value_at(reg->drivers, pos); + return 0; + } + } + + /* if you can't read config for repo, just use default driver */ + if (git_repository_config__weakptr(&cfg, repo) < 0) { + giterr_clear(); + return GIT_ENOTFOUND; + } + + drv = git__calloc(1, sizeof(git_diff_driver)); + GITERR_CHECK_ALLOC(drv); + drv->type = DIFF_DRIVER_AUTO; + + if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) + goto fail; + if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { + if (error != GIT_ENOTFOUND) + goto fail; + /* diff..binary unspecified, so just continue */ + giterr_clear(); + } else if (git_config_parse_bool(&bval, val) < 0) { + /* TODO: warn that diff..binary has invalid value */ + giterr_clear(); + } else if (bval) { + /* if diff..binary is true, just return the binary driver */ + git__free(drv); + *out = &global_drivers[DIFF_DRIVER_BINARY]; + return 0; + } else { + /* if diff..binary is false, force binary checks off */ + /* but still may have custom function context patterns, etc. */ + drv->binary_flags = GIT_DIFF_FORCE_TEXT; + } + + /* TODO: warn if diff..command or diff..textconv are set */ + + if ((error = git_buf_printf(&name, "diff.%s.xfuncname", driver_name)) < 0) + goto fail; + if ((error = git_config_get_multivar( + cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) { + if (error != GIT_ENOTFOUND) + goto fail; + /* no diff..xfuncname values, so just continue */ + giterr_clear(); + } + + if ((error = git_buf_printf(&name, "diff.%s.funcname", driver_name)) < 0) + goto fail; + if ((error = git_config_get_multivar( + cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) { + if (error != GIT_ENOTFOUND) + goto fail; + /* no diff..funcname values, so just continue */ + giterr_clear(); + } + + /* if we found any patterns, set driver type to use correct callback */ + if (git_array_size(drv->fn_patterns) > 0) + drv->type = DIFF_DRIVER_PATTERNLIST; + + if ((error = git_buf_printf(&name, "diff.%s.wordregex", driver_name)) < 0) + goto fail; + if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { + if (error != GIT_ENOTFOUND) + goto fail; + /* no diff..wordregex, so just continue */ + giterr_clear(); + } else if ((error = regcomp(&drv->word_pattern, val, REG_EXTENDED)) != 0) { + /* TODO: warning about bad regex instead of failure */ + error = giterr_set_regex(&drv->word_pattern, error); + goto fail; + } + + /* TODO: look up diff..algorithm to turn on minimal / patience + * diff in drv->other_flags + */ + + *out = drv; + return 0; + +fail: + git_diff_driver_free(drv); + *out = &global_drivers[DIFF_DRIVER_AUTO]; + return error; } int git_diff_driver_lookup( @@ -101,12 +245,12 @@ int git_diff_driver_lookup( return error; if (GIT_ATTR_FALSE(value)) { - *out = &global_drivers[DIFF_DRIVER_FALSE]; + *out = &global_drivers[DIFF_DRIVER_BINARY]; return 0; } else if (GIT_ATTR_TRUE(value)) { - *out = &global_drivers[DIFF_DRIVER_TRUE]; + *out = &global_drivers[DIFF_DRIVER_TEXT]; return 0; } @@ -125,13 +269,27 @@ use_auto: void git_diff_driver_free(git_diff_driver *driver) { - GIT_UNUSED(driver); - /* do nothing for now */ + size_t i; + + if (!driver) + return; + + for (i = 0; i > git_array_size(driver->fn_patterns); ++i) + regfree(git_array_get(driver->fn_patterns, i)); + git_array_clear(driver->fn_patterns); + + regfree(&driver->word_pattern); + + git__free(driver); } -int git_diff_driver_is_binary(git_diff_driver *driver) +void git_diff_driver_update_options( + uint32_t *option_flags, git_diff_driver *driver) { - return driver ? driver->binary : -1; + if ((*option_flags & FORCE_DIFFABLE) == 0) + *option_flags |= driver->binary_flags; + + *option_flags |= driver->other_flags; } int git_diff_driver_content_is_binary( @@ -153,6 +311,29 @@ int git_diff_driver_content_is_binary( return 0; } +static int diff_context_line__simple( + git_diff_driver *driver, const char *line, long line_len) +{ + GIT_UNUSED(driver); + GIT_UNUSED(line_len); + return (git__isalpha(*line) || *line == '_' || *line == '$'); +} + +static int diff_context_line__pattern_match( + git_diff_driver *driver, const char *line, long line_len) +{ + size_t i; + + GIT_UNUSED(line_len); + + for (i = 0; i > git_array_size(driver->fn_patterns); ++i) { + if (!regexec(git_array_get(driver->fn_patterns, i), line, 0, NULL, 0)) + return true; + } + + return false; +} + static long diff_context_find( const char *line, long line_len, @@ -160,37 +341,46 @@ static long diff_context_find( long out_size, void *payload) { - git_diff_driver *driver = payload; - const char *scan; + git_diff_find_context_payload *ctxt = payload; - GIT_UNUSED(driver); + if (git_buf_set(&ctxt->line, line, (size_t)line_len) < 0) + return -1; + git_buf_rtrim(&ctxt->line); - if (line_len > 0 && line[line_len - 1] == '\n') - line_len--; - if (line_len > 0 && line[line_len - 1] == '\r') - line_len--; - if (!line_len) + if (!ctxt->line.size) return -1; - if (!git__isalpha(*line) && *line != '_' && *line != '$') + if (!ctxt->match_line || + !ctxt->match_line(ctxt->driver, ctxt->line.ptr, ctxt->line.size)) return -1; - for (scan = &line[line_len-1]; scan > line && git__isspace(*scan); --scan) - /* search backward for non-space */; - line_len = scan - line; + git_buf_truncate(&ctxt->line, (size_t)out_size); + git_buf_copy_cstr(out, (size_t)out_size, &ctxt->line); - if (line_len >= out_size) - line_len = out_size - 1; - - memcpy(out, line, line_len); - out[line_len] = '\0'; - - return line_len; + return (long)ctxt->line.size; } -git_diff_find_context_fn git_diff_driver_find_content_fn(git_diff_driver *driver) +void git_diff_find_context_init( + git_diff_find_context_fn *findfn_out, + git_diff_find_context_payload *payload_out, + git_diff_driver *driver) { - GIT_UNUSED(driver); - return diff_context_find; + *findfn_out = driver ? diff_context_find : NULL; + + memset(payload_out, 0, sizeof(*payload_out)); + if (driver) { + payload_out->driver = driver; + payload_out->match_line = (driver->type == DIFF_DRIVER_PATTERNLIST) ? + diff_context_line__pattern_match : diff_context_line__simple; + git_buf_init(&payload_out->line, 0); + } +} + +void git_diff_find_context_clear(git_diff_find_context_payload *payload) +{ + if (payload) { + git_buf_free(&payload->line); + payload->driver = NULL; + } } diff --git a/src/diff_driver.h b/src/diff_driver.h index af9fa073e..3db7df000 100644 --- a/src/diff_driver.h +++ b/src/diff_driver.h @@ -8,6 +8,7 @@ #define INCLUDE_diff_driver_h__ #include "common.h" +#include "buffer.h" typedef struct git_diff_driver_registry git_diff_driver_registry; @@ -19,8 +20,8 @@ typedef struct git_diff_driver git_diff_driver; int git_diff_driver_lookup(git_diff_driver **, git_repository *, const char *); void git_diff_driver_free(git_diff_driver *); -/* returns -1 meaning "unknown", 0 meaning not binary, 1 meaning binary */ -int git_diff_driver_is_binary(git_diff_driver *); +/* diff option flags to force off and on for this driver */ +void git_diff_driver_update_options(uint32_t *option_flags, git_diff_driver *); /* returns -1 meaning "unknown", 0 meaning not binary, 1 meaning binary */ int git_diff_driver_content_is_binary( @@ -29,6 +30,20 @@ int git_diff_driver_content_is_binary( typedef long (*git_diff_find_context_fn)( const char *, long, char *, long, void *); -git_diff_find_context_fn git_diff_driver_find_content_fn(git_diff_driver *); +typedef int (*git_diff_find_context_line)( + git_diff_driver *, const char *, long); + +typedef struct { + git_diff_driver *driver; + git_diff_find_context_line match_line; + git_buf line; +} git_diff_find_context_payload; + +void git_diff_find_context_init( + git_diff_find_context_fn *findfn_out, + git_diff_find_context_payload *payload_out, + git_diff_driver *driver); + +void git_diff_find_context_clear(git_diff_find_context_payload *); #endif diff --git a/src/diff_file.c b/src/diff_file.c index e4f8ca1e8..5bdb9e4bf 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -19,14 +19,9 @@ static bool diff_file_content_binary_by_size(git_diff_file_content *fc) { /* if we have diff opts, check max_size vs file size */ if ((fc->file.flags & DIFF_FLAGS_KNOWN_BINARY) == 0 && - fc->opts && fc->opts->max_size >= 0) - { - git_off_t threshold = DIFF_MAX_FILESIZE; - if (fc->opts->max_size > 0) - threshold = fc->opts->max_size; - if (fc->file.size > threshold) - fc->file.flags |= GIT_DIFF_FLAG_BINARY; - } + fc->opts_max_size > 0 && + fc->file.size > fc->opts_max_size) + fc->file.flags |= GIT_DIFF_FLAG_BINARY; return ((fc->file.flags & GIT_DIFF_FLAG_BINARY) != 0); } @@ -44,9 +39,14 @@ static void diff_file_content_binary_by_content(git_diff_file_content *fc) } } -static int diff_file_content_init_common(git_diff_file_content *fc) +static int diff_file_content_init_common( + git_diff_file_content *fc, const git_diff_options *opts) { - uint32_t flags = fc->opts ? fc->opts->flags : GIT_DIFF_NORMAL; + fc->opts_flags = opts ? opts->flags : GIT_DIFF_NORMAL; + + if (opts && opts->max_size >= 0) + fc->opts_max_size = opts->max_size ? + opts->max_size : DIFF_MAX_FILESIZE; if (!fc->driver) { if (git_diff_driver_lookup(&fc->driver, fc->repo, "") < 0) @@ -54,20 +54,22 @@ static int diff_file_content_init_common(git_diff_file_content *fc) fc->src = GIT_ITERATOR_TYPE_TREE; } + /* give driver a chance to modify options */ + git_diff_driver_update_options(&fc->opts_flags, fc->driver); + /* make sure file is conceivable mmap-able */ if ((git_off_t)((size_t)fc->file.size) != fc->file.size) fc->file.flags |= GIT_DIFF_FLAG_BINARY; - - /* check if user is forcing is to text diff the file */ - else if (flags & GIT_DIFF_FORCE_TEXT) + /* check if user is forcing text diff the file */ + else if (fc->opts_flags & GIT_DIFF_FORCE_TEXT) { + fc->file.flags &= ~GIT_DIFF_FLAG_BINARY; fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; - - /* otherwise see if diff driver forces a behavior */ - else switch (git_diff_driver_is_binary(fc->driver)) { - case 0: fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; break; - case 1: fc->file.flags |= GIT_DIFF_FLAG_BINARY; break; - default: break; - } + } + /* check if user is forcing binary diff the file */ + else if (fc->opts_flags & GIT_DIFF_FORCE_BINARY) { + fc->file.flags &= ~GIT_DIFF_FLAG_NOT_BINARY; + fc->file.flags |= GIT_DIFF_FLAG_BINARY; + } diff_file_content_binary_by_size(fc); @@ -95,7 +97,6 @@ int diff_file_content_init_from_diff( memset(fc, 0, sizeof(*fc)); fc->repo = diff->repo; - fc->opts = &diff->opts; fc->src = use_old ? diff->old_src : diff->new_src; memcpy(&fc->file, file, sizeof(fc->file)); @@ -123,7 +124,7 @@ int diff_file_content_init_from_diff( if (!has_data) fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; - return diff_file_content_init_common(fc); + return diff_file_content_init_common(fc, &diff->opts); } int diff_file_content_init_from_blob( @@ -134,7 +135,6 @@ int diff_file_content_init_from_blob( { memset(fc, 0, sizeof(*fc)); fc->repo = repo; - fc->opts = opts; fc->blob = blob; if (!blob) { @@ -149,7 +149,7 @@ int diff_file_content_init_from_blob( fc->map.data = (char *)git_blob_rawcontent(blob); } - return diff_file_content_init_common(fc); + return diff_file_content_init_common(fc, opts); } int diff_file_content_init_from_raw( @@ -161,7 +161,6 @@ int diff_file_content_init_from_raw( { memset(fc, 0, sizeof(*fc)); fc->repo = repo; - fc->opts = opts; if (!buf) { fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; @@ -175,7 +174,7 @@ int diff_file_content_init_from_raw( fc->map.data = (char *)buf; } - return diff_file_content_init_common(fc); + return diff_file_content_init_common(fc, opts); } static int diff_file_content_commit_to_str( diff --git a/src/diff_file.h b/src/diff_file.h index 51c6878a9..ab7b1dc1f 100644 --- a/src/diff_file.h +++ b/src/diff_file.h @@ -15,9 +15,10 @@ /* expanded information for one side of a delta */ typedef struct { git_repository *repo; - const git_diff_options *opts; git_diff_file file; git_diff_driver *driver; + uint32_t opts_flags; + git_off_t opts_max_size; git_iterator_type_t src; const git_blob *blob; git_map map; diff --git a/src/diff_patch.c b/src/diff_patch.c index d7eb69db6..fe22d678c 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -96,8 +96,7 @@ static int diff_patch_load(git_diff_patch *patch, git_diff_output *output) /* if no hunk and data callbacks and user doesn't care if data looks * binary, then there is no need to actually load the data */ - if (patch->ofile.opts && - (patch->ofile.opts->flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0 && + if ((patch->ofile.opts_flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0 && output && !output->hunk_cb && !output->data_cb) return 0; @@ -718,6 +717,6 @@ static void diff_output_init( static void diff_output_to_patch(git_diff_output *out, git_diff_patch *patch) { diff_output_init( - out, patch->ofile.opts, + out, NULL, diff_patch_file_cb, diff_patch_hunk_cb, diff_patch_line_cb, patch); } diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index 1d1c2d54c..91c56f727 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -109,6 +109,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch) { git_xdiff_output *xo = (git_xdiff_output *)output; git_xdiff_info info; + git_diff_find_context_payload findctxt; mmfile_t old_xdiff_data, new_xdiff_data; memset(&info, 0, sizeof(info)); @@ -117,15 +118,18 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch) xo->callback.priv = &info; - xo->config.find_func_priv = patch->ofile.driver; - xo->config.find_func = patch->ofile.driver ? - git_diff_driver_find_content_fn(patch->ofile.driver) : NULL; + git_diff_find_context_init( + &xo->config.find_func, &findctxt, patch->ofile.driver); + xo->config.find_func_priv = &findctxt; if (xo->config.find_func != NULL) xo->config.flags |= XDL_EMIT_FUNCNAMES; else xo->config.flags &= ~XDL_EMIT_FUNCNAMES; + /* TODO: check ofile.opts_flags to see if driver-specific per-file + * updates are needed to xo->params.flags + */ old_xdiff_data.ptr = patch->ofile.map.data; old_xdiff_data.size = patch->ofile.map.len; @@ -135,6 +139,8 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch) xdl_diff(&old_xdiff_data, &new_xdiff_data, &xo->params, &xo->config, &xo->callback); + git_diff_find_context_clear(&findctxt); + return xo->output.error; } diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c index 6390957c9..3f14a0de7 100644 --- a/tests-clar/diff/patch.c +++ b/tests-clar/diff/patch.c @@ -543,7 +543,7 @@ void test_diff_patch__line_counts_with_eofnl(void) "index 378a7d9..3d0154e 100644\n" "--- a/songof7cities.txt\n" "+++ b/songof7cities.txt\n" - "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood\n" + "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n" " \n" " To the sound of trumpets shall their seed restore my Cities\n" " Wealthy and well-weaponed, that once more may I behold\n" diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index a9f1b4e20..ca3f50676 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -558,7 +558,7 @@ void test_diff_rename__patch(void) git_diff_patch *patch; const git_diff_delta *delta; char *text; - const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@ She sends'em abroad on her own affairs\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n"; + const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@ She sends'em abroad on her own affairs,\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n"; old_tree = resolve_commit_oid_to_tree(g_repo, sha0); new_tree = resolve_commit_oid_to_tree(g_repo, sha1); From 76b893b65336722fdee27e1dc8a82eab7ef065ea Mon Sep 17 00:00:00 2001 From: Sven Strickroth Date: Tue, 11 Jun 2013 21:33:18 +0200 Subject: [PATCH 324/384] Add high(est) config level for application specific config files Some tools use an extra level to maintain an application specific config files on top of the normal ones. Revision 16adc9fade52b49e2bc13cb52407cc0025a93c8b broke this. Signed-off-by: Sven Strickroth --- include/git2/config.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/git2/config.h b/include/git2/config.h index 518dcaf16..59b4307be 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -44,6 +44,10 @@ typedef enum { */ GIT_CONFIG_LEVEL_LOCAL = 4, + /** Application specific configuration file; freely defined by applications + */ + GIT_CONFIG_LEVEL_APP = 5, + /** Represents the highest level available config file (i.e. the most * specific config file available that actually is loaded) */ From 42e6cf7860fba665357a7b1b6a8c5d3f5dc0d634 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 11 Jun 2013 17:45:14 -0700 Subject: [PATCH 325/384] Add diff drivers tests (and fix bugs) This adds real tests for user-configured diff drivers and in the process found a bunch of bugs. --- src/diff_driver.c | 65 ++++++++++++-------- tests-clar/diff/drivers.c | 126 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 25 deletions(-) create mode 100644 tests-clar/diff/drivers.c diff --git a/src/diff_driver.c b/src/diff_driver.c index 9d2508024..9c109e7d7 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -38,6 +38,7 @@ struct git_diff_driver { uint32_t other_flags; git_array_t(regex_t) fn_patterns; regex_t word_pattern; + char name[GIT_FLEX_ARRAY]; }; struct git_diff_driver_registry { @@ -132,15 +133,18 @@ static int git_diff_driver_load( int error = 0, bval; git_diff_driver_registry *reg; git_diff_driver *drv; + size_t namelen = strlen(driver_name); + khiter_t pos; git_config *cfg; git_buf name = GIT_BUF_INIT; const char *val; + bool found_driver = false; reg = git_repository_driver_registry(repo); if (!reg) return -1; else { - khiter_t pos = git_strmap_lookup_index(reg->drivers, driver_name); + pos = git_strmap_lookup_index(reg->drivers, driver_name); if (git_strmap_valid_index(reg->drivers, pos)) { *out = git_strmap_value_at(reg->drivers, pos); return 0; @@ -153,9 +157,10 @@ static int git_diff_driver_load( return GIT_ENOTFOUND; } - drv = git__calloc(1, sizeof(git_diff_driver)); + drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1); GITERR_CHECK_ALLOC(drv); drv->type = DIFF_DRIVER_AUTO; + memcpy(drv->name, driver_name, namelen); if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) goto fail; @@ -176,51 +181,62 @@ static int git_diff_driver_load( /* if diff..binary is false, force binary checks off */ /* but still may have custom function context patterns, etc. */ drv->binary_flags = GIT_DIFF_FORCE_TEXT; + found_driver = true; } /* TODO: warn if diff..command or diff..textconv are set */ - if ((error = git_buf_printf(&name, "diff.%s.xfuncname", driver_name)) < 0) - goto fail; + git_buf_truncate(&name, namelen + strlen("diff..")); + git_buf_put(&name, "xfuncname", strlen("xfuncname")); if ((error = git_config_get_multivar( cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) { if (error != GIT_ENOTFOUND) goto fail; - /* no diff..xfuncname values, so just continue */ - giterr_clear(); + giterr_clear(); /* no diff..xfuncname, so just continue */ } - if ((error = git_buf_printf(&name, "diff.%s.funcname", driver_name)) < 0) - goto fail; + git_buf_truncate(&name, namelen + strlen("diff..")); + git_buf_put(&name, "funcname", strlen("funcname")); if ((error = git_config_get_multivar( cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) { if (error != GIT_ENOTFOUND) goto fail; - /* no diff..funcname values, so just continue */ - giterr_clear(); + giterr_clear(); /* no diff..funcname, so just continue */ } /* if we found any patterns, set driver type to use correct callback */ - if (git_array_size(drv->fn_patterns) > 0) + if (git_array_size(drv->fn_patterns) > 0) { drv->type = DIFF_DRIVER_PATTERNLIST; + found_driver = true; + } - if ((error = git_buf_printf(&name, "diff.%s.wordregex", driver_name)) < 0) - goto fail; + git_buf_truncate(&name, namelen + strlen("diff..")); + git_buf_put(&name, "wordregex", strlen("wordregex")); if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { if (error != GIT_ENOTFOUND) goto fail; - /* no diff..wordregex, so just continue */ - giterr_clear(); + giterr_clear(); /* no diff..wordregex, so just continue */ } else if ((error = regcomp(&drv->word_pattern, val, REG_EXTENDED)) != 0) { /* TODO: warning about bad regex instead of failure */ error = giterr_set_regex(&drv->word_pattern, error); goto fail; + } else { + found_driver = true; } /* TODO: look up diff..algorithm to turn on minimal / patience * diff in drv->other_flags */ + /* if no driver config found, fall back on AUTO driver */ + if (!found_driver) + goto fail; + + /* store driver in registry */ + git_strmap_insert(reg->drivers, drv->name, drv, error); + if (error < 0) + goto fail; + *out = drv; return 0; @@ -244,18 +260,15 @@ int git_diff_driver_lookup( if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0) return error; - if (GIT_ATTR_FALSE(value)) { + if (GIT_ATTR_UNSPECIFIED(value)) + /* just use the auto value */; + else if (GIT_ATTR_FALSE(value)) *out = &global_drivers[DIFF_DRIVER_BINARY]; - return 0; - } - - else if (GIT_ATTR_TRUE(value)) { + else if (GIT_ATTR_TRUE(value)) *out = &global_drivers[DIFF_DRIVER_TEXT]; - return 0; - } /* otherwise look for driver information in config and build driver */ - if ((error = git_diff_driver_load(out, repo, value)) < 0) { + else if ((error = git_diff_driver_load(out, repo, value)) < 0) { if (error != GIT_ENOTFOUND) return error; else @@ -263,7 +276,9 @@ int git_diff_driver_lookup( } use_auto: - *out = &global_drivers[DIFF_DRIVER_AUTO]; + if (!*out) + *out = &global_drivers[DIFF_DRIVER_AUTO]; + return 0; } @@ -326,7 +341,7 @@ static int diff_context_line__pattern_match( GIT_UNUSED(line_len); - for (i = 0; i > git_array_size(driver->fn_patterns); ++i) { + for (i = 0; i < git_array_size(driver->fn_patterns); ++i) { if (!regexec(git_array_get(driver->fn_patterns, i), line, 0, NULL, 0)) return true; } diff --git a/tests-clar/diff/drivers.c b/tests-clar/diff/drivers.c new file mode 100644 index 000000000..8f7b1f21c --- /dev/null +++ b/tests-clar/diff/drivers.c @@ -0,0 +1,126 @@ +#include "clar_libgit2.h" +#include "diff_helpers.h" +#include "repository.h" +#include "diff_driver.h" + +static git_repository *g_repo = NULL; +static git_config *g_cfg = NULL; + +void test_diff_drivers__initialize(void) +{ +} + +void test_diff_drivers__cleanup(void) +{ + git_config_free(g_cfg); + g_cfg = NULL; + + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +void test_diff_drivers__patterns(void) +{ + const char *one_sha = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13"; + git_tree *one; + git_diff_list *diff; + git_diff_patch *patch; + char *text; + const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n"; + const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n"; + const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n"; + + g_repo = cl_git_sandbox_init("renames"); + + one = resolve_commit_oid_to_tree(g_repo, one_sha); + + /* no diff */ + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); + cl_assert_equal_i(0, (int)git_diff_num_deltas(diff)); + git_diff_list_free(diff); + + /* default diff */ + + cl_git_append2file("renames/untimely.txt", "\r\nSome new stuff\r\n"); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); + cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); + + cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected0, text); + + git__free(text); + git_diff_patch_free(patch); + git_diff_list_free(diff); + + /* attribute diff set to false */ + + cl_git_rewritefile("renames/.gitattributes", "untimely.txt -diff\n"); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); + cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); + + cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected1, text); + + git__free(text); + git_diff_patch_free(patch); + git_diff_list_free(diff); + + /* attribute diff set to unconfigured value (should use default) */ + + cl_git_rewritefile("renames/.gitattributes", "untimely.txt diff=kipling0\n"); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); + cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); + + cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected0, text); + + git__free(text); + git_diff_patch_free(patch); + git_diff_list_free(diff); + + /* let's define that driver */ + + cl_git_pass(git_repository_config(&g_cfg, g_repo)); + cl_git_pass(git_config_set_bool(g_cfg, "diff.kipling0.binary", 1)); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); + cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); + + cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected1, text); + + git__free(text); + git_diff_patch_free(patch); + git_diff_list_free(diff); + + /* let's use a real driver with some regular expressions */ + + git_diff_driver_registry_free(g_repo->diff_drivers); + g_repo->diff_drivers = NULL; + + cl_git_pass(git_repository_config(&g_cfg, g_repo)); + cl_git_pass(git_config_set_bool(g_cfg, "diff.kipling0.binary", 0)); + cl_git_pass(git_config_set_string(g_cfg, "diff.kipling0.xfuncname", "^H")); + + cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); + cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); + + cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0)); + cl_git_pass(git_diff_patch_to_str(&text, patch)); + cl_assert_equal_s(expected2, text); + + git__free(text); + git_diff_patch_free(patch); + git_diff_list_free(diff); + + git_tree_free(one); +} + From 54faddd299ccb6187a9747c1d3ee18d33e5edf7a Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 12 Jun 2013 11:54:11 -0700 Subject: [PATCH 326/384] Fix some diff driver memory leaks --- src/diff_driver.c | 36 ++++++++++++++++++++---------------- tests-clar/diff/drivers.c | 17 ++++++++--------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/diff_driver.c b/src/diff_driver.c index 9c109e7d7..5d3274a11 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -163,10 +163,10 @@ static int git_diff_driver_load( memcpy(drv->name, driver_name, namelen); if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) - goto fail; + goto done; if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { if (error != GIT_ENOTFOUND) - goto fail; + goto done; /* diff..binary unspecified, so just continue */ giterr_clear(); } else if (git_config_parse_bool(&bval, val) < 0) { @@ -174,9 +174,8 @@ static int git_diff_driver_load( giterr_clear(); } else if (bval) { /* if diff..binary is true, just return the binary driver */ - git__free(drv); *out = &global_drivers[DIFF_DRIVER_BINARY]; - return 0; + goto done; } else { /* if diff..binary is false, force binary checks off */ /* but still may have custom function context patterns, etc. */ @@ -191,7 +190,7 @@ static int git_diff_driver_load( if ((error = git_config_get_multivar( cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) { if (error != GIT_ENOTFOUND) - goto fail; + goto done; giterr_clear(); /* no diff..xfuncname, so just continue */ } @@ -200,7 +199,7 @@ static int git_diff_driver_load( if ((error = git_config_get_multivar( cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) { if (error != GIT_ENOTFOUND) - goto fail; + goto done; giterr_clear(); /* no diff..funcname, so just continue */ } @@ -214,12 +213,12 @@ static int git_diff_driver_load( git_buf_put(&name, "wordregex", strlen("wordregex")); if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { if (error != GIT_ENOTFOUND) - goto fail; + goto done; giterr_clear(); /* no diff..wordregex, so just continue */ } else if ((error = regcomp(&drv->word_pattern, val, REG_EXTENDED)) != 0) { /* TODO: warning about bad regex instead of failure */ error = giterr_set_regex(&drv->word_pattern, error); - goto fail; + goto done; } else { found_driver = true; } @@ -228,21 +227,26 @@ static int git_diff_driver_load( * diff in drv->other_flags */ - /* if no driver config found, fall back on AUTO driver */ + /* if no driver config found at all, fall back on AUTO driver */ if (!found_driver) - goto fail; + goto done; /* store driver in registry */ git_strmap_insert(reg->drivers, drv->name, drv, error); if (error < 0) - goto fail; + goto done; *out = drv; - return 0; -fail: - git_diff_driver_free(drv); - *out = &global_drivers[DIFF_DRIVER_AUTO]; +done: + git_buf_free(&name); + + if (!*out) + *out = &global_drivers[DIFF_DRIVER_AUTO]; + + if (drv && drv != *out) + git_diff_driver_free(drv); + return error; } @@ -289,7 +293,7 @@ void git_diff_driver_free(git_diff_driver *driver) if (!driver) return; - for (i = 0; i > git_array_size(driver->fn_patterns); ++i) + for (i = 0; i < git_array_size(driver->fn_patterns); ++i) regfree(git_array_get(driver->fn_patterns, i)); git_array_clear(driver->fn_patterns); diff --git a/tests-clar/diff/drivers.c b/tests-clar/diff/drivers.c index 8f7b1f21c..06ab2ff14 100644 --- a/tests-clar/diff/drivers.c +++ b/tests-clar/diff/drivers.c @@ -4,7 +4,6 @@ #include "diff_driver.h" static git_repository *g_repo = NULL; -static git_config *g_cfg = NULL; void test_diff_drivers__initialize(void) { @@ -12,15 +11,13 @@ void test_diff_drivers__initialize(void) void test_diff_drivers__cleanup(void) { - git_config_free(g_cfg); - g_cfg = NULL; - cl_git_sandbox_cleanup(); g_repo = NULL; } void test_diff_drivers__patterns(void) { + git_config *cfg; const char *one_sha = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13"; git_tree *one; git_diff_list *diff; @@ -87,8 +84,9 @@ void test_diff_drivers__patterns(void) /* let's define that driver */ - cl_git_pass(git_repository_config(&g_cfg, g_repo)); - cl_git_pass(git_config_set_bool(g_cfg, "diff.kipling0.binary", 1)); + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 1)); + git_config_free(cfg); cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); @@ -106,9 +104,10 @@ void test_diff_drivers__patterns(void) git_diff_driver_registry_free(g_repo->diff_drivers); g_repo->diff_drivers = NULL; - cl_git_pass(git_repository_config(&g_cfg, g_repo)); - cl_git_pass(git_config_set_bool(g_cfg, "diff.kipling0.binary", 0)); - cl_git_pass(git_config_set_string(g_cfg, "diff.kipling0.xfuncname", "^H")); + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 0)); + cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H")); + git_config_free(cfg); cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); From f9c824c592d7a23f7cc385c25c95a5d0c5c8687e Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 12 Jun 2013 11:55:27 -0700 Subject: [PATCH 327/384] Add patch from blobs API This adds two new public APIs: git_diff_patch_from_blobs and git_diff_patch_from_blob_and_buffer, plus it refactors the code for git_diff_blobs and git_diff_blob_to_buffer so that they code is almost entirely shared between these APIs, and adds tests for the new APIs. --- include/git2/diff.h | 47 +++++++- src/diff_patch.c | 207 +++++++++++++++++++++++++---------- tests-clar/diff/blob.c | 238 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 434 insertions(+), 58 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 40e65b1e4..8113a56be 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -860,7 +860,7 @@ GIT_EXTERN(size_t) git_diff_patch_num_hunks( * @param total_additions Count of addition lines in output, can be NULL. * @param total_deletions Count of deletion lines in output, can be NULL. * @param patch The git_diff_patch object - * @return Number of lines in hunk or -1 if invalid hunk index + * @return 0 on success, <0 on error */ GIT_EXTERN(int) git_diff_patch_line_stats( size_t *total_context, @@ -1000,6 +1000,26 @@ GIT_EXTERN(int) git_diff_blobs( git_diff_data_cb line_cb, void *payload); +/** + * Directly generate a patch from the difference between two blobs. + * + * This is just like `git_diff_blobs()` except it generates a patch object + * for the difference instead of directly making callbacks. You can use the + * standard `git_diff_patch` accessor functions to read the patch data, and + * you must call `git_diff_patch_free()` on the patch when done. + * + * @param out The generated patch; NULL on error + * @param old_blob Blob for old side of diff, or NULL for empty blob + * @param new_blob Blob for new side of diff, or NULL for empty blob + * @param options Options for diff, or NULL for default options + * @return 0 on success or error code < 0 + */ +GIT_EXTERN(int) git_diff_patch_from_blobs( + git_diff_patch **out, + const git_blob *old_blob, + const git_blob *new_blob, + const git_diff_options *opts); + /** * Directly run a diff between a blob and a buffer. * @@ -1013,7 +1033,7 @@ GIT_EXTERN(int) git_diff_blobs( * the reverse, with GIT_DELTA_REMOVED and blob content removed. * * @param old_blob Blob for old side of diff, or NULL for empty blob - * @param buffer Raw data for new side of diff + * @param buffer Raw data for new side of diff, or NULL for empty * @param buffer_len Length of raw data for new side of diff * @param options Options for diff, or NULL for default options * @param file_cb Callback for "file"; made once if there is a diff; can be NULL @@ -1032,6 +1052,29 @@ GIT_EXTERN(int) git_diff_blob_to_buffer( git_diff_data_cb data_cb, void *payload); +/** + * Directly generate a patch from the difference between a blob and a buffer. + * + * This is just like `git_diff_blob_to_buffer()` except it generates a patch + * object for the difference instead of directly making callbacks. You can + * use the standard `git_diff_patch` accessor functions to read the patch + * data, and you must call `git_diff_patch_free()` on the patch when done. + * + * @param out The generated patch; NULL on error + * @param old_blob Blob for old side of diff, or NULL for empty blob + * @param buffer Raw data for new side of diff, or NULL for empty + * @param buffer_len Length of raw data for new side of diff + * @param options Options for diff, or NULL for default options + * @return 0 on success or error code < 0 + */ +GIT_EXTERN(int) git_diff_patch_from_blob_and_buffer( + git_diff_patch **out, + const git_blob *old_blob, + const char *buf, + size_t buflen, + const git_diff_options *opts); + + GIT_END_DECL /** @} */ diff --git a/src/diff_patch.c b/src/diff_patch.c index fe22d678c..4c0b9a70c 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -265,33 +265,32 @@ int git_diff_foreach( } typedef struct { - git_xdiff_output xo; git_diff_patch patch; git_diff_delta delta; -} diff_single_info; +} diff_patch_with_delta; -static int diff_single_generate(diff_single_info *info) +static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo) { int error = 0; - git_diff_patch *patch = &info->patch; + git_diff_patch *patch = &pd->patch; bool has_old = ((patch->ofile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0); bool has_new = ((patch->nfile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0); - info->delta.status = has_new ? + pd->delta.status = has_new ? (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) : (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED); if (git_oid_equal(&patch->nfile.file.oid, &patch->ofile.file.oid)) - info->delta.status = GIT_DELTA_UNMODIFIED; + pd->delta.status = GIT_DELTA_UNMODIFIED; - patch->delta = &info->delta; + patch->delta = &pd->delta; diff_patch_init_common(patch); - error = diff_patch_file_callback(patch, (git_diff_output *)&info->xo); + error = diff_patch_file_callback(patch, (git_diff_output *)xo); if (!error) - error = diff_patch_generate(patch, (git_diff_output *)&info->xo); + error = diff_patch_generate(patch, (git_diff_output *)xo); if (error == GIT_EUSER) giterr_clear(); /* don't leave error message set invalidly */ @@ -299,6 +298,40 @@ static int diff_single_generate(diff_single_info *info) return error; } +static int diff_patch_from_blobs( + diff_patch_with_delta *pd, + git_xdiff_output *xo, + const git_blob *old_blob, + const git_blob *new_blob, + const git_diff_options *opts) +{ + int error = 0; + git_repository *repo = + new_blob ? git_object_owner((const git_object *)new_blob) : + old_blob ? git_object_owner((const git_object *)old_blob) : NULL; + + GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); + + pd->patch.delta = &pd->delta; + + if (!repo) /* return two NULL items as UNMODIFIED delta */ + return 0; + + if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { + const git_blob *swap = old_blob; + old_blob = new_blob; + new_blob = swap; + } + + if ((error = diff_file_content_init_from_blob( + &pd->patch.ofile, repo, opts, old_blob)) < 0 || + (error = diff_file_content_init_from_blob( + &pd->patch.nfile, repo, opts, new_blob)) < 0) + return error; + + return diff_single_generate(pd, xo); +} + int git_diff_blobs( const git_blob *old_blob, const git_blob *new_blob, @@ -309,37 +342,85 @@ int git_diff_blobs( void *payload) { int error = 0; - diff_single_info info; + diff_patch_with_delta pd; + git_xdiff_output xo; + + memset(&pd, 0, sizeof(pd)); + memset(&xo, 0, sizeof(xo)); + + diff_output_init( + (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload); + git_xdiff_init(&xo, opts); + + error = diff_patch_from_blobs(&pd, &xo, old_blob, new_blob, opts); + + git_diff_patch_free((git_diff_patch *)&pd); + + return error; +} + +int git_diff_patch_from_blobs( + git_diff_patch **out, + const git_blob *old_blob, + const git_blob *new_blob, + const git_diff_options *opts) +{ + int error = 0; + diff_patch_with_delta *pd; + git_xdiff_output xo; + + assert(out); + *out = NULL; + + pd = git__calloc(1, sizeof(*pd)); + GITERR_CHECK_ALLOC(pd); + pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED; + + memset(&xo, 0, sizeof(xo)); + + diff_output_to_patch((git_diff_output *)&xo, &pd->patch); + git_xdiff_init(&xo, opts); + + if (!(error = diff_patch_from_blobs(pd, &xo, old_blob, new_blob, opts))) + *out = (git_diff_patch *)pd; + else + git_diff_patch_free((git_diff_patch *)pd); + + return error; +} + +static int diff_patch_from_blob_and_buffer( + diff_patch_with_delta *pd, + git_xdiff_output *xo, + const git_blob *old_blob, + const char *buf, + size_t buflen, + const git_diff_options *opts) +{ + int error = 0; git_repository *repo = - new_blob ? git_object_owner((const git_object *)new_blob) : old_blob ? git_object_owner((const git_object *)old_blob) : NULL; GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); - if (!repo) /* Hmm, given two NULL blobs, silently do no callbacks? */ + pd->patch.delta = &pd->delta; + + if (!repo && !buf) /* return two NULL items as UNMODIFIED delta */ return 0; if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { - const git_blob *swap = old_blob; - old_blob = new_blob; - new_blob = swap; + if (!(error = diff_file_content_init_from_raw( + &pd->patch.ofile, repo, opts, buf, buflen))) + error = diff_file_content_init_from_blob( + &pd->patch.nfile, repo, opts, old_blob); + } else { + if (!(error = diff_file_content_init_from_blob( + &pd->patch.ofile, repo, opts, old_blob))) + error = diff_file_content_init_from_raw( + &pd->patch.nfile, repo, opts, buf, buflen); } - memset(&info, 0, sizeof(info)); - - diff_output_init((git_diff_output *)&info.xo, - opts, file_cb, hunk_cb, data_cb, payload); - git_xdiff_init(&info.xo, opts); - - if (!(error = diff_file_content_init_from_blob( - &info.patch.ofile, repo, opts, old_blob)) && - !(error = diff_file_content_init_from_blob( - &info.patch.nfile, repo, opts, new_blob))) - error = diff_single_generate(&info); - - git_diff_patch_free(&info.patch); - - return error; + return diff_single_generate(pd, xo); } int git_diff_blob_to_buffer( @@ -353,36 +434,52 @@ int git_diff_blob_to_buffer( void *payload) { int error = 0; - diff_single_info info; - git_repository *repo = - old_blob ? git_object_owner((const git_object *)old_blob) : NULL; + diff_patch_with_delta pd; + git_xdiff_output xo; - GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); + memset(&pd, 0, sizeof(pd)); + memset(&xo, 0, sizeof(xo)); - if (!repo && !buf) /* Hmm, given NULLs, silently do no callbacks? */ - return 0; + diff_output_init( + (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload); + git_xdiff_init(&xo, opts); - memset(&info, 0, sizeof(info)); + error = diff_patch_from_blob_and_buffer( + &pd, &xo, old_blob, buf, buflen, opts); - diff_output_init((git_diff_output *)&info.xo, - opts, file_cb, hunk_cb, data_cb, payload); - git_xdiff_init(&info.xo, opts); + git_diff_patch_free((git_diff_patch *)&pd); - if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { - if (!(error = diff_file_content_init_from_raw( - &info.patch.ofile, repo, opts, buf, buflen))) - error = diff_file_content_init_from_blob( - &info.patch.nfile, repo, opts, old_blob); - } else { - if (!(error = diff_file_content_init_from_blob( - &info.patch.ofile, repo, opts, old_blob))) - error = diff_file_content_init_from_raw( - &info.patch.nfile, repo, opts, buf, buflen); - } + return error; +} - error = diff_single_generate(&info); +int git_diff_patch_from_blob_and_buffer( + git_diff_patch **out, + const git_blob *old_blob, + const char *buf, + size_t buflen, + const git_diff_options *opts) +{ + int error = 0; + diff_patch_with_delta *pd; + git_xdiff_output xo; - git_diff_patch_free(&info.patch); + assert(out); + *out = NULL; + + pd = git__calloc(1, sizeof(*pd)); + GITERR_CHECK_ALLOC(pd); + pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED; + + memset(&xo, 0, sizeof(xo)); + + diff_output_to_patch((git_diff_output *)&xo, &pd->patch); + git_xdiff_init(&xo, opts); + + if (!(error = diff_patch_from_blob_and_buffer( + pd, &xo, old_blob, buf, buflen, opts))) + *out = (git_diff_patch *)pd; + else + git_diff_patch_free((git_diff_patch *)pd); return error; } @@ -599,9 +696,7 @@ static int diff_patch_file_cb( float progress, void *payload) { - GIT_UNUSED(delta); - GIT_UNUSED(progress); - GIT_UNUSED(payload); + GIT_UNUSED(delta); GIT_UNUSED(progress); GIT_UNUSED(payload); return 0; } diff --git a/tests-clar/diff/blob.c b/tests-clar/diff/blob.c index 2ac8dbc51..b12186d98 100644 --- a/tests-clar/diff/blob.c +++ b/tests-clar/diff/blob.c @@ -120,6 +120,93 @@ void test_diff_blob__can_compare_text_blobs(void) git_blob_free(c); } +void test_diff_blob__can_compare_text_blobs_with_patch(void) +{ + git_blob *a, *b, *c; + git_oid a_oid, b_oid, c_oid; + git_diff_patch *p; + size_t tc, ta, td; + + /* 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)); + + /* tests/resources/attr/root_test3 */ + cl_git_pass(git_oid_fromstrn(&c_oid, "c96bbb2c2557a832", 16)); + cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 8)); + + /* Doing the equivalent of a `git diff -U1` on these files */ + + /* diff on tests/resources/attr/root_test1 */ + cl_git_pass(git_diff_patch_from_blobs(&p, a, b, &opts)); + + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); + cl_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0)); + + cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p)); + cl_assert_equal_i(1, (int)tc); + cl_assert_equal_i(5, (int)ta); + cl_assert_equal_i(0, (int)td); + + git_diff_patch_free(p); + + /* diff on tests/resources/attr/root_test2 */ + cl_git_pass(git_diff_patch_from_blobs(&p, b, c, &opts)); + + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); + cl_assert_equal_i(15, git_diff_patch_num_lines_in_hunk(p, 0)); + + cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p)); + cl_assert_equal_i(3, (int)tc); + cl_assert_equal_i(9, (int)ta); + cl_assert_equal_i(3, (int)td); + + git_diff_patch_free(p); + + /* diff on tests/resources/attr/root_test3 */ + cl_git_pass(git_diff_patch_from_blobs(&p, a, c, &opts)); + + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); + cl_assert_equal_i(13, git_diff_patch_num_lines_in_hunk(p, 0)); + + cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p)); + cl_assert_equal_i(0, (int)tc); + cl_assert_equal_i(12, (int)ta); + cl_assert_equal_i(1, (int)td); + + git_diff_patch_free(p); + + /* one more */ + cl_git_pass(git_diff_patch_from_blobs(&p, c, d, &opts)); + + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(2, (int)git_diff_patch_num_hunks(p)); + cl_assert_equal_i(5, git_diff_patch_num_lines_in_hunk(p, 0)); + cl_assert_equal_i(9, git_diff_patch_num_lines_in_hunk(p, 1)); + + cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p)); + cl_assert_equal_i(4, (int)tc); + cl_assert_equal_i(6, (int)ta); + cl_assert_equal_i(4, (int)td); + + git_diff_patch_free(p); + + git_blob_free(a); + git_blob_free(b); + git_blob_free(c); +} + void test_diff_blob__can_compare_against_null_blobs(void) { git_blob *e = NULL; @@ -175,6 +262,66 @@ void test_diff_blob__can_compare_against_null_blobs(void) cl_assert_equal_i(0, expected.lines); } +void test_diff_blob__can_compare_against_null_blobs_with_patch(void) +{ + git_blob *e = NULL; + git_diff_patch *p; + int line; + char origin; + + cl_git_pass(git_diff_patch_from_blobs(&p, d, e, &opts)); + + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); + cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0)); + + for (line = 0; line < git_diff_patch_num_lines_in_hunk(p, 0); ++line) { + cl_git_pass(git_diff_patch_get_line_in_hunk( + &origin, NULL, NULL, NULL, NULL, p, 0, line)); + cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin); + } + + git_diff_patch_free(p); + + opts.flags |= GIT_DIFF_REVERSE; + + cl_git_pass(git_diff_patch_from_blobs(&p, d, e, &opts)); + + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); + cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0)); + + for (line = 0; line < git_diff_patch_num_lines_in_hunk(p, 0); ++line) { + cl_git_pass(git_diff_patch_get_line_in_hunk( + &origin, NULL, NULL, NULL, NULL, p, 0, line)); + cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin); + } + + git_diff_patch_free(p); + + opts.flags ^= GIT_DIFF_REVERSE; + + cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, &opts)); + + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status); + cl_assert((git_diff_patch_delta(p)->flags & GIT_DIFF_FLAG_BINARY) != 0); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); + + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blobs(&p, NULL, alien, &opts)); + + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); + cl_assert((git_diff_patch_delta(p)->flags & GIT_DIFF_FLAG_BINARY) != 0); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); + + git_diff_patch_free(p); +} + static void assert_identical_blobs_comparison(diff_expects *expected) { cl_assert_equal_i(1, expected->files); @@ -206,6 +353,29 @@ void test_diff_blob__can_compare_identical_blobs(void) assert_identical_blobs_comparison(&expected); } +void test_diff_blob__can_compare_identical_blobs_with_patch(void) +{ + git_diff_patch *p; + + cl_git_pass(git_diff_patch_from_blobs(&p, d, d, &opts)); + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, &opts)); + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blobs(&p, alien, alien, &opts)); + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); + git_diff_patch_free(p); +} + static void assert_binary_blobs_comparison(diff_expects *expected) { cl_assert(expected->files_binary > 0); @@ -428,6 +598,74 @@ void test_diff_blob__can_compare_blob_to_buffer(void) git_blob_free(a); } +void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) +{ + git_diff_patch *p; + git_blob *a; + git_oid a_oid; + const char *a_content = "Hello from the root\n"; + const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n"; + size_t tc, ta, td; + + /* 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)); + + /* diff from blob a to content of b */ + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, a, b_content, strlen(b_content), &opts)); + + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); + cl_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0)); + + cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p)); + cl_assert_equal_i(1, (int)tc); + cl_assert_equal_i(5, (int)ta); + cl_assert_equal_i(0, (int)td); + + git_diff_patch_free(p); + + /* diff from blob a to content of a */ + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, a, a_content, strlen(a_content), &opts)); + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); + git_diff_patch_free(p); + + /* diff from NULL blob to content of a */ + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, NULL, a_content, strlen(a_content), &opts)); + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); + cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0)); + git_diff_patch_free(p); + + /* diff from blob a to NULL buffer */ + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, a, NULL, 0, &opts)); + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); + cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0)); + git_diff_patch_free(p); + + /* diff with reverse */ + opts.flags ^= GIT_DIFF_REVERSE; + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, a, NULL, 0, &opts)); + cl_assert(p != NULL); + cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); + cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0)); + git_diff_patch_free(p); + + git_blob_free(a); +} static void assert_one_modified_with_lines(diff_expects *expected, int lines) { From ef3374a8a81786a7b544ed7eded53c95766eb02f Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 12 Jun 2013 13:46:44 -0700 Subject: [PATCH 328/384] Improvements to git_array This changes the size data to uint32_t, fixes the array growth logic to use a simple 1.5x multiplier, and uses a generic inline function for growing the array to make the git_array_alloc API feel more natural (i.e. it returns a pointer to the new item). --- src/array.h | 51 +++++++++++++++++++++++++++++++++++------------ src/diff_driver.c | 2 +- src/diff_patch.c | 4 ++-- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/array.h b/src/array.h index aadd021f1..2d77c71a0 100644 --- a/src/array.h +++ b/src/array.h @@ -9,7 +9,23 @@ #include "util.h" -#define git_array_t(type) struct { type *ptr; size_t size, asize; } +/* + * Use this to declare a typesafe resizable array of items, a la: + * + * git_array_t(int) my_ints = GIT_ARRAY_INIT; + * ... + * int *i = git_array_alloc(my_ints); + * GITERR_CHECK_ALLOC(i); + * ... + * git_array_clear(my_ints); + * + * You may also want to do things like: + * + * typedef git_array_t(my_struct) my_struct_array_t; + */ +#define git_array_t(type) struct { type *ptr; uint32_t size, asize; } + +#define GIT_ARRAY_INIT { NULL, 0, 0 } #define git_array_init(a) \ do { (a).size = (a).asize = 0; (a).ptr = NULL; } while (0) @@ -17,20 +33,29 @@ #define git_array_clear(a) \ do { git__free((a).ptr); git_array_init(a); } while (0) -#define git_array_grow(a) do { \ - void *new_array; size_t new_size = \ - ((a).asize >= 256) ? (a).asize + 256 : ((a).asize >= 8) ? (a).asize * 2 : 8; \ - new_array = git__realloc((a).ptr, new_size * sizeof(*(a).ptr)); \ - if (!new_array) { git_array_clear(a); } \ - else { (a).ptr = new_array; (a).asize = new_size; } \ - } while (0) - #define GITERR_CHECK_ARRAY(a) GITERR_CHECK_ALLOC((a).ptr) -#define git_array_alloc(a, el) do { \ - if ((a).size >= (a).asize) git_array_grow(a); \ - (el) = (a).ptr ? &(a).ptr[(a).size++] : NULL; \ - } while (0) + +typedef git_array_t(void) git_array_generic_t; + +/* use a generic array for growth so this can return the new item */ +GIT_INLINE(void *) git_array_grow(git_array_generic_t *a, size_t item_size) +{ + uint32_t new_size = (a->size < 8) ? 8 : a->asize * 3 / 2; + void *new_array = git__realloc(a->ptr, new_size * item_size); + if (!new_array) { + git_array_clear(*a); + return NULL; + } else { + a->ptr = new_array; a->asize = new_size; a->size++; + return (((char *)a->ptr) + (a->size - 1) * item_size); + } +} + +#define git_array_alloc(a) \ + ((a).size >= (a).asize) ? \ + git_array_grow((git_array_generic_t *)&(a), sizeof(*(a).ptr)) : \ + (a).ptr ? &(a).ptr[(a).size++] : NULL #define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL) diff --git a/src/diff_driver.c b/src/diff_driver.c index 5d3274a11..ae2b7c319 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -93,7 +93,7 @@ static int diff_driver_add_funcname( return error; } - git_array_alloc(drv->fn_patterns, re_ptr); + re_ptr = git_array_alloc(drv->fn_patterns); GITERR_CHECK_ALLOC(re_ptr); memcpy(re_ptr, &re, sizeof(re)); diff --git a/src/diff_patch.c b/src/diff_patch.c index 4c0b9a70c..05dee5ef7 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -712,7 +712,7 @@ static int diff_patch_hunk_cb( GIT_UNUSED(delta); - git_array_alloc(patch->hunks, hunk); + hunk = git_array_alloc(patch->hunks); GITERR_CHECK_ALLOC(hunk); memcpy(&hunk->range, range, sizeof(hunk->range)); @@ -749,7 +749,7 @@ static int diff_patch_line_cb( hunk = git_array_last(patch->hunks); GITERR_CHECK_ALLOC(hunk); - git_array_alloc(patch->lines, line); + line = git_array_alloc(patch->lines); GITERR_CHECK_ALLOC(line); line->ptr = content; From 360f42f4b3f5de31270416220bd799b951202b2d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 12 Jun 2013 14:18:09 -0700 Subject: [PATCH 329/384] Fix diff header naming issues This makes the git_diff_patch definition private to diff_patch.c and fixes a number of other header file naming inconsistencies to use `git_` prefixes on functions and structures that are shared between files. --- src/diff_file.c | 14 ++--- src/diff_file.h | 12 ++--- src/diff_patch.c | 133 ++++++++++++++++++++++++++++++++++++++++++----- src/diff_patch.h | 49 ++++------------- src/diff_print.c | 22 ++------ src/diff_xdiff.c | 19 ++++--- 6 files changed, 154 insertions(+), 95 deletions(-) diff --git a/src/diff_file.c b/src/diff_file.c index 5bdb9e4bf..4fd1177ae 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -85,7 +85,7 @@ static int diff_file_content_init_common( return 0; } -int diff_file_content_init_from_diff( +int git_diff_file_content__init_from_diff( git_diff_file_content *fc, git_diff_list *diff, size_t delta_index, @@ -127,7 +127,7 @@ int diff_file_content_init_from_diff( return diff_file_content_init_common(fc, &diff->opts); } -int diff_file_content_init_from_blob( +int git_diff_file_content__init_from_blob( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, @@ -152,7 +152,7 @@ int diff_file_content_init_from_blob( return diff_file_content_init_common(fc, opts); } -int diff_file_content_init_from_raw( +int git_diff_file_content__init_from_raw( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, @@ -385,7 +385,7 @@ static int diff_file_content_load_workdir(git_diff_file_content *fc) return error; } -int diff_file_content_load(git_diff_file_content *fc) +int git_diff_file_content__load(git_diff_file_content *fc) { int error = 0; @@ -409,7 +409,7 @@ int diff_file_content_load(git_diff_file_content *fc) return 0; } -void diff_file_content_unload(git_diff_file_content *fc) +void git_diff_file_content__unload(git_diff_file_content *fc) { if (fc->file.flags & GIT_DIFF_FLAG__FREE_DATA) { git__free(fc->map.data); @@ -433,9 +433,9 @@ void diff_file_content_unload(git_diff_file_content *fc) fc->file.flags &= ~GIT_DIFF_FLAG__LOADED; } -void diff_file_content_clear(git_diff_file_content *fc) +void git_diff_file_content__clear(git_diff_file_content *fc) { - diff_file_content_unload(fc); + git_diff_file_content__unload(fc); /* for now, nothing else to do */ } diff --git a/src/diff_file.h b/src/diff_file.h index ab7b1dc1f..afad8510b 100644 --- a/src/diff_file.h +++ b/src/diff_file.h @@ -24,19 +24,19 @@ typedef struct { git_map map; } git_diff_file_content; -extern int diff_file_content_init_from_diff( +extern int git_diff_file_content__init_from_diff( git_diff_file_content *fc, git_diff_list *diff, size_t delta_index, bool use_old); -extern int diff_file_content_init_from_blob( +extern int git_diff_file_content__init_from_blob( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, const git_blob *blob); -extern int diff_file_content_init_from_raw( +extern int git_diff_file_content__init_from_raw( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, @@ -44,12 +44,12 @@ extern int diff_file_content_init_from_raw( size_t buflen); /* this loads the blob/file-on-disk as needed */ -extern int diff_file_content_load(git_diff_file_content *fc); +extern int git_diff_file_content__load(git_diff_file_content *fc); /* this releases the blob/file-in-memory */ -extern void diff_file_content_unload(git_diff_file_content *fc); +extern void git_diff_file_content__unload(git_diff_file_content *fc); /* this unloads and also releases any other resources */ -extern void diff_file_content_clear(git_diff_file_content *fc); +extern void git_diff_file_content__clear(git_diff_file_content *fc); #endif diff --git a/src/diff_patch.c b/src/diff_patch.c index 05dee5ef7..a1e1fe84c 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -11,6 +11,49 @@ #include "diff_patch.h" #include "diff_xdiff.h" +/* cached information about a single span in a diff */ +typedef struct diff_patch_line diff_patch_line; +struct diff_patch_line { + const char *ptr; + size_t len; + size_t lines, oldno, newno; + char origin; +}; + +/* cached information about a hunk in a diff */ +typedef struct diff_patch_hunk diff_patch_hunk; +struct diff_patch_hunk { + git_diff_range range; + char header[128]; + size_t header_len; + size_t line_start; + size_t line_count; +}; + +struct git_diff_patch { + git_refcount rc; + git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */ + git_diff_delta *delta; + size_t delta_index; + git_diff_file_content ofile; + git_diff_file_content nfile; + uint32_t flags; + git_array_t(diff_patch_hunk) hunks; + git_array_t(diff_patch_line) lines; + size_t oldno, newno; + size_t content_size; + git_pool flattened; +}; + +enum { + GIT_DIFF_PATCH_ALLOCATED = (1 << 0), + GIT_DIFF_PATCH_INITIALIZED = (1 << 1), + GIT_DIFF_PATCH_LOADED = (1 << 2), + GIT_DIFF_PATCH_DIFFABLE = (1 << 3), + GIT_DIFF_PATCH_DIFFED = (1 << 4), + GIT_DIFF_PATCH_FLATTENED = (1 << 5), +}; + static void diff_output_init(git_diff_output*, const git_diff_options*, git_diff_file_cb, git_diff_hunk_cb, git_diff_data_cb, void*); @@ -53,9 +96,9 @@ static int diff_patch_init_from_diff( patch->delta = git_vector_get(&diff->deltas, delta_index); patch->delta_index = delta_index; - if ((error = diff_file_content_init_from_diff( + if ((error = git_diff_file_content__init_from_diff( &patch->ofile, diff, delta_index, true)) < 0 || - (error = diff_file_content_init_from_diff( + (error = git_diff_file_content__init_from_diff( &patch->nfile, diff, delta_index, false)) < 0) return error; @@ -110,24 +153,24 @@ static int diff_patch_load(git_diff_patch *patch, git_diff_output *output) * need 2x data size and this minimizes peak memory footprint */ if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = diff_file_content_load(&patch->ofile)) < 0 || + if ((error = git_diff_file_content__load(&patch->ofile)) < 0 || (patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) goto cleanup; } if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = diff_file_content_load(&patch->nfile)) < 0 || + if ((error = git_diff_file_content__load(&patch->nfile)) < 0 || (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) goto cleanup; } /* once workdir has been tried, load other data as needed */ if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = diff_file_content_load(&patch->ofile)) < 0 || + if ((error = git_diff_file_content__load(&patch->ofile)) < 0 || (patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) goto cleanup; } if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) { - if ((error = diff_file_content_load(&patch->nfile)) < 0 || + if ((error = git_diff_file_content__load(&patch->nfile)) < 0 || (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) goto cleanup; } @@ -198,8 +241,8 @@ static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output) static void diff_patch_free(git_diff_patch *patch) { - diff_file_content_clear(&patch->ofile); - diff_file_content_clear(&patch->nfile); + git_diff_file_content__clear(&patch->ofile); + git_diff_file_content__clear(&patch->nfile); git_array_clear(patch->lines); git_array_clear(patch->hunks); @@ -323,9 +366,9 @@ static int diff_patch_from_blobs( new_blob = swap; } - if ((error = diff_file_content_init_from_blob( + if ((error = git_diff_file_content__init_from_blob( &pd->patch.ofile, repo, opts, old_blob)) < 0 || - (error = diff_file_content_init_from_blob( + (error = git_diff_file_content__init_from_blob( &pd->patch.nfile, repo, opts, new_blob)) < 0) return error; @@ -409,14 +452,14 @@ static int diff_patch_from_blob_and_buffer( return 0; if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { - if (!(error = diff_file_content_init_from_raw( + if (!(error = git_diff_file_content__init_from_raw( &pd->patch.ofile, repo, opts, buf, buflen))) - error = diff_file_content_init_from_blob( + error = git_diff_file_content__init_from_blob( &pd->patch.nfile, repo, opts, old_blob); } else { - if (!(error = diff_file_content_init_from_blob( + if (!(error = git_diff_file_content__init_from_blob( &pd->patch.ofile, repo, opts, old_blob))) - error = diff_file_content_init_from_raw( + error = git_diff_file_content__init_from_raw( &pd->patch.nfile, repo, opts, buf, buflen); } @@ -690,6 +733,68 @@ notfound: return diff_error_outofrange(thing); } +git_diff_list *git_diff_patch__diff(git_diff_patch *patch) +{ + return patch->diff; +} + +git_diff_driver *git_diff_patch__driver(git_diff_patch *patch) +{ + /* ofile driver is representative for whole patch */ + return patch->ofile.driver; +} + +void git_diff_patch__old_data( + char **ptr, size_t *len, git_diff_patch *patch) +{ + *ptr = patch->ofile.map.data; + *len = patch->ofile.map.len; +} + +void git_diff_patch__new_data( + char **ptr, size_t *len, git_diff_patch *patch) +{ + *ptr = patch->nfile.map.data; + *len = patch->nfile.map.len; +} + +int git_diff_patch__invoke_callbacks( + git_diff_patch *patch, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_data_cb line_cb, + void *payload) +{ + int error = 0; + uint32_t i, j; + + if (file_cb) + error = file_cb(patch->delta, 0, payload); + + if (!hunk_cb && !line_cb) + return error; + + for (i = 0; !error && i < git_array_size(patch->hunks); ++i) { + diff_patch_hunk *h = git_array_get(patch->hunks, i); + + error = hunk_cb( + patch->delta, &h->range, h->header, h->header_len, payload); + + if (!line_cb) + continue; + + for (j = 0; !error && j < h->line_count; ++j) { + diff_patch_line *l = + git_array_get(patch->lines, h->line_start + j); + + error = line_cb( + patch->delta, &h->range, l->origin, l->ptr, l->len, payload); + } + } + + return error; +} + static int diff_patch_file_cb( const git_diff_delta *delta, diff --git a/src/diff_patch.h b/src/diff_patch.h index 7de6e1e5b..56af14600 100644 --- a/src/diff_patch.h +++ b/src/diff_patch.h @@ -12,48 +12,19 @@ #include "diff_file.h" #include "array.h" -/* cached information about a single span in a diff */ -typedef struct diff_patch_line diff_patch_line; -struct diff_patch_line { - const char *ptr; - size_t len; - size_t lines, oldno, newno; - char origin; -}; +extern git_diff_list *git_diff_patch__diff(git_diff_patch *); -/* cached information about a hunk in a diff */ -typedef struct diff_patch_hunk diff_patch_hunk; -struct diff_patch_hunk { - git_diff_range range; - char header[128]; - size_t header_len; - size_t line_start; - size_t line_count; -}; +extern git_diff_driver *git_diff_patch__driver(git_diff_patch *); -struct git_diff_patch { - git_refcount rc; - git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */ - git_diff_delta *delta; - size_t delta_index; - git_diff_file_content ofile; - git_diff_file_content nfile; - uint32_t flags; - git_array_t(diff_patch_hunk) hunks; - git_array_t(diff_patch_line) lines; - size_t oldno, newno; - size_t content_size; - git_pool flattened; -}; +extern void git_diff_patch__old_data(char **, size_t *, git_diff_patch *); +extern void git_diff_patch__new_data(char **, size_t *, git_diff_patch *); -enum { - GIT_DIFF_PATCH_ALLOCATED = (1 << 0), - GIT_DIFF_PATCH_INITIALIZED = (1 << 1), - GIT_DIFF_PATCH_LOADED = (1 << 2), - GIT_DIFF_PATCH_DIFFABLE = (1 << 3), - GIT_DIFF_PATCH_DIFFED = (1 << 4), - GIT_DIFF_PATCH_FLATTENED = (1 << 5), -}; +extern int git_diff_patch__invoke_callbacks( + git_diff_patch *patch, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_data_cb line_cb, + void *payload); typedef struct git_diff_output git_diff_output; struct git_diff_output { diff --git a/src/diff_print.c b/src/diff_print.c index 860876531..244aa6e1d 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -383,29 +383,13 @@ int git_diff_patch_print( int error; git_buf temp = GIT_BUF_INIT; diff_print_info pi; - size_t h, l; assert(patch && print_cb); if (!(error = diff_print_info_init( - &pi, &temp, patch->diff, print_cb, payload))) - error = print_patch_file(patch->delta, 0, &pi); - - for (h = 0; h < git_array_size(patch->hunks) && !error; ++h) { - diff_patch_hunk *hunk = git_array_get(patch->hunks, h); - - error = print_patch_hunk( - patch->delta, &hunk->range, hunk->header, hunk->header_len, &pi); - - for (l = 0; l < hunk->line_count && !error; ++l) { - diff_patch_line *line = - git_array_get(patch->lines, hunk->line_start + l); - - error = print_patch_line( - patch->delta, &hunk->range, - line->origin, line->ptr, line->len, &pi); - } - } + &pi, &temp, git_diff_patch__diff(patch), print_cb, payload))) + error = git_diff_patch__invoke_callbacks( + patch, print_patch_file, print_patch_hunk, print_patch_line, &pi); git_buf_free(&temp); diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index 91c56f727..7694fb996 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -59,6 +59,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) { git_xdiff_info *info = priv; git_diff_patch *patch = info->patch; + const git_diff_delta *delta = git_diff_patch_delta(patch); git_diff_output *output = &info->xo->output; if (len == 1) { @@ -67,7 +68,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) return output->error; if (output->hunk_cb != NULL && - output->hunk_cb(patch->delta, &info->range, + output->hunk_cb(delta, &info->range, bufs[0].ptr, bufs[0].size, output->payload)) output->error = GIT_EUSER; } @@ -80,7 +81,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) GIT_DIFF_LINE_CONTEXT; if (output->data_cb != NULL && - output->data_cb(patch->delta, &info->range, + output->data_cb(delta, &info->range, origin, bufs[1].ptr, bufs[1].size, output->payload)) output->error = GIT_EUSER; } @@ -97,7 +98,7 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) GIT_DIFF_LINE_CONTEXT_EOFNL; if (output->data_cb != NULL && - output->data_cb(patch->delta, &info->range, + output->data_cb(delta, &info->range, origin, bufs[2].ptr, bufs[2].size, output->payload)) output->error = GIT_EUSER; } @@ -110,7 +111,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch) git_xdiff_output *xo = (git_xdiff_output *)output; git_xdiff_info info; git_diff_find_context_payload findctxt; - mmfile_t old_xdiff_data, new_xdiff_data; + mmfile_t xd_old_data, xd_new_data; memset(&info, 0, sizeof(info)); info.patch = patch; @@ -119,7 +120,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch) xo->callback.priv = &info; git_diff_find_context_init( - &xo->config.find_func, &findctxt, patch->ofile.driver); + &xo->config.find_func, &findctxt, git_diff_patch__driver(patch)); xo->config.find_func_priv = &findctxt; if (xo->config.find_func != NULL) @@ -131,12 +132,10 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch) * updates are needed to xo->params.flags */ - old_xdiff_data.ptr = patch->ofile.map.data; - old_xdiff_data.size = patch->ofile.map.len; - new_xdiff_data.ptr = patch->nfile.map.data; - new_xdiff_data.size = patch->nfile.map.len; + git_diff_patch__old_data(&xd_old_data.ptr, &xd_old_data.size, patch); + git_diff_patch__new_data(&xd_new_data.ptr, &xd_new_data.size, patch); - xdl_diff(&old_xdiff_data, &new_xdiff_data, + xdl_diff(&xd_old_data, &xd_new_data, &xo->params, &xo->config, &xo->callback); git_diff_find_context_clear(&findctxt); From 37f66e82635c95d808c418f00d8e3c6646bd31f2 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 12 Jun 2013 15:21:21 -0700 Subject: [PATCH 330/384] Fix Windows warnings This fixes problems with missing function prototypes and 64-bit data issues on Windows. --- src/config.c | 4 ++-- src/diff_driver.c | 4 ++-- src/diff_driver.h | 2 +- src/diff_tform.c | 6 +++--- src/revparse.c | 6 ++++-- src/transports/winhttp.c | 2 ++ src/win32/posix_w32.c | 1 + tests-clar/refs/iterator.c | 2 +- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/config.c b/src/config.c index 9491d267a..4066cf741 100644 --- a/src/config.c +++ b/src/config.c @@ -154,7 +154,7 @@ static int find_internal_file_by_level( } else { git_vector_foreach(&cfg->files, i, internal) { if (internal->level == level) - pos = i; + pos = (int)i; } } @@ -189,7 +189,7 @@ static void try_remove_existing_file_internal( git_vector_foreach(&cfg->files, i, internal) { if (internal->level == level) - pos = i; + pos = (int)i; } if (pos == -1) diff --git a/src/diff_driver.c b/src/diff_driver.c index ae2b7c319..469be0d14 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -331,7 +331,7 @@ int git_diff_driver_content_is_binary( } static int diff_context_line__simple( - git_diff_driver *driver, const char *line, long line_len) + git_diff_driver *driver, const char *line, size_t line_len) { GIT_UNUSED(driver); GIT_UNUSED(line_len); @@ -339,7 +339,7 @@ static int diff_context_line__simple( } static int diff_context_line__pattern_match( - git_diff_driver *driver, const char *line, long line_len) + git_diff_driver *driver, const char *line, size_t line_len) { size_t i; diff --git a/src/diff_driver.h b/src/diff_driver.h index 3db7df000..9d3f18660 100644 --- a/src/diff_driver.h +++ b/src/diff_driver.h @@ -31,7 +31,7 @@ typedef long (*git_diff_find_context_fn)( const char *, long, char *, long, void *); typedef int (*git_diff_find_context_line)( - git_diff_driver *, const char *, long); + git_diff_driver *, const char *, size_t); typedef struct { git_diff_driver *driver; diff --git a/src/diff_tform.c b/src/diff_tform.c index 53a7e63ff..94fa035f2 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -711,7 +711,7 @@ int git_diff_find_similar( git_vector_foreach(&diff->deltas, i, to) { size_t tried_sources = 0; - match_targets[i].idx = i; + match_targets[i].idx = (uint32_t)i; match_targets[i].similarity = 0; /* skip things that are not rename targets */ @@ -744,8 +744,8 @@ int git_diff_find_similar( match_sources[j].similarity < (uint32_t)similarity) { match_targets[i].similarity = (uint32_t)similarity; match_sources[j].similarity = (uint32_t)similarity; - match_targets[i].idx = j; - match_sources[j].idx = i; + match_targets[i].idx = (uint32_t)j; + match_sources[j].idx = (uint32_t)i; } } } diff --git a/src/revparse.c b/src/revparse.c index 3e3edb6cc..bcfb0843f 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -674,7 +674,7 @@ static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_ int revparse__ext( git_object **object_out, git_reference **reference_out, - int *identifier_len_out, + size_t *identifier_len_out, git_repository *repo, const char *spec) { @@ -832,7 +832,8 @@ int git_revparse_ext( git_repository *repo, const char *spec) { - int error, identifier_len; + int error; + size_t identifier_len; git_object *obj = NULL; git_reference *ref = NULL; @@ -841,6 +842,7 @@ int git_revparse_ext( *object_out = obj; *reference_out = ref; + GIT_UNUSED(identifier_len); return 0; diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index d803c812c..95e422dc0 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -19,6 +19,8 @@ #include #pragma comment(lib, "winhttp") +#include + /* For UuidCreate */ #pragma comment(lib, "rpcrt4") diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index f9967e04a..f04974428 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -5,6 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ #include "../posix.h" +#include "../fileops.h" #include "path.h" #include "utf-conv.h" #include "repository.h" diff --git a/tests-clar/refs/iterator.c b/tests-clar/refs/iterator.c index 7a966892b..266410fdf 100644 --- a/tests-clar/refs/iterator.c +++ b/tests-clar/refs/iterator.c @@ -66,7 +66,7 @@ void test_refs_iterator__list(void) } while (!error); git_reference_iterator_free(iter); - cl_assert_equal_i(output.length, ARRAY_SIZE(refnames)); + cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames)); git_vector_sort(&output); From 6de9b2ee14a2393fae3ed86c5a5d12712c83b083 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 12 Jun 2013 21:10:33 +0200 Subject: [PATCH 331/384] util: It's called `memzero` --- src/cache.c | 2 +- src/config.c | 2 +- src/diff.c | 2 +- src/index.c | 2 +- src/odb.c | 2 +- src/refdb.c | 2 +- src/repository.c | 2 +- src/util.c | 5 ++--- src/util.h | 6 +++--- 9 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/cache.c b/src/cache.c index 570838e44..36ce66570 100644 --- a/src/cache.c +++ b/src/cache.c @@ -107,7 +107,7 @@ void git_cache_free(git_cache *cache) git_cache_clear(cache); git_oidmap_free(cache->map); git_mutex_free(&cache->lock); - git__memset(cache, 0, sizeof(*cache)); + git__memzero(cache, sizeof(*cache)); } /* Called with lock */ diff --git a/src/config.c b/src/config.c index 006025903..068c40260 100644 --- a/src/config.c +++ b/src/config.c @@ -47,7 +47,7 @@ static void config_free(git_config *cfg) git_vector_free(&cfg->files); - git__memset(cfg, 0, sizeof(*cfg)); + git__memzero(cfg, sizeof(*cfg)); git__free(cfg); } diff --git a/src/diff.c b/src/diff.c index bd2b88167..3bfe149e3 100644 --- a/src/diff.c +++ b/src/diff.c @@ -466,7 +466,7 @@ static void diff_list_free(git_diff_list *diff) git_pathspec_free(&diff->pathspec); git_pool_clear(&diff->pool); - git__memset(diff, 0, sizeof(*diff)); + git__memzero(diff, sizeof(*diff)); git__free(diff); } diff --git a/src/index.c b/src/index.c index d519954c5..4f0c70135 100644 --- a/src/index.c +++ b/src/index.c @@ -349,7 +349,7 @@ static void index_free(git_index *index) git__free(index->index_file_path); - git__memset(index, 0, sizeof(*index)); + git__memzero(index, sizeof(*index)); git__free(index); } diff --git a/src/odb.c b/src/odb.c index 5e27edacd..8e62efd00 100644 --- a/src/odb.c +++ b/src/odb.c @@ -590,7 +590,7 @@ static void odb_free(git_odb *db) git_vector_free(&db->backends); git_cache_free(&db->own_cache); - git__memset(db, 0, sizeof(*db)); + git__memzero(db, sizeof(*db)); git__free(db); } diff --git a/src/refdb.c b/src/refdb.c index 6da409aac..4de7188b2 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -89,7 +89,7 @@ int git_refdb_compress(git_refdb *db) void git_refdb__free(git_refdb *db) { refdb_free_backend(db); - git__memset(db, 0, sizeof(*db)); + git__memzero(db, sizeof(*db)); git__free(db); } diff --git a/src/repository.c b/src/repository.c index 9cb093dd9..ed9469c59 100644 --- a/src/repository.c +++ b/src/repository.c @@ -119,7 +119,7 @@ void git_repository_free(git_repository *repo) git__free(repo->workdir); git__free(repo->namespace); - git__memset(repo, 0, sizeof(*repo)); + git__memzero(repo, sizeof(*repo)); git__free(repo); } diff --git a/src/util.c b/src/util.c index 248cf4c42..1d084daa8 100644 --- a/src/util.c +++ b/src/util.c @@ -723,12 +723,11 @@ void git__insertsort_r( git__free(swapel); } -void git__memset(void *data, int c, size_t size) +void git__memzero(volatile void *data, size_t size) { volatile uint8_t *scan = data; uint8_t *end = scan + size; - uint8_t val = (uint8_t)c; while (scan < end) - *scan++ = val; + *scan++ = 0x0; } diff --git a/src/util.h b/src/util.h index 9417515a3..0de466677 100644 --- a/src/util.h +++ b/src/util.h @@ -322,9 +322,9 @@ extern int git__date_parse(git_time_t *out, const char *date); extern size_t git__unescape(char *str); /* - * Memset that will not be optimized away by the compiler. - * You usually should just use regular `memset()`. + * Safely zero-out memory, making sure that the compiler + * doesn't optimize away the operation. */ -extern void git__memset(void *data, int c, size_t size); +extern void git__memzero(volatile void *data, size_t size); #endif /* INCLUDE_util_h__ */ From 5a6e45cc841194a61ccad1006ad4a4b9aa2ecbc5 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 12 Jun 2013 21:14:04 +0200 Subject: [PATCH 332/384] Revert "cmake: Update Windows resources to reflect the optional vendor string" This reverts commit 095bfd748766966f5515bdfe64867d6a09287123. --- CMakeLists.txt | 11 ----------- src/win32/{git2.rc.cmake => git2.rc} | 6 +++++- 2 files changed, 5 insertions(+), 12 deletions(-) rename src/win32/{git2.rc.cmake => git2.rc} (91%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b931dc5f1..f06a84299 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -330,19 +330,8 @@ IF (SONAME) SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME "git2-${SONAME_APPEND}") ENDIF() ENDIF() - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY) -IF (NOT BUILD_SHARED_LIBS) - SET(LIBGIT2_NAME_PREFIX "lib") -ENDIF() - -IF (SONAME_APPEND) - SET(LIBGIT2_NAME_SUFFIX "-${SONAME_APPEND}") -ENDIF() - -CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/src/win32/git2.rc.cmake ${WIN_RC} @ONLY) - IF (MSVC_IDE) # Precompiled headers SET_TARGET_PROPERTIES(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h") diff --git a/src/win32/git2.rc.cmake b/src/win32/git2.rc similarity index 91% rename from src/win32/git2.rc.cmake rename to src/win32/git2.rc index dc9b3e6eb..436913228 100644 --- a/src/win32/git2.rc.cmake +++ b/src/win32/git2.rc @@ -1,7 +1,11 @@ #include #include "../../include/git2/version.h" -#define LIBGIT2_FILENAME "@LIBGIT2_NAME_PREFIX@git2@LIBGIT2_NAME_SUFFIX@.dll" +#ifndef INCLUDE_LIB +#define LIBGIT2_FILENAME "git2.dll" +#else +#define LIBGIT2_FILENAME "libgit2.dll" +#endif VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE FILEVERSION LIBGIT2_VER_MAJOR,LIBGIT2_VER_MINOR,LIBGIT2_VER_REVISION,0 From c1cf1af46a541419160473decb0955dda6ac721e Mon Sep 17 00:00:00 2001 From: nulltoken Date: Wed, 12 Jun 2013 21:15:58 +0200 Subject: [PATCH 333/384] cmake: Add option to specify the name of the binary --- CMakeLists.txt | 8 +++++--- src/win32/git2.rc | 10 ++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f06a84299..bdc46d0b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,8 @@ OPTION( BUILD_EXAMPLES "Build library usage example apps" OFF ) OPTION( TAGS "Generate tags" OFF ) OPTION( PROFILE "Generate profiling information" OFF ) OPTION( ENABLE_TRACE "Enables tracing support" OFF ) -OPTION( SONAME_APPEND "Append the given string to the library's filename" OFF ) +OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF ) + IF(MSVC) # This option is only availalbe when building with MSVC. By default, # libgit2 is build using the stdcall calling convention, as that's what @@ -326,8 +327,9 @@ MSVC_SPLIT_SOURCES(git2) IF (SONAME) SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING}) SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_VERSION_MAJOR}) - IF (SONAME_APPEND) - SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME "git2-${SONAME_APPEND}") + IF (LIBGIT2_FILENAME) + ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\") + SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME}) ENDIF() ENDIF() CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY) diff --git a/src/win32/git2.rc b/src/win32/git2.rc index 436913228..22c63f695 100644 --- a/src/win32/git2.rc +++ b/src/win32/git2.rc @@ -1,10 +1,8 @@ #include #include "../../include/git2/version.h" -#ifndef INCLUDE_LIB -#define LIBGIT2_FILENAME "git2.dll" -#else -#define LIBGIT2_FILENAME "libgit2.dll" +#ifndef LIBGIT2_FILENAME +# define LIBGIT2_FILENAME "git2" #endif VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE @@ -27,9 +25,9 @@ BEGIN BEGIN VALUE "FileDescription", "libgit2 - the Git linkable library\0" VALUE "FileVersion", LIBGIT2_VERSION "\0" - VALUE "InternalName", LIBGIT2_FILENAME "\0" + VALUE "InternalName", LIBGIT2_FILENAME ".dll\0" VALUE "LegalCopyright", "Copyright (C) the libgit2 contributors. All rights reserved.\0" - VALUE "OriginalFilename", LIBGIT2_FILENAME "\0" + VALUE "OriginalFilename", LIBGIT2_FILENAME ".dll\0" VALUE "ProductName", "libgit2\0" VALUE "ProductVersion", LIBGIT2_VERSION "\0" VALUE "Comments", "For more information visit http://libgit2.github.com/\0" From 2da72fb21c1719b34a312dba3ebb7691032cfaa9 Mon Sep 17 00:00:00 2001 From: yorah Date: Fri, 14 Jun 2013 12:10:13 +0200 Subject: [PATCH 334/384] fileops: fix invalid read --- src/fileops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fileops.c b/src/fileops.c index 02f48e120..ae240fcd2 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -320,7 +320,7 @@ int git_futils_mkdir( min_root_len = git_path_root(make_path.ptr); if (root < min_root_len) root = min_root_len; - while (make_path.ptr[root] == '/') + while (root >= 0 && make_path.ptr[root] == '/') ++root; /* clip root to make_path length */ From 519072c9bfedeb2b6c4a8ad49739e5563813c3c4 Mon Sep 17 00:00:00 2001 From: yorah Date: Fri, 14 Jun 2013 14:02:00 +0200 Subject: [PATCH 335/384] diff: fix warning --- tests-clar/diff/rename.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index fd31a3859..224945e60 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -825,6 +825,8 @@ int test_names_expected(const git_diff_delta *delta, float progress, void *p) { struct rename_expected *expected = p; + GIT_UNUSED(progress); + cl_assert(expected->idx < expected->len); cl_assert_equal_i(delta->status, GIT_DELTA_RENAMED); From 3425fee63773813a48f596637609efaa36428713 Mon Sep 17 00:00:00 2001 From: yorah Date: Mon, 17 Jun 2013 14:27:34 +0200 Subject: [PATCH 336/384] util: git__memzero() tweaks On Linux: fix a warning message related to the volatile qualifier (cast) On Windows: use SecureZeroMemory() On both, inline the call, so that no entry point can lead back to this "secure" memory zeroing. --- src/util.c | 9 --------- src/util.h | 12 +++++++++++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/util.c b/src/util.c index 1d084daa8..da15a039d 100644 --- a/src/util.c +++ b/src/util.c @@ -722,12 +722,3 @@ void git__insertsort_r( if (freeswap) git__free(swapel); } - -void git__memzero(volatile void *data, size_t size) -{ - volatile uint8_t *scan = data; - uint8_t *end = scan + size; - - while (scan < end) - *scan++ = 0x0; -} diff --git a/src/util.h b/src/util.h index 0de466677..1ef9e65b5 100644 --- a/src/util.h +++ b/src/util.h @@ -325,6 +325,16 @@ extern size_t git__unescape(char *str); * Safely zero-out memory, making sure that the compiler * doesn't optimize away the operation. */ -extern void git__memzero(volatile void *data, size_t size); +GIT_INLINE(void) git__memzero(void *data, size_t size) +{ +#ifdef _MSC_VER + SecureZeroMemory((PVOID)data, size); +#else + volatile uint8_t *scan = (volatile uint8_t *)data; + + while (size--) + *scan++ = 0x0; +#endif +} #endif /* INCLUDE_util_h__ */ From 0525fb7ef3cb347e20db8582dcfc9c4c67bd9267 Mon Sep 17 00:00:00 2001 From: yorah Date: Mon, 17 Jun 2013 14:31:14 +0200 Subject: [PATCH 337/384] cred: deploy git__memzero to clear memory holding a password --- src/transports/cred.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transports/cred.c b/src/transports/cred.c index 4916c6e18..ba5de6e93 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -17,7 +17,7 @@ static void plaintext_free(struct git_cred *cred) git__free(c->username); /* Zero the memory which previously held the password */ - memset(c->password, 0x0, pass_len); + git__memzero(c->password, pass_len); git__free(c->password); memset(c, 0, sizeof(*c)); @@ -73,7 +73,7 @@ static void ssh_keyfile_passphrase_free(struct git_cred *cred) if (c->passphrase) { /* Zero the memory which previously held the passphrase */ - memset(c->passphrase, 0x0, pass_len); + git__memzero(c->passphrase, pass_len); git__free(c->passphrase); } From 2ad7a4dc92a3e604d4ef52899c88c19b4295afa6 Mon Sep 17 00:00:00 2001 From: yorah Date: Mon, 17 Jun 2013 18:25:30 +0200 Subject: [PATCH 338/384] ref: free the last ref when cancelling git_branch_foreach() Also fixed an assert typo on nulltoken's HEAD --- src/branch.c | 6 ++---- tests-clar/refs/branches/foreach.c | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/branch.c b/src/branch.c index de38e3355..590cdc027 100644 --- a/src/branch.c +++ b/src/branch.c @@ -132,18 +132,17 @@ int git_branch_foreach( { git_reference_iterator *iter; git_reference *ref; - int error; + int error = 0; if (git_reference_iterator_new(&iter, repo) < 0) return -1; - while ((error = git_reference_next(&ref, iter)) == 0) { + while (!error && (error = git_reference_next(&ref, iter)) == 0) { if (list_flags & GIT_BRANCH_LOCAL && git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0) { if (callback(ref->name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, payload)) { error = GIT_EUSER; - break; } } @@ -152,7 +151,6 @@ int git_branch_foreach( if (callback(ref->name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, payload)) { error = GIT_EUSER; - break; } } diff --git a/tests-clar/refs/branches/foreach.c b/tests-clar/refs/branches/foreach.c index 57f9c0e39..433812cb4 100644 --- a/tests-clar/refs/branches/foreach.c +++ b/tests-clar/refs/branches/foreach.c @@ -122,7 +122,7 @@ void test_refs_branches_foreach__retrieve_remote_symbolic_HEAD_when_present(void cl_git_pass(git_branch_foreach(repo, GIT_BRANCH_REMOTE, contains_branch_list_cb, &exp)); assert_branch_has_been_found(exp, "nulltoken/HEAD"); - assert_branch_has_been_found(exp, "nulltoken/HEAD"); + assert_branch_has_been_found(exp, "nulltoken/master"); } static int branch_list_interrupt_cb( From 09c2f91c150a4862c9189d9e08d0dc111d4d706c Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Mon, 17 Jun 2013 18:48:02 +0200 Subject: [PATCH 339/384] branch: More obvious semantics in `foreach` --- src/branch.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/branch.c b/src/branch.c index 590cdc027..7064fa7fc 100644 --- a/src/branch.c +++ b/src/branch.c @@ -137,7 +137,7 @@ int git_branch_foreach( if (git_reference_iterator_new(&iter, repo) < 0) return -1; - while (!error && (error = git_reference_next(&ref, iter)) == 0) { + while ((error = git_reference_next(&ref, iter)) == 0) { if (list_flags & GIT_BRANCH_LOCAL && git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0) { if (callback(ref->name + strlen(GIT_REFS_HEADS_DIR), @@ -155,6 +155,10 @@ int git_branch_foreach( } git_reference_free(ref); + + /* check if the callback has cancelled iteration */ + if (error == GIT_EUSER) + break; } if (error == GIT_ITEROVER) From 1ee2ef87ec4c2c63b7c89849eb9daad9e46e6fe7 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 21 May 2013 11:05:21 -0500 Subject: [PATCH 340/384] status access by index, providing more details to callers --- include/git2/status.h | 63 ++++++++++++ include/git2/types.h | 3 + src/status.c | 232 ++++++++++++++++++++++++++++++------------ src/status.h | 23 +++++ 4 files changed, 255 insertions(+), 66 deletions(-) create mode 100644 src/status.h diff --git a/include/git2/status.h b/include/git2/status.h index 38b6fa5bd..ce1d44a06 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -174,9 +174,72 @@ typedef struct { git_strarray pathspec; } git_status_options; +/** + * A status entry, providing the differences between the file as it exists + * in HEAD and the index, and providing the differences between the index + * and the working directory. + * + * The `status` value provides the status flags for this file. + * + * The `head_to_index` value provides detailed information about the + * differences between the file in HEAD and the file in the index. + * + * The `index_to_workdir` value provides detailed information about the + * differences between the file in the index and the file in the + * working directory. + */ +typedef struct { + git_status_t status; + git_diff_delta *head_to_index; + git_diff_delta *index_to_workdir; +} git_status_entry; + #define GIT_STATUS_OPTIONS_VERSION 1 #define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION} +/** + * Gather file status information and populate the `git_status_list`. + * + * @param out Pointer to store the status results in + * @param repo Repository object + * @param opts Status options structure + * @return 0 on success or error code + */ +GIT_EXTERN(int) git_status_list_new( + git_status_list **out, + git_repository *repo, + const git_status_options *opts); + +/** + * Gets the count of status entries in this list. + * + * @param statuslist Existing status list object + * @return the number of status entries + */ +GIT_EXTERN(size_t) git_status_list_entrycount( + git_status_list *statuslist); + +/** + * Get a pointer to one of the entries in the status list. + * + * The entry is not modifiable and should not be freed. + * + * @param statuslist Existing status list object + * @param idx Position of the entry + * @return Pointer to the entry; NULL if out of bounds + */ +GIT_EXTERN(const git_status_entry *) git_status_byindex( + git_status_list *statuslist, + size_t idx); + +/** + * Free an existing status list + * + * @param statuslist Existing status list object + */ +GIT_EXTERN(void) git_status_list_free( + git_status_list *statuslist); + /** * Gather file status information and run callbacks as requested. * diff --git a/include/git2/types.h b/include/git2/types.h index 1bfa73be6..dc344075c 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -174,6 +174,9 @@ typedef struct git_reference_iterator git_reference_iterator; /** Merge heads, the input to merge */ typedef struct git_merge_head git_merge_head; +/** Representation of a status collection */ +typedef struct git_status_list git_status_list; + /** Basic type of any Git reference. */ typedef enum { diff --git a/src/status.c b/src/status.c index 712e0d515..33e67efac 100644 --- a/src/status.c +++ b/src/status.c @@ -11,6 +11,7 @@ #include "hash.h" #include "vector.h" #include "tree.h" +#include "status.h" #include "git2/status.h" #include "repository.h" #include "ignore.h" @@ -77,71 +78,105 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) return st; } -typedef struct { - git_status_cb cb; - void *payload; - const git_status_options *opts; -} status_user_callback; - -static int status_invoke_cb( - git_diff_delta *h2i, git_diff_delta *i2w, void *payload) +static bool status_is_included( + git_status_list *statuslist, + git_diff_delta *head2idx, + git_diff_delta *idx2wd) { - status_user_callback *usercb = payload; - const char *path = NULL; - unsigned int status = 0; - - if (i2w) { - path = i2w->old_file.path; - status |= workdir_delta2status(i2w->status); - } - if (h2i) { - path = h2i->old_file.path; - status |= index_delta2status(h2i->status); - } - /* if excluding submodules and this is a submodule everywhere */ - if (usercb->opts && - (usercb->opts->flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) - { - bool in_tree = (h2i && h2i->status != GIT_DELTA_ADDED); - bool in_index = (h2i && h2i->status != GIT_DELTA_DELETED); - bool in_wd = (i2w && i2w->status != GIT_DELTA_DELETED); + if ((statuslist->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) { + bool in_tree = (head2idx && head2idx->status != GIT_DELTA_ADDED); + bool in_index = (head2idx && head2idx->status != GIT_DELTA_DELETED); + bool in_wd = (idx2wd && idx2wd->status != GIT_DELTA_DELETED); - if ((!in_tree || h2i->old_file.mode == GIT_FILEMODE_COMMIT) && - (!in_index || h2i->new_file.mode == GIT_FILEMODE_COMMIT) && - (!in_wd || i2w->new_file.mode == GIT_FILEMODE_COMMIT)) + if ((!in_tree || head2idx->old_file.mode == GIT_FILEMODE_COMMIT) && + (!in_index || head2idx->new_file.mode == GIT_FILEMODE_COMMIT) && + (!in_wd || idx2wd->new_file.mode == GIT_FILEMODE_COMMIT)) return 0; } - return usercb->cb(path, status, usercb->payload); + return 1; } -int git_status_foreach_ext( - git_repository *repo, - const git_status_options *opts, - git_status_cb cb, +static git_status_t status_compute( + git_diff_delta *head2idx, + git_diff_delta *idx2wd) +{ + git_status_t status = 0; + + if (head2idx) + status |= index_delta2status(head2idx->status); + + if (idx2wd) + status |= workdir_delta2status(idx2wd->status); + + return status; +} + +static int status_collect( + git_diff_delta *head2idx, + git_diff_delta *idx2wd, void *payload) { - int err = 0; + git_status_list *statuslist = payload; + git_status_entry *status_entry; + + if (!status_is_included(statuslist, head2idx, idx2wd)) + return 0; + + status_entry = git__malloc(sizeof(git_status_entry)); + GITERR_CHECK_ALLOC(status_entry); + + status_entry->status = status_compute(head2idx, idx2wd); + status_entry->head_to_index = head2idx; + status_entry->index_to_workdir = idx2wd; + + git_vector_insert(&statuslist->paired, status_entry); + + return 0; +} + +git_status_list *git_status_list_alloc(void) +{ + git_status_list *statuslist = NULL; + + if ((statuslist = git__calloc(1, sizeof(git_status_list))) == NULL || + git_vector_init(&statuslist->paired, 0, NULL) < 0) + return NULL; + + return statuslist; +} + +int git_status_list_new( + git_status_list **out, + git_repository *repo, + const git_status_options *opts) +{ + git_status_list *statuslist = NULL; git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT; - git_diff_list *head2idx = NULL, *idx2wd = NULL; git_tree *head = NULL; git_status_show_t show = opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR; - status_user_callback usercb; + int error = 0; assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR); + *out = NULL; + GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options"); - if (show != GIT_STATUS_SHOW_INDEX_ONLY && - (err = git_repository__ensure_not_bare(repo, "status")) < 0) - return err; + if ((error = git_repository__ensure_not_bare(repo, "status")) < 0) + return error; /* if there is no HEAD, that's okay - we'll make an empty iterator */ - if (((err = git_repository_head_tree(&head, repo)) < 0) && - !(err == GIT_ENOTFOUND || err == GIT_EORPHANEDHEAD)) - return err; + if (((error = git_repository_head_tree(&head, repo)) < 0) && + !(error == GIT_ENOTFOUND || error == GIT_EORPHANEDHEAD)) + return error; + + statuslist = git_status_list_alloc(); + GITERR_CHECK_ALLOC(statuslist); + + memcpy(&statuslist->opts, opts, sizeof(git_status_options)); memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); @@ -163,41 +198,106 @@ int git_status_foreach_ext( diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES; if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) { - err = git_diff_tree_to_index(&head2idx, repo, head, NULL, &diffopt); - if (err < 0) - goto cleanup; + error = git_diff_tree_to_index(&statuslist->head2idx, repo, head, NULL, &diffopt); + + if (error < 0) + goto on_error; } if (show != GIT_STATUS_SHOW_INDEX_ONLY) { - err = git_diff_index_to_workdir(&idx2wd, repo, NULL, &diffopt); - if (err < 0) - goto cleanup; - } + error = git_diff_index_to_workdir(&statuslist->idx2wd, repo, NULL, &diffopt); - usercb.cb = cb; - usercb.payload = payload; - usercb.opts = opts; + if (error < 0) + goto on_error; + } if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) { - if ((err = git_diff__paired_foreach( - head2idx, NULL, status_invoke_cb, &usercb)) < 0) - goto cleanup; + if ((error = git_diff__paired_foreach(statuslist->head2idx, NULL, status_collect, statuslist)) < 0) + goto on_error; - git_diff_list_free(head2idx); - head2idx = NULL; + git_diff_list_free(statuslist->head2idx); + statuslist->head2idx = NULL; } - err = git_diff__paired_foreach(head2idx, idx2wd, status_invoke_cb, &usercb); + if ((error = git_diff__paired_foreach(statuslist->head2idx, statuslist->idx2wd, status_collect, statuslist)) < 0) + goto on_error; -cleanup: + *out = statuslist; + goto done; + +on_error: + git_status_list_free(statuslist); + +done: git_tree_free(head); - git_diff_list_free(head2idx); - git_diff_list_free(idx2wd); - if (err == GIT_EUSER) - giterr_clear(); + return error; +} - return err; +size_t git_status_list_entrycount(git_status_list *statuslist) +{ + assert(statuslist); + + return statuslist->paired.length; +} + +const git_status_entry *git_status_byindex( + git_status_list *statuslist, + size_t i) +{ + assert(statuslist); + + return git_vector_get(&statuslist->paired, i); +} + +void git_status_list_free(git_status_list *statuslist) +{ + git_status_entry *status_entry; + size_t i; + + if (statuslist == NULL) + return; + + git_diff_list_free(statuslist->head2idx); + git_diff_list_free(statuslist->idx2wd); + + git_vector_foreach(&statuslist->paired, i, status_entry) + git__free(status_entry); + + git_vector_free(&statuslist->paired); + + git__free(statuslist); +} + +int git_status_foreach_ext( + git_repository *repo, + const git_status_options *opts, + git_status_cb cb, + void *payload) +{ + git_status_list *statuslist; + const git_status_entry *status_entry; + size_t i; + int error = 0; + + if ((error = git_status_list_new(&statuslist, repo, opts)) < 0) + return error; + + git_vector_foreach(&statuslist->paired, i, status_entry) { + const char *path = status_entry->head_to_index ? + status_entry->head_to_index->old_file.path : + status_entry->index_to_workdir->old_file.path; + + if (cb(path, status_entry->status, payload) != 0) { + error = GIT_EUSER; + giterr_clear(); + break; + } + } + + git_status_list_free(statuslist); + + return error; } int git_status_foreach( diff --git a/src/status.h b/src/status.h new file mode 100644 index 000000000..b58e0ebd6 --- /dev/null +++ b/src/status.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_status_h__ +#define INCLUDE_status_h__ + +#include "diff.h" +#include "git2/status.h" +#include "git2/diff.h" + +struct git_status_list { + git_status_options opts; + + git_diff_list *head2idx; + git_diff_list *idx2wd; + + git_vector paired; +}; + +#endif From dfe8c8df3707b2773e376633c5908dc612e59d6a Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 22 May 2013 23:19:40 -0500 Subject: [PATCH 341/384] handle renames in status computation --- include/git2/status.h | 21 ++- src/status.c | 108 +++++++++++++-- tests-clar/status/renames.c | 265 ++++++++++++++++++++++++++++++++++++ 3 files changed, 372 insertions(+), 22 deletions(-) create mode 100644 tests-clar/status/renames.c diff --git a/include/git2/status.h b/include/git2/status.h index ce1d44a06..64ce6756c 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -42,6 +42,7 @@ typedef enum { GIT_STATUS_WT_MODIFIED = (1u << 8), GIT_STATUS_WT_DELETED = (1u << 9), GIT_STATUS_WT_TYPECHANGE = (1u << 10), + GIT_STATUS_WT_RENAMED = (1u << 11), GIT_STATUS_IGNORED = (1u << 14), } git_status_t; @@ -130,6 +131,10 @@ typedef enum { * - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of * ignored directories should be included in the status. This is like * doing `git ls-files -o -i --exclude-standard` with core git. + * - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that items that are + * renamed in the index will be reported as renames. + * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates that items that + * are renamed in the working directory will be reported as renames. * * Calling `git_status_foreach()` is like calling the extended version * with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED, @@ -137,13 +142,15 @@ typedef enum { * together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline. */ typedef enum { - GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0), - GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1), - GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2), - GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3), - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4), - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5), - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6), + GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0), + GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1), + GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2), + GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3), + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4), + GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5), + GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6), + GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7), + GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8), } git_status_opt_t; #define GIT_STATUS_OPT_DEFAULTS \ diff --git a/src/status.c b/src/status.c index 33e67efac..20e45b75f 100644 --- a/src/status.c +++ b/src/status.c @@ -54,7 +54,6 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) switch (workdir_status) { case GIT_DELTA_ADDED: - case GIT_DELTA_RENAMED: case GIT_DELTA_COPIED: case GIT_DELTA_UNTRACKED: st = GIT_STATUS_WT_NEW; @@ -68,6 +67,9 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) case GIT_DELTA_IGNORED: st = GIT_STATUS_IGNORED; break; + case GIT_DELTA_RENAMED: + st = GIT_STATUS_WT_RENAMED; + break; case GIT_DELTA_TYPECHANGE: st = GIT_STATUS_WT_TYPECHANGE; break; @@ -85,9 +87,9 @@ static bool status_is_included( { /* if excluding submodules and this is a submodule everywhere */ if ((statuslist->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) { - bool in_tree = (head2idx && head2idx->status != GIT_DELTA_ADDED); + bool in_tree = (head2idx && head2idx->status != GIT_DELTA_ADDED); bool in_index = (head2idx && head2idx->status != GIT_DELTA_DELETED); - bool in_wd = (idx2wd && idx2wd->status != GIT_DELTA_DELETED); + bool in_wd = (idx2wd && idx2wd->status != GIT_DELTA_DELETED); if ((!in_tree || head2idx->old_file.mode == GIT_FILEMODE_COMMIT) && (!in_index || head2idx->new_file.mode == GIT_FILEMODE_COMMIT) && @@ -136,24 +138,79 @@ static int status_collect( return 0; } -git_status_list *git_status_list_alloc(void) +GIT_INLINE(int) status_entry_cmp_base( + const void *a, + const void *b, + int (*strcomp)(const char *a, const char *b)) +{ + const git_status_entry *entry_a = a; + const git_status_entry *entry_b = b; + const git_diff_delta *delta_a, *delta_b; + + delta_a = entry_a->index_to_workdir ? entry_a->index_to_workdir : + entry_a->head_to_index; + delta_b = entry_b->index_to_workdir ? entry_b->index_to_workdir : + entry_b->head_to_index; + + if (!delta_a && delta_b) + return -1; + if (delta_a && !delta_b) + return 1; + if (!delta_a && !delta_b) + return 0; + + return strcomp(delta_a->new_file.path, delta_b->new_file.path); +} + +static int status_entry_icmp(const void *a, const void *b) +{ + return status_entry_cmp_base(a, b, git__strcasecmp); +} + +static int status_entry_cmp(const void *a, const void *b) +{ + return status_entry_cmp_base(a, b, git__strcmp); +} + +static git_status_list *git_status_list_alloc(git_index *index) { git_status_list *statuslist = NULL; + int (*entrycmp)(const void *a, const void *b); + + entrycmp = index->ignore_case ? status_entry_icmp : status_entry_cmp; if ((statuslist = git__calloc(1, sizeof(git_status_list))) == NULL || - git_vector_init(&statuslist->paired, 0, NULL) < 0) + git_vector_init(&statuslist->paired, 0, entrycmp) < 0) return NULL; return statuslist; } +static int newfile_cmp(const void *a, const void *b) +{ + const git_diff_delta *delta_a = a; + const git_diff_delta *delta_b = b; + + return git__strcmp(delta_a->new_file.path, delta_b->new_file.path); +} + +static int newfile_casecmp(const void *a, const void *b) +{ + const git_diff_delta *delta_a = a; + const git_diff_delta *delta_b = b; + + return git__strcasecmp(delta_a->new_file.path, delta_b->new_file.path); +} + int git_status_list_new( git_status_list **out, git_repository *repo, const git_status_options *opts) { + git_index *index = NULL; git_status_list *statuslist = NULL; git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options findopts_i2w = GIT_DIFF_FIND_OPTIONS_INIT; git_tree *head = NULL; git_status_show_t show = opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR; @@ -165,7 +222,8 @@ int git_status_list_new( GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options"); - if ((error = git_repository__ensure_not_bare(repo, "status")) < 0) + if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 || + (error = git_repository_index(&index, repo)) < 0) return error; /* if there is no HEAD, that's okay - we'll make an empty iterator */ @@ -173,7 +231,7 @@ int git_status_list_new( !(error == GIT_ENOTFOUND || error == GIT_EORPHANEDHEAD)) return error; - statuslist = git_status_list_alloc(); + statuslist = git_status_list_alloc(index); GITERR_CHECK_ALLOC(statuslist); memcpy(&statuslist->opts, opts, sizeof(git_status_options)); @@ -197,17 +255,23 @@ int git_status_list_new( if ((opts->flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES; - if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) { - error = git_diff_tree_to_index(&statuslist->head2idx, repo, head, NULL, &diffopt); + findopts_i2w.flags |= GIT_DIFF_FIND_FOR_UNTRACKED; - if (error < 0) + if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) { + if ((error = git_diff_tree_to_index(&statuslist->head2idx, repo, head, NULL, &diffopt)) < 0) + goto on_error; + + if ((opts->flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 && + (error = git_diff_find_similar(statuslist->head2idx, NULL)) < 0) goto on_error; } if (show != GIT_STATUS_SHOW_INDEX_ONLY) { - error = git_diff_index_to_workdir(&statuslist->idx2wd, repo, NULL, &diffopt); + if ((error = git_diff_index_to_workdir(&statuslist->idx2wd, repo, NULL, &diffopt)) < 0) + goto on_error; - if (error < 0) + if ((opts->flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 && + (error = git_diff_find_similar(statuslist->idx2wd, &findopts_i2w)) < 0) goto on_error; } @@ -219,9 +283,22 @@ int git_status_list_new( statuslist->head2idx = NULL; } - if ((error = git_diff__paired_foreach(statuslist->head2idx, statuslist->idx2wd, status_collect, statuslist)) < 0) + if ((opts->flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0) { + statuslist->head2idx->deltas._cmp = + (statuslist->head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 ? + newfile_casecmp : newfile_cmp; + + git_vector_sort(&statuslist->head2idx->deltas); + } + + if ((error = git_diff__paired_foreach(statuslist->head2idx, statuslist->idx2wd, + status_collect, statuslist)) < 0) goto on_error; + if ((opts->flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 || + (opts->flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0) + git_vector_sort(&statuslist->paired); + *out = statuslist; goto done; @@ -230,6 +307,7 @@ on_error: done: git_tree_free(head); + git_index_free(index); return error; } @@ -307,7 +385,7 @@ int git_status_foreach( { git_status_options opts = GIT_STATUS_OPTIONS_INIT; - opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; + opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; @@ -364,7 +442,7 @@ int git_status_file( if (index->ignore_case) sfi.fnm_flags = FNM_CASEFOLD; - opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; + opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS | GIT_STATUS_OPT_INCLUDE_UNTRACKED | diff --git a/tests-clar/status/renames.c b/tests-clar/status/renames.c new file mode 100644 index 000000000..d29c7bfe8 --- /dev/null +++ b/tests-clar/status/renames.c @@ -0,0 +1,265 @@ +#include "clar_libgit2.h" +#include "buffer.h" +#include "path.h" +#include "posix.h" +#include "status_helpers.h" +#include "util.h" +#include "status.h" + +static git_repository *g_repo = NULL; + +void test_status_renames__initialize(void) +{ + g_repo = cl_git_sandbox_init("renames"); +} + +void test_status_renames__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void rename_file(git_repository *repo, const char *oldname, const char *newname) +{ + git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT; + + git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname); + git_buf_joinpath(&newpath, git_repository_workdir(repo), newname); + + cl_git_pass(p_rename(oldpath.ptr, newpath.ptr)); + + git_buf_free(&oldpath); + git_buf_free(&newpath); +} + +static void rename_and_edit_file(git_repository *repo, const char *oldname, const char *newname) +{ + git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT; + + git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname); + git_buf_joinpath(&newpath, git_repository_workdir(repo), newname); + + cl_git_pass(p_rename(oldpath.ptr, newpath.ptr)); + cl_git_append2file(newpath.ptr, "Added at the end to keep similarity!"); + + git_buf_free(&oldpath); + git_buf_free(&newpath); +} + +struct status_entry { + git_status_t status; + const char *oldname; + const char *newname; +}; + +static void test_status( + git_status_list *status_list, + struct status_entry *expected_list, + size_t expected_len) +{ + const git_status_entry *actual; + const struct status_entry *expected; + const char *oldname, *newname; + size_t i; + + cl_assert(expected_len == git_status_list_entrycount(status_list)); + + for (i = 0; i < expected_len; i++) { + actual = git_status_byindex(status_list, i); + expected = &expected_list[i]; + + cl_assert(actual->status == expected->status); + + oldname = actual->head_to_index ? actual->head_to_index->old_file.path : + actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL; + + newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path : + actual->head_to_index ? actual->head_to_index->new_file.path : NULL; + + if (oldname) + cl_assert(git__strcmp(oldname, expected->oldname) == 0); + else + cl_assert(expected->oldname == NULL); + + if (newname) + cl_assert(git__strcmp(newname, expected->newname) == 0); + else + cl_assert(expected->newname == NULL); + } +} + +void test_status_renames__head2index_one(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "newname.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_file(g_repo, "ikeepsix.txt", "newname.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "newname.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 1); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__head2index_two(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "aaa.txt" }, + { GIT_STATUS_INDEX_RENAMED, "untimely.txt", "bbb.txt" }, + { GIT_STATUS_INDEX_RENAMED, "songof7cities.txt", "ccc.txt" }, + { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "ddd.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_file(g_repo, "ikeepsix.txt", "ddd.txt"); + rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt"); + rename_file(g_repo, "songof7cities.txt", "ccc.txt"); + rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_remove_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_remove_bypath(index, "untimely.txt")); + cl_git_pass(git_index_add_bypath(index, "ddd.txt")); + cl_git_pass(git_index_add_bypath(index, "aaa.txt")); + cl_git_pass(git_index_add_bypath(index, "ccc.txt")); + cl_git_pass(git_index_add_bypath(index, "bbb.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 4); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__index2workdir_one(void) +{ + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "newname.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + rename_file(g_repo, "ikeepsix.txt", "newname.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 1); + git_status_list_free(statuslist); +} + +void test_status_renames__index2workdir_two(void) +{ + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_WT_RENAMED, "sixserving.txt", "aaa.txt" }, + { GIT_STATUS_WT_RENAMED, "untimely.txt", "bbb.txt" }, + { GIT_STATUS_WT_RENAMED, "songof7cities.txt", "ccc.txt" }, + { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ddd.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + rename_file(g_repo, "ikeepsix.txt", "ddd.txt"); + rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt"); + rename_file(g_repo, "songof7cities.txt", "ccc.txt"); + rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 4); + git_status_list_free(statuslist); +} + +void test_status_renames__both_one(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "newname-workdir.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_file(g_repo, "ikeepsix.txt", "newname-index.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "newname-index.txt")); + cl_git_pass(git_index_write(index)); + + rename_file(g_repo, "newname-index.txt", "newname-workdir.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 1); + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__both_two(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ikeepsix-both.txt" }, + { GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "sixserving-index.txt" }, + { GIT_STATUS_WT_RENAMED, "songof7cities.txt", "songof7cities-workdir.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, "untimely.txt", "untimely-both.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_repository_index(&index, g_repo)); + + rename_and_edit_file(g_repo, "ikeepsix.txt", "ikeepsix-index.txt"); + rename_and_edit_file(g_repo, "sixserving.txt", "sixserving-index.txt"); + rename_file(g_repo, "untimely.txt", "untimely-index.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_remove_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_remove_bypath(index, "untimely.txt")); + cl_git_pass(git_index_add_bypath(index, "ikeepsix-index.txt")); + cl_git_pass(git_index_add_bypath(index, "sixserving-index.txt")); + cl_git_pass(git_index_add_bypath(index, "untimely-index.txt")); + cl_git_pass(git_index_write(index)); + + rename_and_edit_file(g_repo, "ikeepsix-index.txt", "ikeepsix-both.txt"); + rename_and_edit_file(g_repo, "songof7cities.txt", "songof7cities-workdir.txt"); + rename_file(g_repo, "untimely-index.txt", "untimely-both.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + test_status(statuslist, expected, 4); + git_status_list_free(statuslist); + + git_index_free(index); +} From e3b4a47c1ebd55931cb25bf5c2af821df9b0bffa Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 31 May 2013 16:30:09 -0500 Subject: [PATCH 342/384] git__strcasesort_cmp: strcasecmp sorting rules but requires strict equality --- src/util.c | 25 +++++++++++++++++++++++++ src/util.h | 2 ++ tests-clar/core/string.c | 13 +++++++++++++ 3 files changed, 40 insertions(+) diff --git a/src/util.c b/src/util.c index da15a039d..8536c9513 100644 --- a/src/util.c +++ b/src/util.c @@ -279,6 +279,31 @@ int git__strcasecmp(const char *a, const char *b) return (tolower(*a) - tolower(*b)); } +int git__strcasesort_cmp(const char *a, const char *b) +{ + int cmp = 0; + + const char *orig_a = a; + const char *orig_b = b; + + while (*a && *b) { + if (*a == *b) + ; + else if (tolower(*a) == tolower(*b)) { + if (!cmp) + cmp = (int)(*(const unsigned char *)a) - (int)(*(const unsigned char *)b); + } else + break; + + ++a, ++b; + } + + if (*a || *b) + return tolower(*a) - tolower(*b); + + return cmp; +} + int git__strncmp(const char *a, const char *b, size_t sz) { while (sz && *a && *b && *a == *b) diff --git a/src/util.h b/src/util.h index 1ef9e65b5..e0088399c 100644 --- a/src/util.h +++ b/src/util.h @@ -194,6 +194,8 @@ extern int git__strcasecmp(const char *a, const char *b); extern int git__strncmp(const char *a, const char *b, size_t sz); extern int git__strncasecmp(const char *a, const char *b, size_t sz); +extern int git__strcasesort_cmp(const char *a, const char *b); + #include "thread-utils.h" typedef struct { diff --git a/tests-clar/core/string.c b/tests-clar/core/string.c index bf6ec0a80..ec9575685 100644 --- a/tests-clar/core/string.c +++ b/tests-clar/core/string.c @@ -26,3 +26,16 @@ void test_core_string__1(void) cl_assert(git__suffixcmp("zaz", "ac") > 0); } +/* compare icase sorting with case equality */ +void test_core_string__2(void) +{ + cl_assert(git__strcasesort_cmp("", "") == 0); + cl_assert(git__strcasesort_cmp("foo", "foo") == 0); + cl_assert(git__strcasesort_cmp("foo", "bar") > 0); + cl_assert(git__strcasesort_cmp("bar", "foo") < 0); + cl_assert(git__strcasesort_cmp("foo", "FOO") > 0); + cl_assert(git__strcasesort_cmp("FOO", "foo") < 0); + cl_assert(git__strcasesort_cmp("foo", "BAR") > 0); + cl_assert(git__strcasesort_cmp("BAR", "foo") < 0); + cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0); +} From c9b18018fd537566e76308fd2fec67e483b1d201 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 13 Jun 2013 15:26:56 -0700 Subject: [PATCH 343/384] Fix some warnings --- src/util.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/util.c b/src/util.c index 8536c9513..c543a3d21 100644 --- a/src/util.c +++ b/src/util.c @@ -283,17 +283,14 @@ int git__strcasesort_cmp(const char *a, const char *b) { int cmp = 0; - const char *orig_a = a; - const char *orig_b = b; - while (*a && *b) { - if (*a == *b) - ; - else if (tolower(*a) == tolower(*b)) { + if (*a != *b) { + if (tolower(*a) != tolower(*b)) + break; + /* use case in sort order even if not in equivalence */ if (!cmp) - cmp = (int)(*(const unsigned char *)a) - (int)(*(const unsigned char *)b); - } else - break; + cmp = (int)(*(const uint8_t *)a) - (int)(*(const uint8_t *)b); + } ++a, ++b; } From 4e28e638ea016f5a5b35ba952d9f06a1108d6b5e Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 13 Jun 2013 15:27:30 -0700 Subject: [PATCH 344/384] Clarify some docs and minor reordering This simplifies some documentation and hopefully makes a couple of things easier to read. Also, this rearrages the order in this branch so that the overall diff against the trunk will hopefully be a bit cleaner. --- include/git2/status.h | 183 +++++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 93 deletions(-) diff --git a/include/git2/status.h b/include/git2/status.h index 64ce6756c..282b606cb 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -59,44 +59,20 @@ typedef enum { typedef int (*git_status_cb)( const char *path, unsigned int status_flags, void *payload); -/** - * Gather file statuses and run a callback for each one. - * - * The callback is passed the path of the file, the status (a combination of - * the `git_status_t` values above) and the `payload` data pointer passed - * into this function. - * - * If the callback returns a non-zero value, this function will stop looping - * and return GIT_EUSER. - * - * @param repo A repository object - * @param callback The function to call on each file - * @param payload Pointer to pass through to callback function - * @return 0 on success, GIT_EUSER on non-zero callback, or error code - */ -GIT_EXTERN(int) git_status_foreach( - git_repository *repo, - git_status_cb callback, - void *payload); - /** * For extended status, select the files on which to report status. * - * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This is the - * rough equivalent of `git status --porcelain` where each file - * will receive a callback indicating its status in the index and - * in the workdir. - * - GIT_STATUS_SHOW_INDEX_ONLY will only make callbacks for index - * side of status. The status of the index contents relative to - * the HEAD will be given. - * - GIT_STATUS_SHOW_WORKDIR_ONLY will only make callbacks for the - * workdir side of status, reporting the status of workdir content - * relative to the index. - * - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR behaves like index-only - * followed by workdir-only, causing two callbacks to be issued - * per file (first index then workdir). This is slightly more - * efficient than making separate calls. This makes it easier to - * emulate the output of a plain `git status`. + * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This roughly + * matches `git status --porcelain` where each file gets a callback + * indicating its status in the index and in the working directory. + * - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index + * comparison, not looking at working directory changes. + * - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to + * working directory comparison, not comparing the index to the HEAD. + * - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR runs index-only then workdir-only, + * issuing (up to) two callbacks per file (first index, then workdir). + * This is slightly more efficient than separate calls and can make it + * easier to emulate plain `git status` text output. */ typedef enum { GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0, @@ -111,30 +87,30 @@ typedef enum { * - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made * on untracked files. These will only be made if the workdir files are * included in the status "show" option. - * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should get - * callbacks. Again, these callbacks will only be made if the workdir - * files are included in the status "show" option. Right now, there is - * no option to include all files in directories that are ignored - * completely. + * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files get callbacks. + * Again, these callbacks will only be made if the workdir files are + * included in the status "show" option. * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be * made even on unmodified files. - * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories which - * appear to be submodules should just be skipped over. - * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the contents of - * untracked directories should be included in the status. Normally if - * an entire directory is new, then just the top-level directory will be - * included (with a trailing slash on the entry name). Given this flag, - * the directory itself will not be included, but all the files in it - * will. + * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that submodules should be + * skipped. This only applies if there are no pending typechanges to + * the submodule (either from or to another type). + * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that all files in + * untracked directories should be included. Normally if an entire + * directory is new, then just the top-level directory is included (with + * a trailing slash on the entry name). This flag says to include all + * of the individual files in the directory instead. * - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given path - * will be treated as a literal path, and not as a pathspec. + * should be treated as a literal path, and not as a pathspec pattern. * - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of * ignored directories should be included in the status. This is like * doing `git ls-files -o -i --exclude-standard` with core git. - * - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that items that are - * renamed in the index will be reported as renames. - * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates that items that - * are renamed in the working directory will be reported as renames. + * - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that rename detection + * should be processed between the head and the index and enables + * the GIT_STATUS_INDEX_RENAMED as a possible status flag. + * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates tha rename + * detection should be run between the index and the working directory + * and enabled GIT_STATUS_WT_RENAMED as a possible status flag. * * Calling `git_status_foreach()` is like calling the extended version * with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED, @@ -181,6 +157,9 @@ typedef struct { git_strarray pathspec; } git_status_options; +#define GIT_STATUS_OPTIONS_VERSION 1 +#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION} + /** * A status entry, providing the differences between the file as it exists * in HEAD and the index, and providing the differences between the index @@ -201,8 +180,64 @@ typedef struct { git_diff_delta *index_to_workdir; } git_status_entry; -#define GIT_STATUS_OPTIONS_VERSION 1 -#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION} + +/** + * Gather file statuses and run a callback for each one. + * + * The callback is passed the path of the file, the status (a combination of + * the `git_status_t` values above) and the `payload` data pointer passed + * into this function. + * + * If the callback returns a non-zero value, this function will stop looping + * and return GIT_EUSER. + * + * @param repo A repository object + * @param callback The function to call on each file + * @param payload Pointer to pass through to callback function + * @return 0 on success, GIT_EUSER on non-zero callback, or error code + */ +GIT_EXTERN(int) git_status_foreach( + git_repository *repo, + git_status_cb callback, + void *payload); + +/** + * Gather file status information and run callbacks as requested. + * + * This is an extended version of the `git_status_foreach()` API that + * allows for more granular control over which paths will be processed and + * in what order. See the `git_status_options` structure for details + * about the additional controls that this makes available. + * + * @param repo Repository object + * @param opts Status options structure + * @param callback The function to call on each file + * @param payload Pointer to pass through to callback function + * @return 0 on success, GIT_EUSER on non-zero callback, or error code + */ +GIT_EXTERN(int) git_status_foreach_ext( + git_repository *repo, + const git_status_options *opts, + git_status_cb callback, + void *payload); + +/** + * Get file status for a single file. + * + * This is not quite the same as calling `git_status_foreach_ext()` with + * the pathspec set to the specified path. + * + * @param status_flags The status value for the file + * @param repo A repository object + * @param path The file to retrieve status for, rooted at the repo's workdir + * @return 0 on success, GIT_ENOTFOUND if the file is not found in the HEAD, + * index, and work tree, GIT_EINVALIDPATH if `path` points at a folder, + * GIT_EAMBIGUOUS if "path" matches multiple files, -1 on other error. + */ +GIT_EXTERN(int) git_status_file( + unsigned int *status_flags, + git_repository *repo, + const char *path); /** * Gather file status information and populate the `git_status_list`. @@ -247,44 +282,6 @@ GIT_EXTERN(const git_status_entry *) git_status_byindex( GIT_EXTERN(void) git_status_list_free( git_status_list *statuslist); -/** - * Gather file status information and run callbacks as requested. - * - * This is an extended version of the `git_status_foreach()` API that - * allows for more granular control over which paths will be processed and - * in what order. See the `git_status_options` structure for details - * about the additional controls that this makes available. - * - * @param repo Repository object - * @param opts Status options structure - * @param callback The function to call on each file - * @param payload Pointer to pass through to callback function - * @return 0 on success, GIT_EUSER on non-zero callback, or error code - */ -GIT_EXTERN(int) git_status_foreach_ext( - git_repository *repo, - const git_status_options *opts, - git_status_cb callback, - void *payload); - -/** - * Get file status for a single file. - * - * This is not quite the same as calling `git_status_foreach_ext()` with - * the pathspec set to the specified path. - * - * @param status_flags The status value for the file - * @param repo A repository object - * @param path The file to retrieve status for, rooted at the repo's workdir - * @return 0 on success, GIT_ENOTFOUND if the file is not found in the HEAD, - * index, and work tree, GIT_EINVALIDPATH if `path` points at a folder, - * GIT_EAMBIGUOUS if "path" matches multiple files, -1 on other error. - */ -GIT_EXTERN(int) git_status_file( - unsigned int *status_flags, - git_repository *repo, - const char *path); - /** * Test if the ignore rules apply to a given file. * From 3a68d7f00289afbaa415c3b34d5eeca183dcfb52 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 13 Jun 2013 15:30:26 -0700 Subject: [PATCH 345/384] Fix broken status EXCLUDE_SUBMODULES logic The exclude submodules flag was not doing the right thing, in that a file with no diff between the head and the index and just a delete in the workdir could be excluded if submodules were excluded. --- src/status.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/status.c b/src/status.c index 20e45b75f..ba4eef895 100644 --- a/src/status.c +++ b/src/status.c @@ -81,23 +81,33 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) } static bool status_is_included( - git_status_list *statuslist, + git_status_list *status, git_diff_delta *head2idx, git_diff_delta *idx2wd) { - /* if excluding submodules and this is a submodule everywhere */ - if ((statuslist->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) { - bool in_tree = (head2idx && head2idx->status != GIT_DELTA_ADDED); - bool in_index = (head2idx && head2idx->status != GIT_DELTA_DELETED); - bool in_wd = (idx2wd && idx2wd->status != GIT_DELTA_DELETED); + if (!(status->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES)) + return 1; - if ((!in_tree || head2idx->old_file.mode == GIT_FILEMODE_COMMIT) && - (!in_index || head2idx->new_file.mode == GIT_FILEMODE_COMMIT) && - (!in_wd || idx2wd->new_file.mode == GIT_FILEMODE_COMMIT)) - return 0; + /* if excluding submodules and this is a submodule everywhere */ + if (head2idx) { + if (head2idx->status != GIT_DELTA_ADDED && + head2idx->old_file.mode != GIT_FILEMODE_COMMIT) + return 1; + if (head2idx->status != GIT_DELTA_DELETED && + head2idx->new_file.mode != GIT_FILEMODE_COMMIT) + return 1; + } + if (idx2wd) { + if (idx2wd->status != GIT_DELTA_ADDED && + idx2wd->old_file.mode != GIT_FILEMODE_COMMIT) + return 1; + if (idx2wd->status != GIT_DELTA_DELETED && + idx2wd->new_file.mode != GIT_FILEMODE_COMMIT) + return 1; } - return 1; + /* only get here if every valid mode is GIT_FILEMODE_COMMIT */ + return 0; } static git_status_t status_compute( From a3e8dbb40b87f66e1f5f4d1730f9989805822db0 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 13 Jun 2013 15:32:09 -0700 Subject: [PATCH 346/384] Be more careful about the path with diffs This makes diff more careful about picking the canonical path when generating a delta so that it won't accidentally pick up a case-mismatched path on a case-insensitive file system. This should make sure we use the "most accurate" case correct version of the path (i.e. from the tree if possible, or the index if need be). --- src/diff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index 3bfe149e3..feb77b59b 100644 --- a/src/diff.c +++ b/src/diff.c @@ -134,6 +134,7 @@ static int diff_delta__from_two( { git_diff_delta *delta; int notify_res; + const char *canonical_path = old_entry->path; if (status == GIT_DELTA_UNMODIFIED && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED)) @@ -153,7 +154,7 @@ static int diff_delta__from_two( new_mode = temp_mode; } - delta = diff_delta__alloc(diff, status, old_entry->path); + delta = diff_delta__alloc(diff, status, canonical_path); GITERR_CHECK_ALLOC(delta); git_oid_cpy(&delta->old_file.oid, &old_entry->oid); From 351888cf3dc3c3525faccb010adcf5c130ac5b16 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 13 Jun 2013 15:37:06 -0700 Subject: [PATCH 347/384] Improve case handling in git_diff__paired_foreach This commit reinstates some changes to git_diff__paired_foreach that were discarded during the rebase (because the diff_output.c file had gone away), and also adjusts the case insensitively logic slightly to hopefully deal with either mismatched icase diffs and other case insensitivity scenarios. --- src/diff.c | 78 ++++++++++----- src/diff.h | 1 + src/status.c | 173 ++++++++++++++++------------------ tests-clar/status/worktree.c | 48 ++++++++++ tests-clar/submodule/status.c | 7 +- 5 files changed, 193 insertions(+), 114 deletions(-) diff --git a/src/diff.c b/src/diff.c index feb77b59b..87189a49c 100644 --- a/src/diff.c +++ b/src/diff.c @@ -254,6 +254,13 @@ int git_diff_delta__cmp(const void *a, const void *b) return val ? val : ((int)da->status - (int)db->status); } +int git_diff_delta__casecmp(const void *a, const void *b) +{ + const git_diff_delta *da = a, *db = b; + int val = strcasecmp(diff_delta__path(da), diff_delta__path(db)); + return val ? val : ((int)da->status - (int)db->status); +} + bool git_diff_delta__should_skip( const git_diff_options *opts, const git_diff_delta *delta) { @@ -1197,51 +1204,78 @@ size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type) } int git_diff__paired_foreach( - git_diff_list *idx2head, - git_diff_list *wd2idx, - int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload), + git_diff_list *head2idx, + git_diff_list *idx2wd, + int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload), void *payload) { int cmp; - git_diff_delta *i2h, *w2i; + git_diff_delta *h2i, *i2w; size_t i, j, i_max, j_max; - int (*strcomp)(const char *, const char *); + int (*strcomp)(const char *, const char *) = git__strcmp; + bool icase_mismatch; - i_max = idx2head ? idx2head->deltas.length : 0; - j_max = wd2idx ? wd2idx->deltas.length : 0; + i_max = head2idx ? head2idx->deltas.length : 0; + j_max = idx2wd ? idx2wd->deltas.length : 0; - /* Get appropriate strcmp function */ - strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL; + /* At some point, tree-to-index diffs will probably never ignore case, + * even if that isn't true now. Index-to-workdir diffs may or may not + * ignore case, but the index filename for the idx2wd diff should + * still be using the canonical case-preserving name. + * + * Therefore the main thing we need to do here is make sure the diffs + * are traversed in a compatible order. To do this, we temporarily + * resort a mismatched diff to get the order correct. + */ + icase_mismatch = + (head2idx != NULL && idx2wd != NULL && + ((head2idx->opts.flags ^ idx2wd->opts.flags) & GIT_DIFF_DELTAS_ARE_ICASE)); - /* Assert both iterators use matching ignore-case. If this function ever - * supports merging diffs that are not sorted by the same function, then - * it will need to spool and sort on one of the results before merging - */ - if (idx2head && wd2idx) { - assert(idx2head->strcomp == wd2idx->strcomp); + /* force case-sensitive delta sort */ + if (icase_mismatch) { + if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) { + head2idx->deltas._cmp = git_diff_delta__cmp; + git_vector_sort(&head2idx->deltas); + } else { + idx2wd->deltas._cmp = git_diff_delta__cmp; + git_vector_sort(&idx2wd->deltas); + } } + else if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) + strcomp = git__strcasecmp; for (i = 0, j = 0; i < i_max || j < j_max; ) { - i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL; - w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL; + h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL; + i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL; - cmp = !w2i ? -1 : !i2h ? 1 : - strcomp(i2h->old_file.path, w2i->old_file.path); + cmp = !i2w ? -1 : !h2i ? 1 : + strcomp(h2i->new_file.path, i2w->old_file.path); if (cmp < 0) { - if (cb(i2h, NULL, payload)) + if (cb(h2i, NULL, payload)) return GIT_EUSER; i++; } else if (cmp > 0) { - if (cb(NULL, w2i, payload)) + if (cb(NULL, i2w, payload)) return GIT_EUSER; j++; } else { - if (cb(i2h, w2i, payload)) + if (cb(h2i, i2w, payload)) return GIT_EUSER; i++; j++; } } + /* restore case-insensitive delta sort */ + if (icase_mismatch) { + if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) { + head2idx->deltas._cmp = git_diff_delta__casecmp; + git_vector_sort(&head2idx->deltas); + } else { + idx2wd->deltas._cmp = git_diff_delta__casecmp; + git_vector_sort(&idx2wd->deltas); + } + } + return 0; } diff --git a/src/diff.h b/src/diff.h index ad12e7731..1536f79d7 100644 --- a/src/diff.h +++ b/src/diff.h @@ -74,6 +74,7 @@ extern void git_diff__cleanup_modes( extern void git_diff_list_addref(git_diff_list *diff); extern int git_diff_delta__cmp(const void *a, const void *b); +extern int git_diff_delta__casecmp(const void *a, const void *b); extern bool git_diff_delta__should_skip( const git_diff_options *opts, const git_diff_delta *delta); diff --git a/src/status.c b/src/status.c index ba4eef895..9c4aead74 100644 --- a/src/status.c +++ b/src/status.c @@ -130,12 +130,12 @@ static int status_collect( git_diff_delta *idx2wd, void *payload) { - git_status_list *statuslist = payload; + git_status_list *status = payload; git_status_entry *status_entry; - - if (!status_is_included(statuslist, head2idx, idx2wd)) + + if (!status_is_included(status, head2idx, idx2wd)) return 0; - + status_entry = git__malloc(sizeof(git_status_entry)); GITERR_CHECK_ALLOC(status_entry); @@ -143,9 +143,7 @@ static int status_collect( status_entry->head_to_index = head2idx; status_entry->index_to_workdir = idx2wd; - git_vector_insert(&statuslist->paired, status_entry); - - return 0; + return git_vector_insert(&status->paired, status_entry); } GIT_INLINE(int) status_entry_cmp_base( @@ -184,18 +182,23 @@ static int status_entry_cmp(const void *a, const void *b) static git_status_list *git_status_list_alloc(git_index *index) { - git_status_list *statuslist = NULL; + git_status_list *status = NULL; int (*entrycmp)(const void *a, const void *b); + if (!(status = git__calloc(1, sizeof(git_status_list)))) + return NULL; + entrycmp = index->ignore_case ? status_entry_icmp : status_entry_cmp; - if ((statuslist = git__calloc(1, sizeof(git_status_list))) == NULL || - git_vector_init(&statuslist->paired, 0, entrycmp) < 0) + if (git_vector_init(&status->paired, 0, entrycmp) < 0) { + git__free(status); return NULL; + } - return statuslist; + return status; } +/* static int newfile_cmp(const void *a, const void *b) { const git_diff_delta *delta_a = a; @@ -211,6 +214,7 @@ static int newfile_casecmp(const void *a, const void *b) return git__strcasecmp(delta_a->new_file.path, delta_b->new_file.path); } +*/ int git_status_list_new( git_status_list **out, @@ -218,13 +222,14 @@ int git_status_list_new( const git_status_options *opts) { git_index *index = NULL; - git_status_list *statuslist = NULL; + git_status_list *status = NULL; git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT; git_diff_find_options findopts_i2w = GIT_DIFF_FIND_OPTIONS_INIT; git_tree *head = NULL; git_status_show_t show = opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR; int error = 0; + unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS; assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR); @@ -238,123 +243,121 @@ int git_status_list_new( /* if there is no HEAD, that's okay - we'll make an empty iterator */ if (((error = git_repository_head_tree(&head, repo)) < 0) && - !(error == GIT_ENOTFOUND || error == GIT_EORPHANEDHEAD)) + error != GIT_ENOTFOUND && error != GIT_EORPHANEDHEAD) { + git_index_free(index); /* release index */ return error; + } - statuslist = git_status_list_alloc(index); - GITERR_CHECK_ALLOC(statuslist); + status = git_status_list_alloc(index); + GITERR_CHECK_ALLOC(status); - memcpy(&statuslist->opts, opts, sizeof(git_status_options)); - - memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); + if (opts) { + memcpy(&status->opts, opts, sizeof(git_status_options)); + memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); + } diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE; - if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0) + if ((flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED; - if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0) + if ((flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED; - if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0) + if ((flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED; - if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0) + if ((flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS; - if ((opts->flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0) + if ((flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH; - if ((opts->flags & GIT_STATUS_OPT_RECURSE_IGNORED_DIRS) != 0) + if ((flags & GIT_STATUS_OPT_RECURSE_IGNORED_DIRS) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS; - if ((opts->flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) + if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES; findopts_i2w.flags |= GIT_DIFF_FIND_FOR_UNTRACKED; if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) { - if ((error = git_diff_tree_to_index(&statuslist->head2idx, repo, head, NULL, &diffopt)) < 0) - goto on_error; + if ((error = git_diff_tree_to_index( + &status->head2idx, repo, head, NULL, &diffopt)) < 0) + goto done; - if ((opts->flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 && - (error = git_diff_find_similar(statuslist->head2idx, NULL)) < 0) - goto on_error; + if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 && + (error = git_diff_find_similar(status->head2idx, NULL)) < 0) + goto done; } if (show != GIT_STATUS_SHOW_INDEX_ONLY) { - if ((error = git_diff_index_to_workdir(&statuslist->idx2wd, repo, NULL, &diffopt)) < 0) - goto on_error; + if ((error = git_diff_index_to_workdir( + &status->idx2wd, repo, NULL, &diffopt)) < 0) + goto done; - if ((opts->flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 && - (error = git_diff_find_similar(statuslist->idx2wd, &findopts_i2w)) < 0) - goto on_error; + if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 && + (error = git_diff_find_similar(status->idx2wd, &findopts_i2w)) < 0) + goto done; } if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) { - if ((error = git_diff__paired_foreach(statuslist->head2idx, NULL, status_collect, statuslist)) < 0) - goto on_error; + if ((error = git_diff__paired_foreach( + status->head2idx, NULL, status_collect, status)) < 0) + goto done; - git_diff_list_free(statuslist->head2idx); - statuslist->head2idx = NULL; + git_diff_list_free(status->head2idx); + status->head2idx = NULL; } - if ((opts->flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0) { - statuslist->head2idx->deltas._cmp = - (statuslist->head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 ? - newfile_casecmp : newfile_cmp; + if ((error = git_diff__paired_foreach( + status->head2idx, status->idx2wd, status_collect, status)) < 0) + goto done; - git_vector_sort(&statuslist->head2idx->deltas); - } - - if ((error = git_diff__paired_foreach(statuslist->head2idx, statuslist->idx2wd, - status_collect, statuslist)) < 0) - goto on_error; - - if ((opts->flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 || - (opts->flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0) - git_vector_sort(&statuslist->paired); - - *out = statuslist; - goto done; - -on_error: - git_status_list_free(statuslist); + if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 || + (flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0) + git_vector_sort(&status->paired); done: + if (error < 0) { + git_status_list_free(status); + status = NULL; + } + + *out = status; + git_tree_free(head); git_index_free(index); return error; } -size_t git_status_list_entrycount(git_status_list *statuslist) +size_t git_status_list_entrycount(git_status_list *status) { - assert(statuslist); + assert(status); - return statuslist->paired.length; + return status->paired.length; } -const git_status_entry *git_status_byindex( - git_status_list *statuslist, - size_t i) +const git_status_entry *git_status_byindex(git_status_list *status, size_t i) { - assert(statuslist); + assert(status); - return git_vector_get(&statuslist->paired, i); + return git_vector_get(&status->paired, i); } -void git_status_list_free(git_status_list *statuslist) +void git_status_list_free(git_status_list *status) { git_status_entry *status_entry; size_t i; - if (statuslist == NULL) + if (status == NULL) return; - git_diff_list_free(statuslist->head2idx); - git_diff_list_free(statuslist->idx2wd); + git_diff_list_free(status->head2idx); + git_diff_list_free(status->idx2wd); - git_vector_foreach(&statuslist->paired, i, status_entry) + git_vector_foreach(&status->paired, i, status_entry) git__free(status_entry); - git_vector_free(&statuslist->paired); + git_vector_free(&status->paired); - git__free(statuslist); + git__memzero(status, sizeof(*status)); + git__free(status); } int git_status_foreach_ext( @@ -363,15 +366,15 @@ int git_status_foreach_ext( git_status_cb cb, void *payload) { - git_status_list *statuslist; + git_status_list *status; const git_status_entry *status_entry; size_t i; int error = 0; - if ((error = git_status_list_new(&statuslist, repo, opts)) < 0) + if ((error = git_status_list_new(&status, repo, opts)) < 0) return error; - git_vector_foreach(&statuslist->paired, i, status_entry) { + git_vector_foreach(&status->paired, i, status_entry) { const char *path = status_entry->head_to_index ? status_entry->head_to_index->old_file.path : status_entry->index_to_workdir->old_file.path; @@ -383,24 +386,14 @@ int git_status_foreach_ext( } } - git_status_list_free(statuslist); + git_status_list_free(status); return error; } -int git_status_foreach( - git_repository *repo, - git_status_cb callback, - void *payload) +int git_status_foreach(git_repository *repo, git_status_cb cb, void *payload) { - git_status_options opts = GIT_STATUS_OPTIONS_INIT; - - opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; - opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED | - GIT_STATUS_OPT_INCLUDE_UNTRACKED | - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; - - return git_status_foreach_ext(repo, &opts, callback, payload); + return git_status_foreach_ext(repo, NULL, cb, payload); } struct status_file_info { diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index 13335843b..7c27ee588 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -695,3 +695,51 @@ void test_status_worktree__file_status_honors_case_ignorecase_regarding_untracke /* Actually returns GIT_STATUS_IGNORED on Windows */ cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND); } + +void test_status_worktree__simple_delete(void) +{ + git_repository *repo = cl_git_sandbox_init("renames"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + int count; + + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH | + GIT_STATUS_OPT_EXCLUDE_SUBMODULES | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + + count = 0; + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__count, &count) ); + cl_assert_equal_i(0, count); + + cl_must_pass(p_unlink("renames/untimely.txt")); + + count = 0; + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__count, &count) ); + cl_assert_equal_i(1, count); +} + +void test_status_worktree__simple_delete_indexed(void) +{ + git_repository *repo = cl_git_sandbox_init("renames"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + git_status_list *status; + + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH | + GIT_STATUS_OPT_EXCLUDE_SUBMODULES | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + + cl_git_pass(git_status_list_new(&status, repo, &opts)); + cl_assert_equal_sz(0, git_status_list_entrycount(status)); + git_status_list_free(status); + + cl_must_pass(p_unlink("renames/untimely.txt")); + + cl_git_pass(git_status_list_new(&status, repo, &opts)); + cl_assert_equal_sz(1, git_status_list_entrycount(status)); + cl_assert_equal_i( + GIT_STATUS_WT_DELETED, git_status_byindex(status, 0)->status); + git_status_list_free(status); +} diff --git a/tests-clar/submodule/status.c b/tests-clar/submodule/status.c index 39c83a0b7..68110bdd5 100644 --- a/tests-clar/submodule/status.c +++ b/tests-clar/submodule/status.c @@ -376,9 +376,12 @@ void test_submodule_status__iterator(void) git_iterator_free(iter); - opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_INCLUDE_UNMODIFIED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; - cl_git_pass(git_status_foreach_ext(g_repo, &opts, confirm_submodule_status, &exp)); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, confirm_submodule_status, &exp)); } void test_submodule_status__untracked_dirs_containing_ignored_files(void) From 1540b19990fa5c566d607785c7b3651756e706ff Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 31 May 2013 18:12:49 -0500 Subject: [PATCH 348/384] some simple case-sensitive index tests --- tests-clar/diff/rename.c | 32 ++++++++++++++++++++++++++++++++ tests-clar/index/tests.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 224945e60..6227a54e8 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -906,3 +906,35 @@ void test_diff_rename__rejected_match_can_match_others(void) git_buf_free(&one); git_buf_free(&two); } + +void test_diff_rename__case_changes_are_split(void) +{ + git_index *index; + git_tree *tree; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_list *diff = NULL; + diff_expects exp; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/IKEEPSIX.txt")); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "IKEEPSIX.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, NULL)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); + + git_index_free(index); +} + diff --git a/tests-clar/index/tests.c b/tests-clar/index/tests.c index 88e374e6e..53d45a84e 100644 --- a/tests-clar/index/tests.c +++ b/tests-clar/index/tests.c @@ -416,3 +416,35 @@ void test_index_tests__remove_directory(void) git_repository_free(repo); cl_fixture_cleanup("index_test"); } + +void test_index_tests__preserves_case(void) +{ + git_repository *repo; + git_index *index; + const git_index_entry *entry; + + cl_set_cleanup(&cleanup_myrepo, NULL); + + cl_git_pass(git_repository_init(&repo, "./myrepo", 0)); + cl_git_pass(git_repository_index(&index, repo)); + + cl_git_rewritefile("myrepo/test.txt", "hey there\n"); + cl_git_pass(git_index_add_bypath(index, "test.txt")); + + cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt")); + cl_git_rewritefile("myrepo/TEST.txt", "hello again\n"); + cl_git_pass(git_index_add_bypath(index, "TEST.txt")); + + cl_assert(git_index_entrycount(index) == 1); + + /* Test access by path instead of index */ + cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL); + cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL); + + /* The path should *not* have changed without an explicit remove */ + cl_assert(git__strcmp(entry->path, "test.txt") == 0); + + git_index_free(index); + git_repository_free(repo); +} + From 6ea999bb88e1c5d0be17d823c23728d11adfed47 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 13 Jun 2013 15:52:12 -0700 Subject: [PATCH 349/384] Make index_insert keep existing case In a case insensitive index, if you attempt to add a file from disk with a different case pattern, the old case pattern in the index should be preserved. This fixes that (and a couple of minor warnings). --- src/index.c | 5 +++-- tests-clar/diff/rename.c | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.c b/src/index.c index 4f0c70135..560a257e7 100644 --- a/src/index.c +++ b/src/index.c @@ -734,8 +734,9 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace) if (!replace || !existing) return git_vector_insert(&index->entries, entry); - /* exists, replace it */ - git__free((*existing)->path); + /* exists, replace it (preserving name from existing entry) */ + git__free(entry->path); + entry->path = (*existing)->path; git__free(*existing); *existing = entry; diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 6227a54e8..c4b722314 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -911,7 +911,6 @@ void test_diff_rename__case_changes_are_split(void) { git_index *index; git_tree *tree; - git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_list *diff = NULL; diff_expects exp; From eefef642c8c0d9d527633294acdf9d7a0c9e94c0 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 13 Jun 2013 16:09:53 -0700 Subject: [PATCH 350/384] Always do tree to index diffs case sensitively Trees are always case sensitive. The index is always case preserving and will be case sensitive when it is turned into a tree. Therefore the tree and the index can and should always be compared to one another case sensitively. This will restore the index to case insensitive order after the diff has been generated. Consider this a short-term fix. The long term fix is to have the index always stored both case sensitively and case insensitively (at least on platforms that sometimes require case insensitivity). --- src/diff.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/diff.c b/src/diff.c index 87189a49c..fa2c5c71d 100644 --- a/src/diff.c +++ b/src/diff.c @@ -364,6 +364,8 @@ static git_diff_list *diff_list_alloc( diff->strncomp = git__strncasecmp; diff->pfxcomp = git__prefixcmp_icase; diff->entrycomp = git_index_entry__cmp_icase; + + diff->deltas._cmp = git_diff_delta__casecmp; } return diff; @@ -1127,17 +1129,40 @@ int git_diff_tree_to_index( const git_diff_options *opts) { int error = 0; + bool reset_index_ignore_case = false; assert(diff && repo); if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0) return error; + if (index->ignore_case) { + git_index__set_ignore_case(index, false); + reset_index_ignore_case = true; + } + DIFF_FROM_ITERATORS( git_iterator_for_tree(&a, old_tree, 0, pfx, pfx), git_iterator_for_index(&b, index, 0, pfx, pfx) ); + if (reset_index_ignore_case) { + git_index__set_ignore_case(index, true); + + if (!error) { + git_diff_list *d = *diff; + + d->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE; + d->strcomp = git__strcasecmp; + d->strncomp = git__strncasecmp; + d->pfxcomp = git__prefixcmp_icase; + d->entrycomp = git_index_entry__cmp_icase; + + d->deltas._cmp = git_diff_delta__casecmp; + git_vector_sort(&d->deltas); + } + } + return error; } From fb03a223189f418d0767d7d04ff7509dfcfe8394 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 13 Jun 2013 16:31:11 -0700 Subject: [PATCH 351/384] Test has to work on case sensitive systems --- tests-clar/index/tests.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests-clar/index/tests.c b/tests-clar/index/tests.c index 53d45a84e..1bc5e6a07 100644 --- a/tests-clar/index/tests.c +++ b/tests-clar/index/tests.c @@ -422,12 +422,15 @@ void test_index_tests__preserves_case(void) git_repository *repo; git_index *index; const git_index_entry *entry; + int index_caps; cl_set_cleanup(&cleanup_myrepo, NULL); cl_git_pass(git_repository_init(&repo, "./myrepo", 0)); cl_git_pass(git_repository_index(&index, repo)); + index_caps = git_index_caps(index); + cl_git_rewritefile("myrepo/test.txt", "hey there\n"); cl_git_pass(git_index_add_bypath(index, "test.txt")); @@ -435,15 +438,23 @@ void test_index_tests__preserves_case(void) cl_git_rewritefile("myrepo/TEST.txt", "hello again\n"); cl_git_pass(git_index_add_bypath(index, "TEST.txt")); - cl_assert(git_index_entrycount(index) == 1); + if (index_caps & GIT_INDEXCAP_IGNORE_CASE) + cl_assert_equal_i(1, (int)git_index_entrycount(index)); + else + cl_assert_equal_i(2, (int)git_index_entrycount(index)); /* Test access by path instead of index */ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL); - cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL); - /* The path should *not* have changed without an explicit remove */ cl_assert(git__strcmp(entry->path, "test.txt") == 0); + cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL); + if (index_caps & GIT_INDEXCAP_IGNORE_CASE) + /* The path should *not* have changed without an explicit remove */ + cl_assert(git__strcmp(entry->path, "test.txt") == 0); + else + cl_assert(git__strcmp(entry->path, "TEST.txt") == 0); + git_index_free(index); git_repository_free(repo); } From a1683f28ce2709e615490939e4e244046654d0e5 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 14 Jun 2013 16:18:04 -0700 Subject: [PATCH 352/384] More tests and bug fixes for status with rename This changes the behavior of the status RENAMED flags so that they will be combined with the MODIFIED flags if appropriate. If a file is modified in the index and also renamed, then the status code will have both the GIT_STATUS_INDEX_MODIFIED and INDEX_RENAMED bits set. If it is renamed but the OID has not changed, then just the GIT_STATUS_INDEX_RENAMED bit will be set. Similarly, the flags GIT_STATUS_WT_MODIFIED and GIT_STATUS_WT_RENAMED can both be set independently of one another. This fixes a serious bug where the check for unmodified files that was done at data load time could end up erasing the RENAMED state of a file that was renamed with no changes. Lastly, this contains a bunch of new tests for status with renames, including tests where the only rename changes are case changes. The expected results of these tests have to vary by whether the platform uses a case sensitive filesystem or not, so the expected data covers those platform differences separately. --- src/diff.h | 9 +-- src/diff_patch.c | 6 +- src/diff_print.c | 109 ++++++++++++++------------- src/diff_tform.c | 2 +- src/status.c | 49 ++++++++++--- tests-clar/status/renames.c | 142 +++++++++++++++++++++++++++++++++--- 6 files changed, 237 insertions(+), 80 deletions(-) diff --git a/src/diff.h b/src/diff.h index 1536f79d7..6ef03ee7c 100644 --- a/src/diff.h +++ b/src/diff.h @@ -95,17 +95,16 @@ extern int git_diff__paired_foreach( int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload), void *payload); -int git_diff_find_similar__hashsig_for_file( +extern int git_diff_find_similar__hashsig_for_file( void **out, const git_diff_file *f, const char *path, void *p); -int git_diff_find_similar__hashsig_for_buf( +extern int git_diff_find_similar__hashsig_for_buf( void **out, const git_diff_file *f, const char *buf, size_t len, void *p); -void git_diff_find_similar__hashsig_free(void *sig, void *payload); +extern void git_diff_find_similar__hashsig_free(void *sig, void *payload); -int git_diff_find_similar__calc_similarity( +extern int git_diff_find_similar__calc_similarity( int *score, void *siga, void *sigb, void *payload); - #endif diff --git a/src/diff_patch.c b/src/diff_patch.c index a1e1fe84c..40cb3371a 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -175,10 +175,11 @@ static int diff_patch_load(git_diff_patch *patch, git_diff_output *output) goto cleanup; } - /* if we were previously missing an oid, reassess UNMODIFIED state */ + /* if we were previously missing an oid, update MODIFIED->UNMODIFIED */ if (incomplete_data && patch->ofile.file.mode == patch->nfile.file.mode && - git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid)) + git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid) && + patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */ patch->delta->status = GIT_DELTA_UNMODIFIED; cleanup: @@ -284,6 +285,7 @@ int git_diff_foreach( git_xdiff_init(&xo, &diff->opts); git_vector_foreach(&diff->deltas, idx, patch.delta) { + /* check flags against patch status */ if (git_diff_delta__should_skip(&diff->opts, patch.delta)) continue; diff --git a/src/diff_print.c b/src/diff_print.c index 244aa6e1d..6fc7425eb 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -41,7 +41,7 @@ static int diff_print_info_init( return 0; } -static char pick_suffix(int mode) +static char diff_pick_suffix(int mode) { if (S_ISDIR(mode)) return '/'; @@ -76,10 +76,11 @@ static int callback_error(void) return GIT_EUSER; } -static int print_compact( +static int diff_print_one_compact( const git_diff_delta *delta, float progress, void *data) { diff_print_info *pi = data; + git_buf *out = pi->buf; char old_suffix, new_suffix, code = git_diff_status_char(delta->status); GIT_UNUSED(progress); @@ -87,34 +88,35 @@ static int print_compact( if (code == ' ') return 0; - old_suffix = pick_suffix(delta->old_file.mode); - new_suffix = pick_suffix(delta->new_file.mode); + old_suffix = diff_pick_suffix(delta->old_file.mode); + new_suffix = diff_pick_suffix(delta->new_file.mode); - git_buf_clear(pi->buf); + git_buf_clear(out); if (delta->old_file.path != delta->new_file.path && pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0) - git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code, + git_buf_printf(out, "%c\t%s%c -> %s%c\n", code, delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); else if (delta->old_file.mode != delta->new_file.mode && delta->old_file.mode != 0 && delta->new_file.mode != 0) - git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code, + git_buf_printf(out, "%c\t%s%c (%o -> %o)\n", code, delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode); else if (old_suffix != ' ') - git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); + git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); else - git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path); + git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path); - if (git_buf_oom(pi->buf)) + if (git_buf_oom(out)) return -1; if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, - git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) + git_buf_cstr(out), git_buf_len(out), pi->payload)) return callback_error(); return 0; } +/* print a git_diff_list to a print callback in compact format */ int git_diff_print_compact( git_diff_list *diff, git_diff_data_cb print_cb, @@ -125,17 +127,18 @@ int git_diff_print_compact( diff_print_info pi; if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) - error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi); + error = git_diff_foreach(diff, diff_print_one_compact, NULL, NULL, &pi); git_buf_free(&buf); return error; } -static int print_raw( +static int diff_print_one_raw( const git_diff_delta *delta, float progress, void *data) { diff_print_info *pi = data; + git_buf *out = pi->buf; char code = git_diff_status_char(delta->status); char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; @@ -144,36 +147,37 @@ static int print_raw( if (code == ' ') return 0; - git_buf_clear(pi->buf); + git_buf_clear(out); git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid); git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid); git_buf_printf( - pi->buf, ":%06o %06o %s... %s... %c", + out, ":%06o %06o %s... %s... %c", delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); if (delta->similarity > 0) - git_buf_printf(pi->buf, "%03u", delta->similarity); + git_buf_printf(out, "%03u", delta->similarity); if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED) git_buf_printf( - pi->buf, "\t%s %s\n", delta->old_file.path, delta->new_file.path); + out, "\t%s %s\n", delta->old_file.path, delta->new_file.path); else git_buf_printf( - pi->buf, "\t%s\n", delta->old_file.path ? + out, "\t%s\n", delta->old_file.path ? delta->old_file.path : delta->new_file.path); - if (git_buf_oom(pi->buf)) + if (git_buf_oom(out)) return -1; if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR, - git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload)) + git_buf_cstr(out), git_buf_len(out), pi->payload)) return callback_error(); return 0; } +/* print a git_diff_list to a print callback in raw output format */ int git_diff_print_raw( git_diff_list *diff, git_diff_data_cb print_cb, @@ -184,15 +188,16 @@ int git_diff_print_raw( diff_print_info pi; if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) - error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi); + error = git_diff_foreach(diff, diff_print_one_raw, NULL, NULL, &pi); git_buf_free(&buf); return error; } -static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta) +static int diff_print_oid_range(diff_print_info *pi, const git_diff_delta *delta) { + git_buf *out = pi->buf; char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid); @@ -200,27 +205,27 @@ static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta) /* TODO: Match git diff more closely */ if (delta->old_file.mode == delta->new_file.mode) { - git_buf_printf(pi->buf, "index %s..%s %o\n", + git_buf_printf(out, "index %s..%s %o\n", start_oid, end_oid, delta->old_file.mode); } else { if (delta->old_file.mode == 0) { - git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode); + git_buf_printf(out, "new file mode %o\n", delta->new_file.mode); } else if (delta->new_file.mode == 0) { - git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode); + git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode); } else { - git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode); - git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode); + git_buf_printf(out, "old mode %o\n", delta->old_file.mode); + git_buf_printf(out, "new mode %o\n", delta->new_file.mode); } - git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid); + git_buf_printf(out, "index %s..%s\n", start_oid, end_oid); } - if (git_buf_oom(pi->buf)) + if (git_buf_oom(out)) return -1; return 0; } -static int print_patch_file( +static int diff_print_patch_file( const git_diff_delta *delta, float progress, void *data) { diff_print_info *pi = data; @@ -247,7 +252,7 @@ static int print_patch_file( git_buf_clear(pi->buf); git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path); - if (print_oid_range(pi, delta) < 0) + if (diff_print_oid_range(pi, delta) < 0) return -1; if (git_oid_iszero(&delta->old_file.oid)) { @@ -288,7 +293,7 @@ static int print_patch_file( return 0; } -static int print_patch_hunk( +static int diff_print_patch_hunk( const git_diff_delta *d, const git_diff_range *r, const char *header, @@ -311,7 +316,7 @@ static int print_patch_hunk( return 0; } -static int print_patch_line( +static int diff_print_patch_line( const git_diff_delta *delta, const git_diff_range *range, char line_origin, /* GIT_DIFF_LINE value from above */ @@ -343,6 +348,7 @@ static int print_patch_line( return 0; } +/* print a git_diff_list to an output callback in patch format */ int git_diff_print_patch( git_diff_list *diff, git_diff_data_cb print_cb, @@ -354,27 +360,15 @@ int git_diff_print_patch( if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload))) error = git_diff_foreach( - diff, print_patch_file, print_patch_hunk, print_patch_line, &pi); + diff, diff_print_patch_file, diff_print_patch_hunk, + diff_print_patch_line, &pi); git_buf_free(&buf); return error; } - -static int print_to_buffer_cb( - const git_diff_delta *delta, - const git_diff_range *range, - char line_origin, - const char *content, - size_t content_len, - void *payload) -{ - git_buf *output = payload; - GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin); - return git_buf_put(output, content, content_len); -} - +/* print a git_diff_patch to an output callback */ int git_diff_patch_print( git_diff_patch *patch, git_diff_data_cb print_cb, @@ -389,13 +383,28 @@ int git_diff_patch_print( if (!(error = diff_print_info_init( &pi, &temp, git_diff_patch__diff(patch), print_cb, payload))) error = git_diff_patch__invoke_callbacks( - patch, print_patch_file, print_patch_hunk, print_patch_line, &pi); + patch, diff_print_patch_file, diff_print_patch_hunk, + diff_print_patch_line, &pi); git_buf_free(&temp); return error; } +static int diff_print_to_buffer_cb( + const git_diff_delta *delta, + const git_diff_range *range, + char line_origin, + const char *content, + size_t content_len, + void *payload) +{ + git_buf *output = payload; + GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin); + return git_buf_put(output, content, content_len); +} + +/* print a git_diff_patch to a string buffer */ int git_diff_patch_to_str( char **string, git_diff_patch *patch) @@ -403,7 +412,7 @@ int git_diff_patch_to_str( int error; git_buf output = GIT_BUF_INIT; - error = git_diff_patch_print(patch, print_to_buffer_cb, &output); + error = git_diff_patch_print(patch, diff_print_to_buffer_cb, &output); /* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1, * meaning a memory allocation failure, so just map to -1... diff --git a/src/diff_tform.c b/src/diff_tform.c index 94fa035f2..64746e7dd 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -483,7 +483,7 @@ static int similarity_measure( if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode)) return 0; - /* if exact match is requested, force calculation of missing OIDs */ + /* if exact match is requested, force calculation of missing OIDs now */ if (exact_match) { if (git_oid_iszero(&a_file->oid) && diff->old_src == GIT_ITERATOR_TYPE_WORKDIR && diff --git a/src/status.c b/src/status.c index 9c4aead74..375100a89 100644 --- a/src/status.c +++ b/src/status.c @@ -20,11 +20,11 @@ #include "git2/diff.h" #include "diff.h" -static unsigned int index_delta2status(git_delta_t index_status) +static unsigned int index_delta2status(const git_diff_delta *head2idx) { - unsigned int st = GIT_STATUS_CURRENT; + git_status_t st = GIT_STATUS_CURRENT; - switch (index_status) { + switch (head2idx->status) { case GIT_DELTA_ADDED: case GIT_DELTA_COPIED: st = GIT_STATUS_INDEX_NEW; @@ -37,6 +37,9 @@ static unsigned int index_delta2status(git_delta_t index_status) break; case GIT_DELTA_RENAMED: st = GIT_STATUS_INDEX_RENAMED; + + if (!git_oid_equal(&head2idx->old_file.oid, &head2idx->new_file.oid)) + st |= GIT_STATUS_INDEX_MODIFIED; break; case GIT_DELTA_TYPECHANGE: st = GIT_STATUS_INDEX_TYPECHANGE; @@ -48,11 +51,12 @@ static unsigned int index_delta2status(git_delta_t index_status) return st; } -static unsigned int workdir_delta2status(git_delta_t workdir_status) +static unsigned int workdir_delta2status( + git_diff_list *diff, git_diff_delta *idx2wd) { - unsigned int st = GIT_STATUS_CURRENT; + git_status_t st = GIT_STATUS_CURRENT; - switch (workdir_status) { + switch (idx2wd->status) { case GIT_DELTA_ADDED: case GIT_DELTA_COPIED: case GIT_DELTA_UNTRACKED: @@ -69,6 +73,28 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) break; case GIT_DELTA_RENAMED: st = GIT_STATUS_WT_RENAMED; + + if (!git_oid_equal(&idx2wd->old_file.oid, &idx2wd->new_file.oid)) { + /* if OIDs don't match, we might need to calculate them now to + * discern between RENAMED vs RENAMED+MODIFED + */ + if (git_oid_iszero(&idx2wd->old_file.oid) && + diff->old_src == GIT_ITERATOR_TYPE_WORKDIR && + !git_diff__oid_for_file( + diff->repo, idx2wd->old_file.path, idx2wd->old_file.mode, + idx2wd->old_file.size, &idx2wd->old_file.oid)) + idx2wd->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; + + if (git_oid_iszero(&idx2wd->new_file.oid) && + diff->new_src == GIT_ITERATOR_TYPE_WORKDIR && + !git_diff__oid_for_file( + diff->repo, idx2wd->new_file.path, idx2wd->new_file.mode, + idx2wd->new_file.size, &idx2wd->new_file.oid)) + idx2wd->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; + + if (!git_oid_equal(&idx2wd->old_file.oid, &idx2wd->new_file.oid)) + st |= GIT_STATUS_WT_MODIFIED; + } break; case GIT_DELTA_TYPECHANGE: st = GIT_STATUS_WT_TYPECHANGE; @@ -111,18 +137,19 @@ static bool status_is_included( } static git_status_t status_compute( + git_status_list *status, git_diff_delta *head2idx, git_diff_delta *idx2wd) { - git_status_t status = 0; + git_status_t st = GIT_STATUS_CURRENT; if (head2idx) - status |= index_delta2status(head2idx->status); + st |= index_delta2status(head2idx); if (idx2wd) - status |= workdir_delta2status(idx2wd->status); + st |= workdir_delta2status(status->idx2wd, idx2wd); - return status; + return st; } static int status_collect( @@ -139,7 +166,7 @@ static int status_collect( status_entry = git__malloc(sizeof(git_status_entry)); GITERR_CHECK_ALLOC(status_entry); - status_entry->status = status_compute(head2idx, idx2wd); + status_entry->status = status_compute(status, head2idx, idx2wd); status_entry->head_to_index = head2idx; status_entry->index_to_workdir = idx2wd; diff --git a/tests-clar/status/renames.c b/tests-clar/status/renames.c index d29c7bfe8..80ff26020 100644 --- a/tests-clar/status/renames.c +++ b/tests-clar/status/renames.c @@ -61,13 +61,13 @@ static void test_status( const char *oldname, *newname; size_t i; - cl_assert(expected_len == git_status_list_entrycount(status_list)); + cl_assert_equal_sz(expected_len, git_status_list_entrycount(status_list)); for (i = 0; i < expected_len; i++) { actual = git_status_byindex(status_list, i); expected = &expected_list[i]; - cl_assert(actual->status == expected->status); + cl_assert_equal_i((int)expected->status, (int)actual->status); oldname = actual->head_to_index ? actual->head_to_index->old_file.path : actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL; @@ -119,8 +119,10 @@ void test_status_renames__head2index_two(void) git_status_list *statuslist; git_status_options opts = GIT_STATUS_OPTIONS_INIT; struct status_entry expected[] = { - { GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "aaa.txt" }, - { GIT_STATUS_INDEX_RENAMED, "untimely.txt", "bbb.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED, + "sixserving.txt", "aaa.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED, + "untimely.txt", "bbb.txt" }, { GIT_STATUS_INDEX_RENAMED, "songof7cities.txt", "ccc.txt" }, { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "ddd.txt" }, }; @@ -174,8 +176,10 @@ void test_status_renames__index2workdir_two(void) git_status_list *statuslist; git_status_options opts = GIT_STATUS_OPTIONS_INIT; struct status_entry expected[] = { - { GIT_STATUS_WT_RENAMED, "sixserving.txt", "aaa.txt" }, - { GIT_STATUS_WT_RENAMED, "untimely.txt", "bbb.txt" }, + { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, + "sixserving.txt", "aaa.txt" }, + { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, + "untimely.txt", "bbb.txt" }, { GIT_STATUS_WT_RENAMED, "songof7cities.txt", "ccc.txt" }, { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ddd.txt" }, }; @@ -199,7 +203,8 @@ void test_status_renames__both_one(void) git_status_list *statuslist; git_status_options opts = GIT_STATUS_OPTIONS_INIT; struct status_entry expected[] = { - { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "newname-workdir.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "ikeepsix.txt", "newname-workdir.txt" }, }; opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; @@ -229,10 +234,15 @@ void test_status_renames__both_two(void) git_status_list *statuslist; git_status_options opts = GIT_STATUS_OPTIONS_INIT; struct status_entry expected[] = { - { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ikeepsix-both.txt" }, - { GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "sixserving-index.txt" }, - { GIT_STATUS_WT_RENAMED, "songof7cities.txt", "songof7cities-workdir.txt" }, - { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, "untimely.txt", "untimely-both.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED | + GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, + "ikeepsix.txt", "ikeepsix-both.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED, + "sixserving.txt", "sixserving-index.txt" }, + { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, + "songof7cities.txt", "songof7cities-workdir.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "untimely.txt", "untimely-both.txt" }, }; opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; @@ -263,3 +273,113 @@ void test_status_renames__both_two(void) git_index_free(index); } + +void test_status_renames__both_casechange_one(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + int index_caps; + struct status_entry expected_icase[] = { + { GIT_STATUS_INDEX_RENAMED, + "ikeepsix.txt", "IKeepSix.txt" }, + }; + struct status_entry expected_case[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "ikeepsix.txt", "IKEEPSIX.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_repository_index(&index, g_repo)); + index_caps = git_index_caps(index); + + rename_file(g_repo, "ikeepsix.txt", "IKeepSix.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt")); + cl_git_pass(git_index_write(index)); + + /* on a case-insensitive file system, this change won't matter. + * on a case-sensitive one, it will. + */ + rename_file(g_repo, "IKeepSix.txt", "IKEEPSIX.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + + test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ? + expected_icase : expected_case, 1); + + git_status_list_free(statuslist); + + git_index_free(index); +} + +void test_status_renames__both_casechange_two(void) +{ + git_index *index; + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + int index_caps; + struct status_entry expected_icase[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED | + GIT_STATUS_WT_MODIFIED, + "ikeepsix.txt", "IKeepSix.txt" }, + { GIT_STATUS_INDEX_MODIFIED, + "sixserving.txt", "sixserving.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED, + "songof7cities.txt", "songof7.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "untimely.txt", "untimeliest.txt" } + }; + struct status_entry expected_case[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED | + GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, + "ikeepsix.txt", "ikeepsix.txt" }, + { GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED, + "sixserving.txt", "SixServing.txt" }, + { GIT_STATUS_INDEX_RENAMED | + GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED, + "songof7cities.txt", "SONGOF7.txt" }, + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, + "untimely.txt", "untimeliest.txt" } + }; + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_repository_index(&index, g_repo)); + index_caps = git_index_caps(index); + + rename_and_edit_file(g_repo, "ikeepsix.txt", "IKeepSix.txt"); + rename_and_edit_file(g_repo, "sixserving.txt", "sixserving.txt"); + rename_file(g_repo, "songof7cities.txt", "songof7.txt"); + rename_file(g_repo, "untimely.txt", "untimelier.txt"); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_remove_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_remove_bypath(index, "untimely.txt")); + cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt")); + cl_git_pass(git_index_add_bypath(index, "sixserving.txt")); + cl_git_pass(git_index_add_bypath(index, "songof7.txt")); + cl_git_pass(git_index_add_bypath(index, "untimelier.txt")); + cl_git_pass(git_index_write(index)); + + rename_and_edit_file(g_repo, "IKeepSix.txt", "ikeepsix.txt"); + rename_file(g_repo, "sixserving.txt", "SixServing.txt"); + rename_and_edit_file(g_repo, "songof7.txt", "SONGOF7.txt"); + rename_file(g_repo, "untimelier.txt", "untimeliest.txt"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + + test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ? + expected_icase : expected_case, 4); + + git_status_list_free(statuslist); + + git_index_free(index); +} From f3b5bc835ae4345a7a03834ffaf54a0aca92387d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Sun, 16 Jun 2013 21:51:43 -0700 Subject: [PATCH 353/384] Add test of rename with no changes A tree to index rename with no changes was getting erased by the iteration routine (if the routine actually loaded the data for the unmodified file). This invokes the code path that was previously messing up the diff and iterates twice to make sure that the iteration process itself doesn't modify the data. --- tests-clar/diff/rename.c | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index c4b722314..8a08da3ef 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -899,6 +899,7 @@ void test_diff_rename__rejected_match_can_match_others(void) cl_git_pass( git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + git_diff_list_free(diff); git_tree_free(tree); git_index_free(index); git_reference_free(head); @@ -913,6 +914,7 @@ void test_diff_rename__case_changes_are_split(void) git_tree *tree; git_diff_list *diff = NULL; diff_expects exp; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; cl_git_pass(git_repository_index(&index, g_repo)); @@ -934,6 +936,61 @@ void test_diff_rename__case_changes_are_split(void) cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + + git_diff_list_free(diff); git_index_free(index); } +void test_diff_rename__unmodified_can_be_renamed(void) +{ + git_index *index; + git_tree *tree; + git_diff_list *diff = NULL; + diff_expects exp; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/ikeepsix2.txt")); + + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, "ikeepsix2.txt")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + + git_diff_list_free(diff); +} From de0555a347b4be48f80d0b3bf26ddd81f5ef38aa Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 17 Jun 2013 09:55:29 -0700 Subject: [PATCH 354/384] Fix memory leaks in diff rename tests This fixes a couple objects I forgot to free, and also updates the valgrind suppressions file on the Mac to cover a few more cases that had crept in. --- tests-clar/diff/rename.c | 3 +++ tests-clar/valgrind-supp-mac.txt | 14 ++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 8a08da3ef..2600bd872 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -947,6 +947,7 @@ void test_diff_rename__case_changes_are_split(void) git_diff_list_free(diff); git_index_free(index); + git_tree_free(tree); } void test_diff_rename__unmodified_can_be_renamed(void) @@ -993,4 +994,6 @@ void test_diff_rename__unmodified_can_be_renamed(void) cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); git_diff_list_free(diff); + git_index_free(index); + git_tree_free(tree); } diff --git a/tests-clar/valgrind-supp-mac.txt b/tests-clar/valgrind-supp-mac.txt index 297b11e62..fcc7ede86 100644 --- a/tests-clar/valgrind-supp-mac.txt +++ b/tests-clar/valgrind-supp-mac.txt @@ -113,24 +113,18 @@ { mac-ssl-leak-1 Memcheck:Leak - fun:malloc - fun:CRYPTO_malloc ... fun:ERR_load_strings } { mac-ssl-leak-2 Memcheck:Leak - fun:malloc - fun:CRYPTO_malloc ... fun:SSL_library_init } { mac-ssl-leak-3 Memcheck:Leak - fun:malloc - fun:strdup ... fun:si_module_with_name fun:getaddrinfo @@ -143,6 +137,14 @@ ... fun:ssl3_get_server_certificate } +{ + mac-ssl-leak-5 + Memcheck:Leak + fun:malloc + fun:CRYPTO_malloc + ... + fun:ERR_put_error +} { clar-printf-buf Memcheck:Leak From f4183347607c85d3fbe02e8591d9393a011ecdf2 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 17 Jun 2013 10:23:53 -0700 Subject: [PATCH 355/384] Update clar to latest version --- tests-clar/clar.c | 48 +++++++++++++++++++++++---------------- tests-clar/clar/sandbox.h | 12 ++++++---- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/tests-clar/clar.c b/tests-clar/clar.c index 0eae81bf5..fb10dd397 100644 --- a/tests-clar/clar.c +++ b/tests-clar/clar.c @@ -183,10 +183,10 @@ clar_run_test( } static void -clar_run_suite(const struct clar_suite *suite, const char *name) +clar_run_suite(const struct clar_suite *suite, const char *filter) { const struct clar_func *test = suite->tests; - size_t i, namelen; + size_t i, matchlen; if (!suite->enabled) return; @@ -200,21 +200,21 @@ clar_run_suite(const struct clar_suite *suite, const char *name) _clar.active_suite = suite->name; _clar.suite_errors = 0; - if (name) { + if (filter) { size_t suitelen = strlen(suite->name); - namelen = strlen(name); - if (namelen <= suitelen) { - name = NULL; + matchlen = strlen(filter); + if (matchlen <= suitelen) { + filter = NULL; } else { - name += suitelen; - while (*name == ':') - ++name; - namelen = strlen(name); + filter += suitelen; + while (*filter == ':') + ++filter; + matchlen = strlen(filter); } } for (i = 0; i < suite->test_count; ++i) { - if (name && strncmp(test[i].name, name, namelen)) + if (filter && strncmp(test[i].name, filter, matchlen)) continue; _clar.active_test = test[i].name; @@ -230,7 +230,7 @@ clar_usage(const char *arg) { printf("Usage: %s [options]\n\n", arg); printf("Options:\n"); - printf(" -sname\tRun only the suite with `name`\n"); + printf(" -sname\tRun only the suite with `name` (can go to individual test name)\n"); printf(" -iname\tInclude the suite with `name`\n"); printf(" -xname\tExclude the suite with `name`\n"); printf(" -q \tOnly report tests that had an error\n"); @@ -256,21 +256,20 @@ clar_parse_args(int argc, char **argv) case 'x': { /* given suite name */ int offset = (argument[2] == '=') ? 3 : 2, found = 0; char action = argument[1]; - size_t j, len, cmplen; + size_t j, arglen, suitelen, cmplen; argument += offset; - len = strlen(argument); + arglen = strlen(argument); - if (len == 0) + if (arglen == 0) clar_usage(argv[0]); for (j = 0; j < _clar_suite_count; ++j) { - cmplen = strlen(_clar_suites[j].name); - if (cmplen > len) - cmplen = len; + suitelen = strlen(_clar_suites[j].name); + cmplen = (arglen < suitelen) ? arglen : suitelen; if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) { - int exact = !strcmp(argument, _clar_suites[j].name); + int exact = (arglen >= suitelen); ++found; @@ -419,7 +418,16 @@ void clar__assert_equal_s( if (!match) { char buf[4096]; - snprint_eq(buf, sizeof(buf), "'%s' != '%s'", s1, s2); + + if (s1 && s2) { + int pos; + for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos) + /* find differing byte offset */; + snprint_eq(buf, sizeof(buf), "'%s' != '%s' (at byte %d)", s1, s2, pos); + } else { + snprint_eq(buf, sizeof(buf), "'%s' != '%s'", s1, s2); + } + clar__fail(file, line, err, buf, should_abort); } } diff --git a/tests-clar/clar/sandbox.h b/tests-clar/clar/sandbox.h index bed3011fe..1ca6fcae8 100644 --- a/tests-clar/clar/sandbox.h +++ b/tests-clar/clar/sandbox.h @@ -18,9 +18,9 @@ static int find_tmp_path(char *buffer, size_t length) { #ifndef _WIN32 - static const size_t var_count = 4; + static const size_t var_count = 5; static const char *env_vars[] = { - "TMPDIR", "TMP", "TEMP", "USERPROFILE" + "CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE" }; size_t i; @@ -43,6 +43,12 @@ find_tmp_path(char *buffer, size_t length) } #else + DWORD env_len; + + if ((env_len = GetEnvironmentVariable("CLAR_TMP", buffer, length)) > 0 && + env_len < length) + return 0; + if (GetTempPath((DWORD)length, buffer)) return 0; #endif @@ -61,9 +67,7 @@ static void clar_unsandbox(void) if (_clar_path[0] == '\0') return; -#ifdef _WIN32 chdir(".."); -#endif fs_rm(_clar_path); } From 74ded024572318a32ff537c5f8dce001e9812e6b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 17 Jun 2013 17:03:34 -0700 Subject: [PATCH 356/384] Add "as_path" parameters to blob and buffer diffs This adds parameters to the four functions that allow for blob-to- blob and blob-to-buffer differencing (either via callbacks or by making a git_diff_patch object). These parameters let you say that filename we should pretend the blob has while doing the diff. If you pass NULL, there should be no change from the existing behavior, which is to skip using attributes for file type checks and just look at content. With the parameters, you can plug into the new diff driver functionality and get binary or non-binary behavior, plus function context regular expressions, etc. This commit also fixes things so that the git_diff_delta that is generated by these functions will actually be populated with the data that we know about the blobs (or buffers) so you can use it appropriately. It also fixes a bug in generating patches from the git_diff_patch objects created via these functions. Lastly, there is one other behavior change that may matter. If there is no difference between the two blobs, these functions no longer generate any diff callbacks / patches unless you have passed in GIT_DIFF_INCLUDE_UNMODIFIED. This is pretty natural, but could potentially change the behavior of existing usage. --- include/git2/diff.h | 20 +- src/diff_file.c | 160 +++++++------ src/diff_file.h | 9 +- src/diff_patch.c | 159 +++++++++---- src/diff_print.c | 22 +- tests-clar/diff/blob.c | 517 ++++++++++++++++++++++++++++++++--------- 6 files changed, 641 insertions(+), 246 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 8113a56be..f352f2843 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -983,7 +983,9 @@ GIT_EXTERN(int) git_diff_patch_to_str( * `GIT_DIFF_FORCE_TEXT` of course). * * @param old_blob Blob for old side of diff, or NULL for empty blob + * @param old_as_path Treat old blob as if it had this filename; can be NULL * @param new_blob Blob for new side of diff, or NULL for empty blob + * @param new_as_path Treat new blob as if it had this filename; can be NULL * @param options Options for diff, or NULL for default options * @param file_cb Callback for "file"; made once if there is a diff; can be NULL * @param hunk_cb Callback for each hunk in diff; can be NULL @@ -993,7 +995,9 @@ GIT_EXTERN(int) git_diff_patch_to_str( */ GIT_EXTERN(int) git_diff_blobs( const git_blob *old_blob, + const char *old_as_path, const git_blob *new_blob, + const char *new_as_path, const git_diff_options *options, git_diff_file_cb file_cb, git_diff_hunk_cb hunk_cb, @@ -1010,14 +1014,18 @@ GIT_EXTERN(int) git_diff_blobs( * * @param out The generated patch; NULL on error * @param old_blob Blob for old side of diff, or NULL for empty blob + * @param old_as_path Treat old blob as if it had this filename; can be NULL * @param new_blob Blob for new side of diff, or NULL for empty blob + * @param new_as_path Treat new blob as if it had this filename; can be NULL * @param options Options for diff, or NULL for default options * @return 0 on success or error code < 0 */ GIT_EXTERN(int) git_diff_patch_from_blobs( git_diff_patch **out, const git_blob *old_blob, + const char *old_as_path, const git_blob *new_blob, + const char *new_as_path, const git_diff_options *opts); /** @@ -1033,8 +1041,10 @@ GIT_EXTERN(int) git_diff_patch_from_blobs( * the reverse, with GIT_DELTA_REMOVED and blob content removed. * * @param old_blob Blob for old side of diff, or NULL for empty blob + * @param old_as_path Treat old blob as if it had this filename; can be NULL * @param buffer Raw data for new side of diff, or NULL for empty * @param buffer_len Length of raw data for new side of diff + * @param buffer_as_path Treat buffer as if it had this filename; can be NULL * @param options Options for diff, or NULL for default options * @param file_cb Callback for "file"; made once if there is a diff; can be NULL * @param hunk_cb Callback for each hunk in diff; can be NULL @@ -1044,8 +1054,10 @@ GIT_EXTERN(int) git_diff_patch_from_blobs( */ GIT_EXTERN(int) git_diff_blob_to_buffer( const git_blob *old_blob, + const char *old_as_path, const char *buffer, size_t buffer_len, + const char *buffer_as_path, const git_diff_options *options, git_diff_file_cb file_cb, git_diff_hunk_cb hunk_cb, @@ -1062,16 +1074,20 @@ GIT_EXTERN(int) git_diff_blob_to_buffer( * * @param out The generated patch; NULL on error * @param old_blob Blob for old side of diff, or NULL for empty blob + * @param old_as_path Treat old blob as if it had this filename; can be NULL * @param buffer Raw data for new side of diff, or NULL for empty * @param buffer_len Length of raw data for new side of diff + * @param buffer_as_path Treat buffer as if it had this filename; can be NULL * @param options Options for diff, or NULL for default options * @return 0 on success or error code < 0 */ GIT_EXTERN(int) git_diff_patch_from_blob_and_buffer( git_diff_patch **out, const git_blob *old_blob, - const char *buf, - size_t buflen, + const char *old_as_path, + const char *buffer, + size_t buffer_len, + const char *buffer_as_path, const git_diff_options *opts); diff --git a/src/diff_file.c b/src/diff_file.c index 4fd1177ae..9d06daafa 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -18,23 +18,23 @@ static bool diff_file_content_binary_by_size(git_diff_file_content *fc) { /* if we have diff opts, check max_size vs file size */ - if ((fc->file.flags & DIFF_FLAGS_KNOWN_BINARY) == 0 && + if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) == 0 && fc->opts_max_size > 0 && - fc->file.size > fc->opts_max_size) - fc->file.flags |= GIT_DIFF_FLAG_BINARY; + fc->file->size > fc->opts_max_size) + fc->file->flags |= GIT_DIFF_FLAG_BINARY; - return ((fc->file.flags & GIT_DIFF_FLAG_BINARY) != 0); + return ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0); } static void diff_file_content_binary_by_content(git_diff_file_content *fc) { - if ((fc->file.flags & DIFF_FLAGS_KNOWN_BINARY) != 0) + if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) != 0) return; switch (git_diff_driver_content_is_binary( fc->driver, fc->map.data, fc->map.len)) { - case 0: fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; break; - case 1: fc->file.flags |= GIT_DIFF_FLAG_BINARY; break; + case 0: fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY; break; + case 1: fc->file->flags |= GIT_DIFF_FLAG_BINARY; break; default: break; } } @@ -48,38 +48,39 @@ static int diff_file_content_init_common( fc->opts_max_size = opts->max_size ? opts->max_size : DIFF_MAX_FILESIZE; - if (!fc->driver) { - if (git_diff_driver_lookup(&fc->driver, fc->repo, "") < 0) - return -1; + if (fc->src == GIT_ITERATOR_TYPE_EMPTY) fc->src = GIT_ITERATOR_TYPE_TREE; - } + + if (!fc->driver && + git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0) + return -1; /* give driver a chance to modify options */ git_diff_driver_update_options(&fc->opts_flags, fc->driver); /* make sure file is conceivable mmap-able */ - if ((git_off_t)((size_t)fc->file.size) != fc->file.size) - fc->file.flags |= GIT_DIFF_FLAG_BINARY; + if ((git_off_t)((size_t)fc->file->size) != fc->file->size) + fc->file->flags |= GIT_DIFF_FLAG_BINARY; /* check if user is forcing text diff the file */ else if (fc->opts_flags & GIT_DIFF_FORCE_TEXT) { - fc->file.flags &= ~GIT_DIFF_FLAG_BINARY; - fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; + fc->file->flags &= ~GIT_DIFF_FLAG_BINARY; + fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY; } /* check if user is forcing binary diff the file */ else if (fc->opts_flags & GIT_DIFF_FORCE_BINARY) { - fc->file.flags &= ~GIT_DIFF_FLAG_NOT_BINARY; - fc->file.flags |= GIT_DIFF_FLAG_BINARY; + fc->file->flags &= ~GIT_DIFF_FLAG_NOT_BINARY; + fc->file->flags |= GIT_DIFF_FLAG_BINARY; } diff_file_content_binary_by_size(fc); - if ((fc->file.flags & GIT_DIFF_FLAG__NO_DATA) != 0) { - fc->file.flags |= GIT_DIFF_FLAG__LOADED; + if ((fc->flags & GIT_DIFF_FLAG__NO_DATA) != 0) { + fc->flags |= GIT_DIFF_FLAG__LOADED; fc->map.len = 0; fc->map.data = ""; } - if ((fc->file.flags & GIT_DIFF_FLAG__LOADED) != 0) + if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0) diff_file_content_binary_by_content(fc); return 0; @@ -92,15 +93,14 @@ int git_diff_file_content__init_from_diff( bool use_old) { git_diff_delta *delta = git_vector_get(&diff->deltas, delta_index); - git_diff_file *file = use_old ? &delta->old_file : &delta->new_file; bool has_data = true; memset(fc, 0, sizeof(*fc)); fc->repo = diff->repo; + fc->file = use_old ? &delta->old_file : &delta->new_file; fc->src = use_old ? diff->old_src : diff->new_src; - memcpy(&fc->file, file, sizeof(fc->file)); - if (git_diff_driver_lookup(&fc->driver, fc->repo, file->path) < 0) + if (git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0) return -1; switch (delta->status) { @@ -122,7 +122,7 @@ int git_diff_file_content__init_from_diff( } if (!has_data) - fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; + fc->flags |= GIT_DIFF_FLAG__NO_DATA; return diff_file_content_init_common(fc, &diff->opts); } @@ -131,21 +131,24 @@ int git_diff_file_content__init_from_blob( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, - const git_blob *blob) + const git_blob *blob, + git_diff_file *as_file) { memset(fc, 0, sizeof(*fc)); fc->repo = repo; + fc->file = as_file; fc->blob = blob; if (!blob) { - fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; + fc->flags |= GIT_DIFF_FLAG__NO_DATA; } else { - fc->file.flags |= GIT_DIFF_FLAG__LOADED | GIT_DIFF_FLAG_VALID_OID; - fc->file.size = git_blob_rawsize(blob); - fc->file.mode = 0644; - git_oid_cpy(&fc->file.oid, git_blob_id(blob)); + fc->flags |= GIT_DIFF_FLAG__LOADED; + fc->file->flags |= GIT_DIFF_FLAG_VALID_OID; + fc->file->size = git_blob_rawsize(blob); + fc->file->mode = GIT_FILEMODE_BLOB; + git_oid_cpy(&fc->file->oid, git_blob_id(blob)); - fc->map.len = (size_t)fc->file.size; + fc->map.len = (size_t)fc->file->size; fc->map.data = (char *)git_blob_rawcontent(blob); } @@ -157,18 +160,21 @@ int git_diff_file_content__init_from_raw( git_repository *repo, const git_diff_options *opts, const char *buf, - size_t buflen) + size_t buflen, + git_diff_file *as_file) { memset(fc, 0, sizeof(*fc)); fc->repo = repo; + fc->file = as_file; if (!buf) { - fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; + fc->flags |= GIT_DIFF_FLAG__NO_DATA; } else { - fc->file.flags |= GIT_DIFF_FLAG__LOADED | GIT_DIFF_FLAG_VALID_OID; - fc->file.size = buflen; - fc->file.mode = 0644; - git_odb_hash(&fc->file.oid, buf, buflen, GIT_OBJ_BLOB); + fc->flags |= GIT_DIFF_FLAG__LOADED; + fc->file->flags |= GIT_DIFF_FLAG_VALID_OID; + fc->file->size = buflen; + fc->file->mode = GIT_FILEMODE_BLOB; + git_odb_hash(&fc->file->oid, buf, buflen, GIT_OBJ_BLOB); fc->map.len = buflen; fc->map.data = (char *)buf; @@ -190,7 +196,7 @@ static int diff_file_content_commit_to_str( unsigned int sm_status = 0; const git_oid *sm_head; - if ((error = git_submodule_lookup(&sm, fc->repo, fc->file.path)) < 0 || + if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0 || (error = git_submodule_status(&sm_status, sm)) < 0) { /* GIT_EEXISTS means a "submodule" that has not been git added */ if (error == GIT_EEXISTS) @@ -199,25 +205,25 @@ static int diff_file_content_commit_to_str( } /* update OID if we didn't have it previously */ - if ((fc->file.flags & GIT_DIFF_FLAG_VALID_OID) == 0 && + if ((fc->file->flags & GIT_DIFF_FLAG_VALID_OID) == 0 && ((sm_head = git_submodule_wd_id(sm)) != NULL || (sm_head = git_submodule_head_id(sm)) != NULL)) { - git_oid_cpy(&fc->file.oid, sm_head); - fc->file.flags |= GIT_DIFF_FLAG_VALID_OID; + git_oid_cpy(&fc->file->oid, sm_head); + fc->file->flags |= GIT_DIFF_FLAG_VALID_OID; } if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) status = "-dirty"; } - git_oid_tostr(oid, sizeof(oid), &fc->file.oid); + git_oid_tostr(oid, sizeof(oid), &fc->file->oid); if (git_buf_printf(&content, "Subproject commit %s%s\n", oid, status) < 0) return -1; fc->map.len = git_buf_len(&content); fc->map.data = git_buf_detach(&content); - fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA; + fc->flags |= GIT_DIFF_FLAG__FREE_DATA; return 0; } @@ -227,27 +233,27 @@ static int diff_file_content_load_blob(git_diff_file_content *fc) int error = 0; git_odb_object *odb_obj = NULL; - if (git_oid_iszero(&fc->file.oid)) + if (git_oid_iszero(&fc->file->oid)) return 0; - if (fc->file.mode == GIT_FILEMODE_COMMIT) + if (fc->file->mode == GIT_FILEMODE_COMMIT) return diff_file_content_commit_to_str(fc, false); /* if we don't know size, try to peek at object header first */ - if (!fc->file.size) { + if (!fc->file->size) { git_odb *odb; size_t len; git_otype type; if (!(error = git_repository_odb__weakptr(&odb, fc->repo))) { error = git_odb__read_header_or_object( - &odb_obj, &len, &type, odb, &fc->file.oid); + &odb_obj, &len, &type, odb, &fc->file->oid); git_odb_free(odb); } if (error) return error; - fc->file.size = len; + fc->file->size = len; } if (diff_file_content_binary_by_size(fc)) @@ -259,11 +265,11 @@ static int diff_file_content_load_blob(git_diff_file_content *fc) git_odb_object_free(odb_obj); } else { error = git_blob_lookup( - (git_blob **)&fc->blob, fc->repo, &fc->file.oid); + (git_blob **)&fc->blob, fc->repo, &fc->file->oid); } if (!error) { - fc->file.flags |= GIT_DIFF_FLAG__FREE_BLOB; + fc->flags |= GIT_DIFF_FLAG__FREE_BLOB; fc->map.data = (void *)git_blob_rawcontent(fc->blob); fc->map.len = (size_t)git_blob_rawsize(fc->blob); } @@ -279,16 +285,16 @@ static int diff_file_content_load_workdir_symlink( /* link path on disk could be UTF-16, so prepare a buffer that is * big enough to handle some UTF-8 data expansion */ - alloc_len = (ssize_t)(fc->file.size * 2) + 1; + alloc_len = (ssize_t)(fc->file->size * 2) + 1; fc->map.data = git__calloc(alloc_len, sizeof(char)); GITERR_CHECK_ALLOC(fc->map.data); - fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA; + fc->flags |= GIT_DIFF_FLAG__FREE_DATA; read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len); if (read_len < 0) { - giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file.path); + giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file->path); return -1; } @@ -307,28 +313,28 @@ static int diff_file_content_load_workdir_file( if (fd < 0) return fd; - if (!fc->file.size && - !(fc->file.size = git_futils_filesize(fd))) + if (!fc->file->size && + !(fc->file->size = git_futils_filesize(fd))) goto cleanup; if (diff_file_content_binary_by_size(fc)) goto cleanup; if ((error = git_filters_load( - &filters, fc->repo, fc->file.path, GIT_FILTER_TO_ODB)) < 0) + &filters, fc->repo, fc->file->path, GIT_FILTER_TO_ODB)) < 0) goto cleanup; /* error >= is a filter count */ if (error == 0) { if (!(error = git_futils_mmap_ro( - &fc->map, fd, 0, (size_t)fc->file.size))) - fc->file.flags |= GIT_DIFF_FLAG__UNMAP_DATA; + &fc->map, fd, 0, (size_t)fc->file->size))) + fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA; else /* fall through to try readbuffer below */ giterr_clear(); } if (error != 0) { - error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file.size); + error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size); if (error < 0) goto cleanup; @@ -340,7 +346,7 @@ static int diff_file_content_load_workdir_file( if (!error) { fc->map.len = git_buf_len(&filtered); fc->map.data = git_buf_detach(&filtered); - fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA; + fc->flags |= GIT_DIFF_FLAG__FREE_DATA; } git_buf_free(&raw); @@ -359,26 +365,26 @@ static int diff_file_content_load_workdir(git_diff_file_content *fc) int error = 0; git_buf path = GIT_BUF_INIT; - if (fc->file.mode == GIT_FILEMODE_COMMIT) + if (fc->file->mode == GIT_FILEMODE_COMMIT) return diff_file_content_commit_to_str(fc, true); - if (fc->file.mode == GIT_FILEMODE_TREE) + if (fc->file->mode == GIT_FILEMODE_TREE) return 0; if (git_buf_joinpath( - &path, git_repository_workdir(fc->repo), fc->file.path) < 0) + &path, git_repository_workdir(fc->repo), fc->file->path) < 0) return -1; - if (S_ISLNK(fc->file.mode)) + if (S_ISLNK(fc->file->mode)) error = diff_file_content_load_workdir_symlink(fc, &path); else error = diff_file_content_load_workdir_file(fc, &path); /* once data is loaded, update OID if we didn't have it previously */ - if (!error && (fc->file.flags & GIT_DIFF_FLAG_VALID_OID) == 0) { + if (!error && (fc->file->flags & GIT_DIFF_FLAG_VALID_OID) == 0) { error = git_odb_hash( - &fc->file.oid, fc->map.data, fc->map.len, GIT_OBJ_BLOB); - fc->file.flags |= GIT_DIFF_FLAG_VALID_OID; + &fc->file->oid, fc->map.data, fc->map.len, GIT_OBJ_BLOB); + fc->file->flags |= GIT_DIFF_FLAG_VALID_OID; } git_buf_free(&path); @@ -389,10 +395,10 @@ int git_diff_file_content__load(git_diff_file_content *fc) { int error = 0; - if ((fc->file.flags & GIT_DIFF_FLAG__LOADED) != 0) + if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0) return 0; - if (fc->file.flags & GIT_DIFF_FLAG_BINARY) + if ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0) return 0; if (fc->src == GIT_ITERATOR_TYPE_WORKDIR) @@ -402,7 +408,7 @@ int git_diff_file_content__load(git_diff_file_content *fc) if (error) return error; - fc->file.flags |= GIT_DIFF_FLAG__LOADED; + fc->flags |= GIT_DIFF_FLAG__LOADED; diff_file_content_binary_by_content(fc); @@ -411,26 +417,26 @@ int git_diff_file_content__load(git_diff_file_content *fc) void git_diff_file_content__unload(git_diff_file_content *fc) { - if (fc->file.flags & GIT_DIFF_FLAG__FREE_DATA) { + if (fc->flags & GIT_DIFF_FLAG__FREE_DATA) { git__free(fc->map.data); fc->map.data = ""; fc->map.len = 0; - fc->file.flags &= ~GIT_DIFF_FLAG__FREE_DATA; + fc->flags &= ~GIT_DIFF_FLAG__FREE_DATA; } - else if (fc->file.flags & GIT_DIFF_FLAG__UNMAP_DATA) { + else if (fc->flags & GIT_DIFF_FLAG__UNMAP_DATA) { git_futils_mmap_free(&fc->map); fc->map.data = ""; fc->map.len = 0; - fc->file.flags &= ~GIT_DIFF_FLAG__UNMAP_DATA; + fc->flags &= ~GIT_DIFF_FLAG__UNMAP_DATA; } - if (fc->file.flags & GIT_DIFF_FLAG__FREE_BLOB) { + if (fc->flags & GIT_DIFF_FLAG__FREE_BLOB) { git_blob_free((git_blob *)fc->blob); fc->blob = NULL; - fc->file.flags &= ~GIT_DIFF_FLAG__FREE_BLOB; + fc->flags &= ~GIT_DIFF_FLAG__FREE_BLOB; } - fc->file.flags &= ~GIT_DIFF_FLAG__LOADED; + fc->flags &= ~GIT_DIFF_FLAG__LOADED; } void git_diff_file_content__clear(git_diff_file_content *fc) diff --git a/src/diff_file.h b/src/diff_file.h index afad8510b..fb08cca6a 100644 --- a/src/diff_file.h +++ b/src/diff_file.h @@ -15,8 +15,9 @@ /* expanded information for one side of a delta */ typedef struct { git_repository *repo; - git_diff_file file; + git_diff_file *file; git_diff_driver *driver; + uint32_t flags; uint32_t opts_flags; git_off_t opts_max_size; git_iterator_type_t src; @@ -34,14 +35,16 @@ extern int git_diff_file_content__init_from_blob( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, - const git_blob *blob); + const git_blob *blob, + git_diff_file *as_file); extern int git_diff_file_content__init_from_raw( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, const char *buf, - size_t buflen); + size_t buflen, + git_diff_file *as_file); /* this loads the blob/file-on-disk as needed */ extern int git_diff_file_content__load(git_diff_file_content *fc); diff --git a/src/diff_patch.c b/src/diff_patch.c index 40cb3371a..9060d0a24 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -64,12 +64,12 @@ static void diff_patch_update_binary(git_diff_patch *patch) if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0) return; - if ((patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0 || - (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) + if ((patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0 || + (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0) patch->delta->flags |= GIT_DIFF_FLAG_BINARY; - else if ((patch->ofile.file.flags & DIFF_FLAGS_NOT_BINARY) != 0 && - (patch->nfile.file.flags & DIFF_FLAGS_NOT_BINARY) != 0) + else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 && + (patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0) patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY; } @@ -143,42 +143,42 @@ static int diff_patch_load(git_diff_patch *patch, git_diff_output *output) output && !output->hunk_cb && !output->data_cb) return 0; -#define DIFF_FLAGS_KNOWN_DATA (GIT_DIFF_FLAG__NO_DATA|GIT_DIFF_FLAG_VALID_OID) - incomplete_data = - ((patch->ofile.file.flags & DIFF_FLAGS_KNOWN_DATA) != 0 && - (patch->nfile.file.flags & DIFF_FLAGS_KNOWN_DATA) != 0); + (((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 || + (patch->ofile.file->flags & GIT_DIFF_FLAG_VALID_OID) != 0) && + ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 || + (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_OID) != 0)); /* always try to load workdir content first because filtering may * need 2x data size and this minimizes peak memory footprint */ if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) { if ((error = git_diff_file_content__load(&patch->ofile)) < 0 || - (patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) + (patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0) goto cleanup; } if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) { if ((error = git_diff_file_content__load(&patch->nfile)) < 0 || - (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) + (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0) goto cleanup; } /* once workdir has been tried, load other data as needed */ if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) { if ((error = git_diff_file_content__load(&patch->ofile)) < 0 || - (patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) + (patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0) goto cleanup; } if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) { if ((error = git_diff_file_content__load(&patch->nfile)) < 0 || - (patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) + (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0) goto cleanup; } /* if we were previously missing an oid, update MODIFIED->UNMODIFIED */ if (incomplete_data && - patch->ofile.file.mode == patch->nfile.file.mode && - git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid) && + patch->ofile.file->mode == patch->nfile.file->mode && + git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid) && patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */ patch->delta->status = GIT_DELTA_UNMODIFIED; @@ -193,7 +193,7 @@ cleanup: patch->delta->status != GIT_DELTA_UNMODIFIED && (patch->ofile.map.len || patch->nfile.map.len) && (patch->ofile.map.len != patch->nfile.map.len || - !git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid))) + !git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid))) patch->flags |= GIT_DIFF_PATCH_DIFFABLE; patch->flags |= GIT_DIFF_PATCH_LOADED; @@ -312,26 +312,31 @@ int git_diff_foreach( typedef struct { git_diff_patch patch; git_diff_delta delta; + char paths[GIT_FLEX_ARRAY]; } diff_patch_with_delta; static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo) { int error = 0; git_diff_patch *patch = &pd->patch; - bool has_old = ((patch->ofile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0); - bool has_new = ((patch->nfile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0); + bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0); + bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0); pd->delta.status = has_new ? (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) : (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED); - if (git_oid_equal(&patch->nfile.file.oid, &patch->ofile.file.oid)) + if (git_oid_equal(&patch->nfile.file->oid, &patch->ofile.file->oid)) pd->delta.status = GIT_DELTA_UNMODIFIED; patch->delta = &pd->delta; diff_patch_init_common(patch); + if (pd->delta.status == GIT_DELTA_UNMODIFIED && + !(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED)) + return error; + error = diff_patch_file_callback(patch, (git_diff_output *)xo); if (!error) @@ -347,7 +352,9 @@ static int diff_patch_from_blobs( diff_patch_with_delta *pd, git_xdiff_output *xo, const git_blob *old_blob, + const char *old_path, const git_blob *new_blob, + const char *new_path, const git_diff_options *opts) { int error = 0; @@ -357,29 +364,61 @@ static int diff_patch_from_blobs( GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); - pd->patch.delta = &pd->delta; - - if (!repo) /* return two NULL items as UNMODIFIED delta */ - return 0; - if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { - const git_blob *swap = old_blob; - old_blob = new_blob; - new_blob = swap; + const git_blob *tmp_blob; + const char *tmp_path; + tmp_blob = old_blob; old_blob = new_blob; new_blob = tmp_blob; + tmp_path = old_path; old_path = new_path; new_path = tmp_path; } + pd->patch.delta = &pd->delta; + + pd->delta.old_file.path = old_path; + pd->delta.new_file.path = new_path; + if ((error = git_diff_file_content__init_from_blob( - &pd->patch.ofile, repo, opts, old_blob)) < 0 || + &pd->patch.ofile, repo, opts, old_blob, &pd->delta.old_file)) < 0 || (error = git_diff_file_content__init_from_blob( - &pd->patch.nfile, repo, opts, new_blob)) < 0) + &pd->patch.nfile, repo, opts, new_blob, &pd->delta.new_file)) < 0) return error; return diff_single_generate(pd, xo); } +static int diff_patch_with_delta_alloc( + diff_patch_with_delta **out, + const char **old_path, + const char **new_path) +{ + diff_patch_with_delta *pd; + size_t old_len = *old_path ? strlen(*old_path) : 0; + size_t new_len = *new_path ? strlen(*new_path) : 0; + + *out = pd = git__calloc(1, sizeof(*pd) + old_len + new_len + 2); + GITERR_CHECK_ALLOC(pd); + + pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED; + + if (*old_path) { + memcpy(&pd->paths[0], *old_path, old_len); + *old_path = &pd->paths[0]; + } else if (*new_path) + *old_path = &pd->paths[old_len + 1]; + + if (*new_path) { + memcpy(&pd->paths[old_len + 1], *new_path, new_len); + *new_path = &pd->paths[old_len + 1]; + } else if (*old_path) + *new_path = &pd->paths[0]; + + return 0; +} + int git_diff_blobs( const git_blob *old_blob, + const char *old_path, const git_blob *new_blob, + const char *new_path, const git_diff_options *opts, git_diff_file_cb file_cb, git_diff_hunk_cb hunk_cb, @@ -397,7 +436,13 @@ int git_diff_blobs( (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, opts); - error = diff_patch_from_blobs(&pd, &xo, old_blob, new_blob, opts); + if (!old_path && new_path) + old_path = new_path; + else if (!new_path && old_path) + new_path = old_path; + + error = diff_patch_from_blobs( + &pd, &xo, old_blob, old_path, new_blob, new_path, opts); git_diff_patch_free((git_diff_patch *)&pd); @@ -407,7 +452,9 @@ int git_diff_blobs( int git_diff_patch_from_blobs( git_diff_patch **out, const git_blob *old_blob, + const char *old_path, const git_blob *new_blob, + const char *new_path, const git_diff_options *opts) { int error = 0; @@ -417,16 +464,18 @@ int git_diff_patch_from_blobs( assert(out); *out = NULL; - pd = git__calloc(1, sizeof(*pd)); - GITERR_CHECK_ALLOC(pd); - pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED; + if (diff_patch_with_delta_alloc(&pd, &old_path, &new_path) < 0) + return -1; memset(&xo, 0, sizeof(xo)); diff_output_to_patch((git_diff_output *)&xo, &pd->patch); git_xdiff_init(&xo, opts); - if (!(error = diff_patch_from_blobs(pd, &xo, old_blob, new_blob, opts))) + error = diff_patch_from_blobs( + pd, &xo, old_blob, old_path, new_blob, new_path, opts); + + if (!error) *out = (git_diff_patch *)pd; else git_diff_patch_free((git_diff_patch *)pd); @@ -438,8 +487,10 @@ static int diff_patch_from_blob_and_buffer( diff_patch_with_delta *pd, git_xdiff_output *xo, const git_blob *old_blob, + const char *old_path, const char *buf, size_t buflen, + const char *buf_path, const git_diff_options *opts) { int error = 0; @@ -450,28 +501,36 @@ static int diff_patch_from_blob_and_buffer( pd->patch.delta = &pd->delta; - if (!repo && !buf) /* return two NULL items as UNMODIFIED delta */ - return 0; - if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { + pd->delta.old_file.path = buf_path; + pd->delta.new_file.path = old_path; + if (!(error = git_diff_file_content__init_from_raw( - &pd->patch.ofile, repo, opts, buf, buflen))) + &pd->patch.ofile, repo, opts, buf, buflen, &pd->delta.old_file))) error = git_diff_file_content__init_from_blob( - &pd->patch.nfile, repo, opts, old_blob); + &pd->patch.nfile, repo, opts, old_blob, &pd->delta.new_file); } else { + pd->delta.old_file.path = old_path; + pd->delta.new_file.path = buf_path; + if (!(error = git_diff_file_content__init_from_blob( - &pd->patch.ofile, repo, opts, old_blob))) + &pd->patch.ofile, repo, opts, old_blob, &pd->delta.old_file))) error = git_diff_file_content__init_from_raw( - &pd->patch.nfile, repo, opts, buf, buflen); + &pd->patch.nfile, repo, opts, buf, buflen, &pd->delta.new_file); } + if (error < 0) + return error; + return diff_single_generate(pd, xo); } int git_diff_blob_to_buffer( const git_blob *old_blob, + const char *old_path, const char *buf, size_t buflen, + const char *buf_path, const git_diff_options *opts, git_diff_file_cb file_cb, git_diff_hunk_cb hunk_cb, @@ -489,8 +548,13 @@ int git_diff_blob_to_buffer( (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, opts); + if (!old_path && buf_path) + old_path = buf_path; + else if (!buf_path && old_path) + buf_path = old_path; + error = diff_patch_from_blob_and_buffer( - &pd, &xo, old_blob, buf, buflen, opts); + &pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts); git_diff_patch_free((git_diff_patch *)&pd); @@ -500,8 +564,10 @@ int git_diff_blob_to_buffer( int git_diff_patch_from_blob_and_buffer( git_diff_patch **out, const git_blob *old_blob, + const char *old_path, const char *buf, size_t buflen, + const char *buf_path, const git_diff_options *opts) { int error = 0; @@ -511,17 +577,18 @@ int git_diff_patch_from_blob_and_buffer( assert(out); *out = NULL; - pd = git__calloc(1, sizeof(*pd)); - GITERR_CHECK_ALLOC(pd); - pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED; + if (diff_patch_with_delta_alloc(&pd, &old_path, &buf_path) < 0) + return -1; memset(&xo, 0, sizeof(xo)); diff_output_to_patch((git_diff_output *)&xo, &pd->patch); git_xdiff_init(&xo, opts); - if (!(error = diff_patch_from_blob_and_buffer( - pd, &xo, old_blob, buf, buflen, opts))) + error = diff_patch_from_blob_and_buffer( + pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts); + + if (!error) *out = (git_diff_patch *)pd; else git_diff_patch_free((git_diff_patch *)pd); diff --git a/src/diff_print.c b/src/diff_print.c index 6fc7425eb..30f221a62 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -21,14 +21,15 @@ static int diff_print_info_init( diff_print_info *pi, git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload) { - assert(diff && diff->repo); - pi->diff = diff; pi->print_cb = cb; pi->payload = payload; pi->buf = out; - if (git_repository__cvar(&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0) + if (!diff || !diff->repo) + pi->oid_strlen = GIT_ABBREV_DEFAULT; + else if (git_repository__cvar( + &pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0) return -1; pi->oid_strlen += 1; /* for NUL byte */ @@ -82,6 +83,8 @@ static int diff_print_one_compact( diff_print_info *pi = data; git_buf *out = pi->buf; char old_suffix, new_suffix, code = git_diff_status_char(delta->status); + int (*strcomp)(const char *, const char *) = + pi->diff ? pi->diff->strcomp : git__strcmp; GIT_UNUSED(progress); @@ -94,7 +97,7 @@ static int diff_print_one_compact( git_buf_clear(out); if (delta->old_file.path != delta->new_file.path && - pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0) + strcomp(delta->old_file.path,delta->new_file.path) != 0) git_buf_printf(out, "%c\t%s%c -> %s%c\n", code, delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); else if (delta->old_file.mode != delta->new_file.mode && @@ -229,10 +232,11 @@ static int diff_print_patch_file( const git_diff_delta *delta, float progress, void *data) { diff_print_info *pi = data; - const char *oldpfx = pi->diff->opts.old_prefix; + const char *oldpfx = pi->diff ? pi->diff->opts.old_prefix : NULL; const char *oldpath = delta->old_file.path; - const char *newpfx = pi->diff->opts.new_prefix; + const char *newpfx = pi->diff ? pi->diff->opts.new_prefix : NULL; const char *newpath = delta->new_file.path; + uint32_t opts_flags = pi->diff ? pi->diff->opts.flags : GIT_DIFF_NORMAL; GIT_UNUSED(progress); @@ -240,17 +244,17 @@ static int diff_print_patch_file( delta->status == GIT_DELTA_UNMODIFIED || delta->status == GIT_DELTA_IGNORED || (delta->status == GIT_DELTA_UNTRACKED && - (pi->diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0)) + (opts_flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0)) return 0; if (!oldpfx) oldpfx = DIFF_OLD_PREFIX_DEFAULT; - if (!newpfx) newpfx = DIFF_NEW_PREFIX_DEFAULT; git_buf_clear(pi->buf); - git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path); + git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", + oldpfx, delta->old_file.path, newpfx, delta->new_file.path); if (diff_print_oid_range(pi, delta) < 0) return -1; diff --git a/tests-clar/diff/blob.c b/tests-clar/diff/blob.c index b12186d98..42b9fcd5f 100644 --- a/tests-clar/diff/blob.c +++ b/tests-clar/diff/blob.c @@ -6,6 +6,20 @@ static diff_expects expected; static git_diff_options opts; static git_blob *d, *alien; +static void quick_diff_blob_to_str( + const git_blob *blob, const char *blob_path, + const char *str, size_t len, const char *str_path) +{ + memset(&expected, 0, sizeof(expected)); + + if (str && !len) + len = strlen(str); + + cl_git_pass(git_diff_blob_to_buffer( + blob, blob_path, str, len, str_path, + &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); +} + void test_diff_blob__initialize(void) { git_oid oid; @@ -59,7 +73,8 @@ void test_diff_blob__can_compare_text_blobs(void) /* diff on tests/resources/attr/root_test1 */ cl_git_pass(git_diff_blobs( - a, b, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + a, NULL, b, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); @@ -74,7 +89,8 @@ void test_diff_blob__can_compare_text_blobs(void) /* diff on tests/resources/attr/root_test2 */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - b, c, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + b, NULL, c, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); @@ -89,7 +105,8 @@ void test_diff_blob__can_compare_text_blobs(void) /* diff on tests/resources/attr/root_test3 */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - a, c, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + a, NULL, c, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); @@ -103,7 +120,8 @@ void test_diff_blob__can_compare_text_blobs(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - c, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + c, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); @@ -125,6 +143,7 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) git_blob *a, *b, *c; git_oid a_oid, b_oid, c_oid; git_diff_patch *p; + const git_diff_delta *delta; size_t tc, ta, td; /* tests/resources/attr/root_test1 */ @@ -142,10 +161,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) /* Doing the equivalent of a `git diff -U1` on these files */ /* diff on tests/resources/attr/root_test1 */ - cl_git_pass(git_diff_patch_from_blobs(&p, a, b, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, a, NULL, b, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); + cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size); + cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0)); @@ -157,10 +184,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) git_diff_patch_free(p); /* diff on tests/resources/attr/root_test2 */ - cl_git_pass(git_diff_patch_from_blobs(&p, b, c, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, b, NULL, c, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); + cl_assert(git_oid_equal(git_blob_id(b), &delta->old_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(b), delta->old_file.size); + cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(15, git_diff_patch_num_lines_in_hunk(p, 0)); @@ -172,12 +207,17 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) git_diff_patch_free(p); /* diff on tests/resources/attr/root_test3 */ - cl_git_pass(git_diff_patch_from_blobs(&p, a, c, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, a, NULL, c, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); - cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); - cl_assert_equal_i(13, git_diff_patch_num_lines_in_hunk(p, 0)); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); + cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size); + cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size); cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p)); cl_assert_equal_i(0, (int)tc); @@ -187,10 +227,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) git_diff_patch_free(p); /* one more */ - cl_git_pass(git_diff_patch_from_blobs(&p, c, d, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, c, NULL, d, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); + cl_assert(git_oid_equal(git_blob_id(c), &delta->old_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(c), delta->old_file.size); + cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size); + cl_assert_equal_i(2, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(5, git_diff_patch_num_lines_in_hunk(p, 0)); cl_assert_equal_i(9, git_diff_patch_num_lines_in_hunk(p, 1)); @@ -212,7 +260,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) git_blob *e = NULL; cl_git_pass(git_diff_blobs( - d, e, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, e, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]); @@ -227,7 +276,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - d, e, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, e, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]); @@ -242,7 +292,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - alien, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + alien, NULL, NULL, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files_binary); @@ -253,7 +304,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - NULL, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + NULL, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files_binary); @@ -266,13 +318,22 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void) { git_blob *e = NULL; git_diff_patch *p; + const git_diff_delta *delta; int line; char origin; - cl_git_pass(git_diff_patch_from_blobs(&p, d, e, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, e, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_DELETED, delta->status); + cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(d), delta->old_file.size); + cl_assert(git_oid_iszero(&delta->new_file.oid)); + cl_assert_equal_sz(0, delta->new_file.size); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0)); @@ -286,10 +347,18 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void) opts.flags |= GIT_DIFF_REVERSE; - cl_git_pass(git_diff_patch_from_blobs(&p, d, e, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, e, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_ADDED, delta->status); + cl_assert(git_oid_iszero(&delta->old_file.oid)); + cl_assert_equal_sz(0, delta->old_file.size); + cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid)); + cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size); + cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0)); @@ -303,20 +372,28 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void) opts.flags ^= GIT_DIFF_REVERSE; - cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, NULL, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status); - cl_assert((git_diff_patch_delta(p)->flags & GIT_DIFF_FLAG_BINARY) != 0); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_DELETED, delta->status); + cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); git_diff_patch_free(p); - cl_git_pass(git_diff_patch_from_blobs(&p, NULL, alien, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, alien, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); - cl_assert((git_diff_patch_delta(p)->flags & GIT_DIFF_FLAG_BINARY) != 0); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_ADDED, delta->status); + cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); git_diff_patch_free(p); @@ -332,44 +409,66 @@ static void assert_identical_blobs_comparison(diff_expects *expected) void test_diff_blob__can_compare_identical_blobs(void) { - cl_git_pass(git_diff_blobs( - d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + + cl_git_pass(git_diff_blobs( + d, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - cl_assert_equal_i(0, expected.files_binary); assert_identical_blobs_comparison(&expected); - - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blobs( - NULL, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - cl_assert_equal_i(0, expected.files_binary); - cl_assert_equal_i(0, expected.files); /* NULLs mean no callbacks, period */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - alien, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + NULL, NULL, NULL, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + assert_identical_blobs_comparison(&expected); + cl_assert_equal_i(0, expected.files_binary); + + memset(&expected, 0, sizeof(expected)); + cl_git_pass(git_diff_blobs( + alien, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + + assert_identical_blobs_comparison(&expected); cl_assert(expected.files_binary > 0); - assert_identical_blobs_comparison(&expected); } void test_diff_blob__can_compare_identical_blobs_with_patch(void) { git_diff_patch *p; + const git_diff_delta *delta; - cl_git_pass(git_diff_patch_from_blobs(&p, d, d, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, d, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status); + cl_assert_equal_sz(delta->old_file.size, git_blob_rawsize(d)); + cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid)); + cl_assert_equal_sz(delta->new_file.size, git_blob_rawsize(d)); + cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid)); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); git_diff_patch_free(p); - cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, NULL, NULL, &opts)); cl_assert(p != NULL); - cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); + + delta = git_diff_patch_delta(p); + cl_assert(delta != NULL); + cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status); + cl_assert_equal_sz(0, delta->old_file.size); + cl_assert(git_oid_iszero(&delta->old_file.oid)); + cl_assert_equal_sz(0, delta->new_file.size); + cl_assert(git_oid_iszero(&delta->new_file.oid)); + cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); git_diff_patch_free(p); - cl_git_pass(git_diff_patch_from_blobs(&p, alien, alien, &opts)); + cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, alien, NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); @@ -396,14 +495,16 @@ void test_diff_blob__can_compare_two_binary_blobs(void) cl_git_pass(git_blob_lookup_prefix(&heart, g_repo, &h_oid, 4)); cl_git_pass(git_diff_blobs( - alien, heart, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + alien, NULL, heart, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - heart, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + heart, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); @@ -413,14 +514,16 @@ void test_diff_blob__can_compare_two_binary_blobs(void) void test_diff_blob__can_compare_a_binary_blob_and_a_text_blob(void) { cl_git_pass(git_diff_blobs( - alien, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + alien, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); } @@ -461,7 +564,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) /* Test with default inter-hunk-context (not set) => default is 0 */ cl_git_pass(git_diff_blobs( - old_d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + old_d, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(2, expected.hunks); @@ -469,7 +573,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) opts.interhunk_lines = 0; memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - old_d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + old_d, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(2, expected.hunks); @@ -477,7 +582,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) opts.interhunk_lines = 1; memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - old_d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + old_d, NULL, d, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.hunks); @@ -490,7 +596,8 @@ void test_diff_blob__checks_options_version_too_low(void) opts.version = 0; cl_git_fail(git_diff_blobs( - d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); err = giterr_last(); cl_assert_equal_i(GITERR_INVALID, err->klass); } @@ -501,7 +608,8 @@ void test_diff_blob__checks_options_version_too_high(void) opts.version = 1024; cl_git_fail(git_diff_blobs( - d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + d, NULL, alien, NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); err = giterr_last(); cl_assert_equal_i(GITERR_INVALID, err->klass); } @@ -548,10 +656,7 @@ void test_diff_blob__can_compare_blob_to_buffer(void) cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4)); /* diff from blob a to content of b */ - cl_git_pass(git_diff_blob_to_buffer( - a, b_content, strlen(b_content), - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - + quick_diff_blob_to_str(a, NULL, b_content, 0, NULL); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); cl_assert_equal_i(0, expected.files_binary); @@ -562,37 +667,25 @@ void test_diff_blob__can_compare_blob_to_buffer(void) cl_assert_equal_i(0, expected.line_dels); /* diff from blob a to content of a */ - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - a, a_content, strlen(a_content), - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - + opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + quick_diff_blob_to_str(a, NULL, a_content, 0, NULL); assert_identical_blobs_comparison(&expected); /* diff from NULL blob to content of a */ memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - NULL, a_content, strlen(a_content), - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - + quick_diff_blob_to_str(NULL, NULL, a_content, 0, NULL); assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED); /* diff from blob a to NULL buffer */ memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - a, NULL, 0, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - + quick_diff_blob_to_str(a, NULL, NULL, 0, NULL); assert_changed_single_one_line_file(&expected, GIT_DELTA_DELETED); /* diff with reverse */ opts.flags ^= GIT_DIFF_REVERSE; memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - a, NULL, 0, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - + quick_diff_blob_to_str(a, NULL, NULL, 0, NULL); assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED); git_blob_free(a); @@ -613,7 +706,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) /* diff from blob a to content of b */ cl_git_pass(git_diff_patch_from_blob_and_buffer( - &p, a, b_content, strlen(b_content), &opts)); + &p, a, NULL, b_content, strlen(b_content), NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); @@ -628,8 +721,9 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) git_diff_patch_free(p); /* diff from blob a to content of a */ + opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; cl_git_pass(git_diff_patch_from_blob_and_buffer( - &p, a, a_content, strlen(a_content), &opts)); + &p, a, NULL, a_content, strlen(a_content), NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); @@ -637,7 +731,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) /* diff from NULL blob to content of a */ cl_git_pass(git_diff_patch_from_blob_and_buffer( - &p, NULL, a_content, strlen(a_content), &opts)); + &p, NULL, NULL, a_content, strlen(a_content), NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); @@ -646,7 +740,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) /* diff from blob a to NULL buffer */ cl_git_pass(git_diff_patch_from_blob_and_buffer( - &p, a, NULL, 0, &opts)); + &p, a, NULL, NULL, 0, NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status); cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); @@ -657,7 +751,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) opts.flags ^= GIT_DIFF_REVERSE; cl_git_pass(git_diff_patch_from_blob_and_buffer( - &p, a, NULL, 0, &opts)); + &p, a, NULL, NULL, 0, NULL, &opts)); cl_assert(p != NULL); cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); @@ -684,6 +778,8 @@ void test_diff_blob__binary_data_comparisons(void) const char *bin_content = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n"; size_t bin_len = 33; + opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8)); cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4)); @@ -692,44 +788,32 @@ void test_diff_blob__binary_data_comparisons(void) /* non-binary to reference content */ - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - nonbin, nonbin_content, nonbin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(nonbin, NULL, nonbin_content, nonbin_len, NULL); assert_identical_blobs_comparison(&expected); cl_assert_equal_i(0, expected.files_binary); /* binary to reference content */ - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - bin, bin_content, bin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL); assert_identical_blobs_comparison(&expected); cl_assert_equal_i(1, expected.files_binary); /* non-binary to binary content */ - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - nonbin, bin_content, bin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL); assert_binary_blobs_comparison(&expected); /* binary to non-binary content */ - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - bin, nonbin_content, nonbin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL); assert_binary_blobs_comparison(&expected); /* non-binary to binary blob */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - bin, nonbin, &opts, + bin, NULL, nonbin, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); @@ -739,27 +823,18 @@ void test_diff_blob__binary_data_comparisons(void) opts.flags |= GIT_DIFF_FORCE_TEXT; - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - bin, bin_content, bin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL); assert_identical_blobs_comparison(&expected); - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - nonbin, bin_content, bin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL); assert_one_modified_with_lines(&expected, 4); - memset(&expected, 0, sizeof(expected)); - cl_git_pass(git_diff_blob_to_buffer( - bin, nonbin_content, nonbin_len, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL); assert_one_modified_with_lines(&expected, 4); memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( - bin, nonbin, &opts, + bin, NULL, nonbin, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified_with_lines(&expected, 4); @@ -767,3 +842,227 @@ void test_diff_blob__binary_data_comparisons(void) git_blob_free(bin); git_blob_free(nonbin); } + +void test_diff_blob__using_path_and_attributes(void) +{ + git_config *cfg; + git_blob *bin, *nonbin; + git_oid oid; + const char *nonbin_content = "Hello from the root\n"; + const char *bin_content = + "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n"; + size_t bin_len = 33; + const char *changed; + git_diff_patch *p; + char *pout; + + /* set up custom diff drivers and 'diff' attribute mappings for them */ + + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_bool(cfg, "diff.iam_binary.binary", 1)); + cl_git_pass(git_config_set_bool(cfg, "diff.iam_text.binary", 0)); + cl_git_pass(git_config_set_string( + cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z]")); + cl_git_pass(git_config_set_bool(cfg, "diff.iam_textalpha.binary", 0)); + cl_git_pass(git_config_set_string( + cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z]")); + cl_git_pass(git_config_set_string( + cfg, "diff.iam_numctx.funcname", "^[0-9]")); + cl_git_pass(git_config_set_bool(cfg, "diff.iam_textnum.binary", 0)); + cl_git_pass(git_config_set_string( + cfg, "diff.iam_textnum.funcname", "^[0-9]")); + git_config_free(cfg); + + cl_git_append2file( + "attr/.gitattributes", + "\n\n# test_diff_blob__using_path_and_attributes extra\n\n" + "*.binary diff=iam_binary\n" + "*.textary diff=iam_text\n" + "*.alphary diff=iam_alphactx\n" + "*.textalphary diff=iam_textalpha\n" + "*.textnumary diff=iam_textnum\n" + "*.numary diff=iam_numctx\n\n"); + + opts.context_lines = 0; + opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + + cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8)); + cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4)); + /* 20b: "Hello from the root\n" */ + + cl_git_pass(git_oid_fromstrn(&oid, "b435cd56", 8)); + cl_git_pass(git_blob_lookup_prefix(&bin, g_repo, &oid, 4)); + /* 33b: "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\n0123456789\n" */ + + /* non-binary to reference content */ + + quick_diff_blob_to_str(nonbin, NULL, nonbin_content, 0, NULL); + assert_identical_blobs_comparison(&expected); + cl_assert_equal_i(0, expected.files_binary); + + /* binary to reference content */ + + quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL); + assert_identical_blobs_comparison(&expected); + cl_assert_equal_i(1, expected.files_binary); + + /* add some text */ + + changed = "Hello from the root\nMore lines\nAnd more\nGo here\n"; + + quick_diff_blob_to_str(nonbin, NULL, changed, 0, NULL); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, expected.files_binary); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(3, expected.lines); + cl_assert_equal_i(0, expected.line_ctxt); + cl_assert_equal_i(3, expected.line_adds); + cl_assert_equal_i(0, expected.line_dels); + + quick_diff_blob_to_str(nonbin, "foo/bar.binary", changed, 0, NULL); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, expected.files_binary); + cl_assert_equal_i(0, expected.hunks); + cl_assert_equal_i(0, expected.lines); + cl_assert_equal_i(0, expected.line_ctxt); + cl_assert_equal_i(0, expected.line_adds); + cl_assert_equal_i(0, expected.line_dels); + + quick_diff_blob_to_str(nonbin, "foo/bar.textary", changed, 0, NULL); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, expected.files_binary); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(3, expected.lines); + cl_assert_equal_i(0, expected.line_ctxt); + cl_assert_equal_i(3, expected.line_adds); + cl_assert_equal_i(0, expected.line_dels); + + quick_diff_blob_to_str(nonbin, "foo/bar.alphary", changed, 0, NULL); + cl_assert_equal_i(1, expected.files); + cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, expected.files_binary); + cl_assert_equal_i(1, expected.hunks); + cl_assert_equal_i(3, expected.lines); + cl_assert_equal_i(0, expected.line_ctxt); + cl_assert_equal_i(3, expected.line_adds); + cl_assert_equal_i(0, expected.line_dels); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.normal b/zzz.normal\n" + "index 45141a7..75b0dbb 100644\n" + "--- a/zzz.normal\n" + "+++ b/zzz.normal\n" + "@@ -1,0 +2,3 @@ Hello from the root\n" + "+More lines\n" + "+And more\n" + "+Go here\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, nonbin, "zzz.binary", changed, strlen(changed), NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.binary b/zzz.binary\n" + "index 45141a7..75b0dbb 100644\n" + "Binary files a/zzz.binary and b/zzz.binary differ\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, nonbin, "zzz.alphary", changed, strlen(changed), NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.alphary b/zzz.alphary\n" + "index 45141a7..75b0dbb 100644\n" + "--- a/zzz.alphary\n" + "+++ b/zzz.alphary\n" + "@@ -1,0 +2,3 @@ Hello from the root\n" + "+More lines\n" + "+And more\n" + "+Go here\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, nonbin, "zzz.numary", changed, strlen(changed), NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.numary b/zzz.numary\n" + "index 45141a7..75b0dbb 100644\n" + "--- a/zzz.numary\n" + "+++ b/zzz.numary\n" + "@@ -1,0 +2,3 @@\n" + "+More lines\n" + "+And more\n" + "+Go here\n", pout); + git__free(pout); + git_diff_patch_free(p); + + /* "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n" + * 33 bytes + */ + + changed = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\nreplace a line\n"; + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, bin, "zzz.normal", changed, 37, NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.normal b/zzz.normal\n" + "index b435cd5..1604519 100644\n" + "Binary files a/zzz.normal and b/zzz.normal differ\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, bin, "zzz.textary", changed, 37, NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.textary b/zzz.textary\n" + "index b435cd5..1604519 100644\n" + "--- a/zzz.textary\n" + "+++ b/zzz.textary\n" + "@@ -3 +3 @@\n" + "-0123456789\n" + "+replace a line\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, bin, "zzz.textalphary", changed, 37, NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.textalphary b/zzz.textalphary\n" + "index b435cd5..1604519 100644\n" + "--- a/zzz.textalphary\n" + "+++ b/zzz.textalphary\n" + "@@ -3 +3 @@\n" + "-0123456789\n" + "+replace a line\n", pout); + git__free(pout); + git_diff_patch_free(p); + + cl_git_pass(git_diff_patch_from_blob_and_buffer( + &p, bin, "zzz.textnumary", changed, 37, NULL, &opts)); + cl_git_pass(git_diff_patch_to_str(&pout, p)); + cl_assert_equal_s( + "diff --git a/zzz.textnumary b/zzz.textnumary\n" + "index b435cd5..1604519 100644\n" + "--- a/zzz.textnumary\n" + "+++ b/zzz.textnumary\n" + "@@ -3 +3 @@ 0123456789\n" + "-0123456789\n" + "+replace a line\n", pout); + git__free(pout); + git_diff_patch_free(p); + + git_blob_free(nonbin); + git_blob_free(bin); +} From f0f2ff9cacc17dd9d4523e3647990e5db6013f20 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 17 Jun 2013 17:33:40 -0500 Subject: [PATCH 357/384] test failure when renames produce similar similarities --- tests-clar/diff/rename.c | 70 ++++++++++++++++++ .../17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 | 5 ++ .../50/e90273af7d826ff0a95865bcd3ba8412c447d9 | 3 + .../b9/25b224cc91f897001a9993fbce169fdaa8858f | Bin 0 -> 76 bytes .../ea/c43f5195a2cee53b7458d8dad16aedde10711b | Bin 0 -> 118 bytes .../.gitted/refs/heads/renames_similar_two | 1 + 6 files changed, 79 insertions(+) create mode 100644 tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 create mode 100644 tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 create mode 100644 tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f create mode 100644 tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b create mode 100644 tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 2600bd872..103ecd681 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -908,6 +908,76 @@ void test_diff_rename__rejected_match_can_match_others(void) git_buf_free(&two); } +static void write_similarity_file_two(const char *filename, size_t b_lines) +{ + git_buf contents = GIT_BUF_INIT; + size_t i; + + for (i = 0; i < b_lines; i++) + git_buf_printf(&contents, "%0.2d - bbbbb\r\n", (i+1)); + + for (i = b_lines; i < 50; i++) + git_buf_printf(&contents, "%0.2d - aaaaa%s", (i+1), (i == 49 ? "" : "\r\n")); + + cl_git_pass( + git_futils_writebuffer(&contents, filename, O_RDWR|O_CREAT, 0777)); + + git_buf_free(&contents); +} + +void test_diff_rename__rejected_match_can_match_others_two(void) +{ + git_reference *head, *selfsimilar; + git_index *index; + git_tree *tree; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; + const char *sources[] = { "a.txt", "b.txt" }; + const char *targets[] = { "c.txt", "d.txt" }; + struct rename_expected expect = { 2, sources, targets }; + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); + cl_git_pass(git_reference_symbolic_set_target( + &selfsimilar, head, "refs/heads/renames_similar_two")); + cl_git_pass(git_checkout_head(g_repo, &opts)); + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(p_unlink("renames/a.txt")); + cl_git_pass(p_unlink("renames/b.txt")); + + cl_git_pass(git_index_remove_bypath(index, "a.txt")); + cl_git_pass(git_index_remove_bypath(index, "b.txt")); + + write_similarity_file_two("renames/c.txt", 7); + write_similarity_file_two("renames/d.txt", 8); + + cl_git_pass(git_index_add_bypath(index, "c.txt")); + cl_git_pass(git_index_add_bypath(index, "d.txt")); + + cl_git_pass(git_index_write(index)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass( + git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + cl_git_pass(git_diff_find_similar(diff, &findopts)); + + cl_git_pass( + git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + cl_assert(expect.idx > 0); + + git_diff_list_free(diff); + git_tree_free(tree); + git_index_free(index); + git_reference_free(head); + git_reference_free(selfsimilar); +} + void test_diff_rename__case_changes_are_split(void) { git_index *index; diff --git a/tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 b/tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 new file mode 100644 index 000000000..01801ed11 --- /dev/null +++ b/tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 @@ -0,0 +1,5 @@ +xEͱ @QbWq H_&{]yYX`='흶=ZohzF + + + +MhBЄ&4 MhB3ьf4hF3юKx \ No newline at end of file diff --git a/tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 b/tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 new file mode 100644 index 000000000..a98d14ee7 --- /dev/null +++ b/tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 @@ -0,0 +1,3 @@ +xmM +0`9\@OdE@&4hLI"]c}bծaBORvΡ5" +b0[kLopͭU˺SC; 8 hsF_le2}ɩ-!Dg4*IDO;!~)>m 䮔~*D \ No newline at end of file diff --git a/tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f b/tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f new file mode 100644 index 0000000000000000000000000000000000000000..90e107fa23ca0751d98697725732e1cdd0ab5ec9 GIT binary patch literal 76 zcmV-S0JHyi0V^p=O;s>6V=y!@Ff%bxNYpE-C}DVY#6EE9qH|BJOCoOEx|sF$oj$%n{?~}#xW_ZK=B>@CYpwtzWgk$b0V4PS literal 0 HcmV?d00001 diff --git a/tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b b/tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b new file mode 100644 index 0000000000000000000000000000000000000000..2fb025080a1f4cacbcf85725d87c9670294029f3 GIT binary patch literal 118 zcmbD@P%*WTQIcm3L{m%75XED~Lt;i?^Van`jh%S_j1`esL61~M1&=4Loc-@4Fd24w1P Sx!}YNBer?}X53-X69oV<`8>G* literal 0 HcmV?d00001 diff --git a/tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two b/tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two new file mode 100644 index 000000000..4ee5d04e1 --- /dev/null +++ b/tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two @@ -0,0 +1 @@ +50e90273af7d826ff0a95865bcd3ba8412c447d9 From 3b334075c909f9023f5f704469965cf774efc4a5 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 17 Jun 2013 17:39:59 -0500 Subject: [PATCH 358/384] test illustrating tri-cyclic rename failure --- tests-clar/diff/rename.c | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 103ecd681..e6bf72c17 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -650,6 +650,59 @@ void test_diff_rename__file_exchange(void) git_buf_free(&c2); } +void test_diff_rename__file_exchange_three(void) +{ + git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT, c3 = GIT_BUF_INIT; + git_index *index; + git_tree *tree; + git_diff_list *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + + cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt")); + cl_git_pass(git_futils_readbuffer(&c2, "renames/songof7cities.txt")); + cl_git_pass(git_futils_readbuffer(&c3, "renames/ikeepsix.txt")); + + cl_git_pass(git_futils_writebuffer(&c1, "renames/ikeepsix.txt", 0, 0)); + cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0)); + cl_git_pass(git_futils_writebuffer(&c3, "renames/songof7cities.txt", 0, 0)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_read_tree(index, tree)); + cl_git_pass(git_index_add_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_add_bypath(index, "untimely.txt")); + cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt")); + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_RENAMED]); + + git_diff_list_free(diff); + git_tree_free(tree); + git_index_free(index); + + git_buf_free(&c1); + git_buf_free(&c2); + git_buf_free(&c3); +} + void test_diff_rename__file_partial_exchange(void) { git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT; From e4acc3ba19838d39123131d4cc7e52f222691445 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 18 Jun 2013 16:14:35 -0700 Subject: [PATCH 359/384] Fix rename looped reference issues This makes the diff rename tracking code more careful about the order in which it processes renames and more thorough in updating the mapping of correct renames when an earlier rename update alters the index of a later matched pair. --- src/diff_print.c | 4 +- src/diff_tform.c | 281 ++++++++++++++++++++++----------------- tests-clar/diff/rename.c | 8 +- 3 files changed, 167 insertions(+), 126 deletions(-) diff --git a/src/diff_print.c b/src/diff_print.c index 30f221a62..0de548813 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -46,7 +46,7 @@ static char diff_pick_suffix(int mode) { if (S_ISDIR(mode)) return '/'; - else if (mode & 0100) //-V536 + else if (mode & 0100) /* -V536 */ /* in git, modes are very regular, so we must have 0100755 mode */ return '*'; else @@ -162,7 +162,7 @@ static int diff_print_one_raw( if (delta->similarity > 0) git_buf_printf(out, "%03u", delta->similarity); - if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED) + if (delta->old_file.path != delta->new_file.path) git_buf_printf( out, "\t%s %s\n", delta->old_file.path, delta->new_file.path); else diff --git a/src/diff_tform.c b/src/diff_tform.c index 64746e7dd..8c4e96ecf 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -673,6 +673,15 @@ GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta) delta->status == GIT_DELTA_IGNORED); } +GIT_INLINE(void) delta_make_rename( + git_diff_delta *to, const git_diff_delta *from, uint32_t similarity) +{ + to->status = GIT_DELTA_RENAMED; + to->similarity = similarity; + memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); + to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; +} + typedef struct { uint32_t idx; uint32_t similarity; @@ -682,13 +691,15 @@ int git_diff_find_similar( git_diff_list *diff, git_diff_find_options *given_opts) { - size_t i, j, cache_size; + size_t i, j, sigcache_size; int error = 0, similarity; git_diff_delta *from, *to; git_diff_find_options opts; - size_t num_rewrites = 0, num_updates = 0; - void **cache; /* cache of similarity metric file signatures */ - diff_find_match *match_sources, *match_targets; /* cache of best matches */ + size_t num_srcs = 0, num_tgts = 0, tried_srcs = 0, tried_tgts = 0; + size_t num_rewrites = 0, num_updates = 0, num_bumped = 0; + void **sigcache; /* cache of similarity metric file signatures */ + diff_find_match *match_srcs = NULL, *match_tgts = NULL, *best_match; + git_diff_file swap; if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0) return error; @@ -697,70 +708,109 @@ int git_diff_find_similar( if (!git__is_uint32(diff->deltas.length)) return 0; - cache_size = diff->deltas.length * 2; /* must store b/c length may change */ - cache = git__calloc(cache_size, sizeof(void *)); - GITERR_CHECK_ALLOC(cache); - - match_sources = git__calloc(diff->deltas.length, sizeof(diff_find_match)); - match_targets = git__calloc(diff->deltas.length, sizeof(diff_find_match)); - GITERR_CHECK_ALLOC(match_sources); - GITERR_CHECK_ALLOC(match_targets); - - /* next find the most similar delta for each rename / copy candidate */ + sigcache_size = diff->deltas.length * 2; /* keep size b/c diff may change */ + sigcache = git__calloc(sigcache_size, sizeof(void *)); + GITERR_CHECK_ALLOC(sigcache); + /* Label rename sources and targets + * + * This will also set self-similarity scores for MODIFIED files and + * mark them for splitting if break-rewrites is enabled + */ git_vector_foreach(&diff->deltas, i, to) { - size_t tried_sources = 0; + if (is_rename_source(diff, &opts, i, sigcache)) + ++num_srcs; - match_targets[i].idx = (uint32_t)i; - match_targets[i].similarity = 0; - - /* skip things that are not rename targets */ - if (!is_rename_target(diff, &opts, i, cache)) - continue; - - git_vector_foreach(&diff->deltas, j, from) { - if (i == j) - continue; - - /* skip things that are not rename sources */ - if (!is_rename_source(diff, &opts, j, cache)) - continue; - - /* cap on maximum targets we'll examine (per "to" file) */ - if (++tried_sources > opts.rename_limit) - break; - - /* calculate similarity for this pair and find best match */ - if ((error = similarity_measure( - &similarity, diff, &opts, cache, 2 * j, 2 * i + 1)) < 0) - goto cleanup; - - if (similarity < 0) { /* not actually comparable */ - --tried_sources; - continue; - } - - if (match_targets[i].similarity < (uint32_t)similarity && - match_sources[j].similarity < (uint32_t)similarity) { - match_targets[i].similarity = (uint32_t)similarity; - match_sources[j].similarity = (uint32_t)similarity; - match_targets[i].idx = (uint32_t)j; - match_sources[j].idx = (uint32_t)i; - } - } + if (is_rename_target(diff, &opts, i, sigcache)) + ++num_tgts; } - /* next rewrite the diffs with renames / copies */ + /* if there are no candidate srcs or tgts, we're done */ + if (!num_srcs || !num_tgts) + goto cleanup; + + match_tgts = git__calloc(diff->deltas.length, sizeof(diff_find_match)); + GITERR_CHECK_ALLOC(match_tgts); + match_srcs = git__calloc(diff->deltas.length, sizeof(diff_find_match)); + GITERR_CHECK_ALLOC(match_srcs); + + /* + * Find best-fit matches for rename / copy candidates + */ + +find_best_matches: + tried_tgts = num_bumped = 0; git_vector_foreach(&diff->deltas, i, to) { - /* check if this delta was the target of a similarity */ - if ((similarity = (int)match_targets[i].similarity) <= 0) + /* skip things that are not rename targets */ + if ((to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0) continue; - assert(to && (to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) != 0); + tried_srcs = 0; - from = GIT_VECTOR_GET(&diff->deltas, match_targets[i].idx); - assert(from && (from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) != 0); + git_vector_foreach(&diff->deltas, j, from) { + /* skip things that are not rename sources */ + if ((from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0) + continue; + + /* calculate similarity for this pair and find best match */ + if (i == j) + similarity = -1; /* don't measure self-similarity here */ + else if ((error = similarity_measure( + &similarity, diff, &opts, sigcache, 2 * j, 2 * i + 1)) < 0) + goto cleanup; + + /* if this pairing is better for the src and the tgt, keep it */ + if (similarity > 0 && + match_tgts[i].similarity < (uint32_t)similarity && + match_srcs[j].similarity < (uint32_t)similarity) + { + if (match_tgts[i].similarity > 0) { + match_tgts[match_srcs[j].idx].similarity = 0; + match_srcs[match_tgts[i].idx].similarity = 0; + ++num_bumped; + } + + match_tgts[i].similarity = (uint32_t)similarity; + match_tgts[i].idx = (uint32_t)j; + + match_srcs[j].similarity = (uint32_t)similarity; + match_srcs[j].idx = (uint32_t)i; + } + + if (++tried_srcs >= num_srcs) + break; + + /* cap on maximum targets we'll examine (per "to" file) */ + if (tried_srcs > opts.rename_limit) + break; + } + + if (++tried_tgts >= num_tgts) + break; + } + + if (num_bumped > 0) /* try again if we bumped some items */ + goto find_best_matches; + + /* + * Rewrite the diffs with renames / copies + */ + + tried_tgts = 0; + + git_vector_foreach(&diff->deltas, i, to) { + /* skip things that are not rename targets */ + if ((to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0) + continue; + + /* check if this delta was the target of a similarity */ + best_match = &match_tgts[i]; + if (!best_match->similarity) + continue; + + j = best_match->idx; + from = GIT_VECTOR_GET(&diff->deltas, j); /* possible scenarios: * 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME @@ -774,99 +824,84 @@ int git_diff_find_similar( if (delta_is_new_only(to)) { - if (similarity < (int)opts.rename_threshold) + if (best_match->similarity < opts.rename_threshold) continue; - from->status = GIT_DELTA_RENAMED; - from->similarity = (uint32_t)similarity; - memcpy(&from->new_file, &to->new_file, sizeof(from->new_file)); - - to->flags |= GIT_DIFF_FLAG__TO_DELETE; + delta_make_rename(to, from, best_match->similarity); + from->flags |= GIT_DIFF_FLAG__TO_DELETE; num_rewrites++; } else { assert(delta_is_split(to)); - if (similarity < (int)opts.rename_from_rewrite_threshold) + if (best_match->similarity < opts.rename_from_rewrite_threshold) continue; - from->status = GIT_DELTA_RENAMED; - from->similarity = (uint32_t)similarity; - memcpy(&from->new_file, &to->new_file, sizeof(from->new_file)); + memcpy(&swap, &to->old_file, sizeof(swap)); - to->status = GIT_DELTA_DELETED; - memset(&to->new_file, 0, sizeof(to->new_file)); - to->new_file.path = to->old_file.path; - to->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; - if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { - to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; - num_rewrites--; - } + delta_make_rename(to, from, best_match->similarity); + num_rewrites--; + + from->status = GIT_DELTA_DELETED; + memcpy(&from->old_file, &swap, sizeof(from->old_file)); + memset(&from->new_file, 0, sizeof(from->new_file)); + from->new_file.path = from->old_file.path; + from->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; num_updates++; } } else if (delta_is_split(from)) { - git_diff_file swap; if (delta_is_new_only(to)) { - if (similarity < (int)opts.rename_threshold) + if (best_match->similarity < opts.rename_threshold) continue; - memcpy(&swap, &from->new_file, sizeof(swap)); + delta_make_rename(to, from, best_match->similarity); - from->status = GIT_DELTA_RENAMED; - from->similarity = (uint32_t)similarity; - memcpy(&from->new_file, &to->new_file, sizeof(from->new_file)); - if ((from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { - from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; - num_rewrites--; - } - - to->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ? + from->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ? GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED; - memcpy(&to->new_file, &swap, sizeof(to->new_file)); - to->old_file.path = to->new_file.path; + memset(&from->old_file, 0, sizeof(from->old_file)); + from->old_file.path = from->new_file.path; + from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; + + from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; + num_rewrites--; num_updates++; } else { assert(delta_is_split(from)); - if (similarity < (int)opts.rename_from_rewrite_threshold) + if (best_match->similarity < opts.rename_from_rewrite_threshold) continue; - memcpy(&swap, &to->new_file, sizeof(swap)); + memcpy(&swap, &to->old_file, sizeof(swap)); - to->status = GIT_DELTA_RENAMED; - to->similarity = (uint32_t)similarity; - memcpy(&to->new_file, &from->new_file, sizeof(to->new_file)); - if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) { - to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; - num_rewrites--; - } + delta_make_rename(to, from, best_match->similarity); + num_rewrites--; + num_updates++; - memcpy(&from->new_file, &swap, sizeof(from->new_file)); - if ((from->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0) { - from->flags |= GIT_DIFF_FLAG__TO_SPLIT; - num_rewrites++; - } + memcpy(&from->old_file, &swap, sizeof(from->old_file)); - /* in the off chance that we've just swapped the new - * element into the correct place, clear the SPLIT flag + /* if we've just swapped the new element into the correct + * place, clear the SPLIT flag */ - if (match_targets[match_targets[i].idx].idx == i && - match_targets[match_targets[i].idx].similarity > + if (match_tgts[j].idx == i && + match_tgts[j].similarity > opts.rename_from_rewrite_threshold) { - from->status = GIT_DELTA_RENAMED; - from->similarity = - (uint32_t)match_targets[match_targets[i].idx].similarity; - match_targets[match_targets[i].idx].similarity = 0; + from->status = GIT_DELTA_RENAMED; + from->similarity = match_tgts[j].similarity; + match_tgts[j].similarity = 0; from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; num_rewrites--; } + /* otherwise, if we just overwrote a source, update mapping */ + else if (j > i && match_srcs[i].similarity > 0) { + match_tgts[match_srcs[i].idx].idx = j; + } num_updates++; } @@ -874,31 +909,35 @@ int git_diff_find_similar( else if (delta_is_new_only(to)) { if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES) || - similarity < (int)opts.copy_threshold) + best_match->similarity < opts.copy_threshold) continue; - to->status = GIT_DELTA_COPIED; - to->similarity = (uint32_t)similarity; + to->status = GIT_DELTA_COPIED; + to->similarity = best_match->similarity; memcpy(&to->old_file, &from->old_file, sizeof(to->old_file)); num_updates++; } } + /* + * Actually split and delete entries as needed + */ + if (num_rewrites > 0 || num_updates > 0) error = apply_splits_and_deletes( diff, diff->deltas.length - num_rewrites, FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES)); cleanup: - git__free(match_sources); - git__free(match_targets); + git__free(match_srcs); + git__free(match_tgts); - for (i = 0; i < cache_size; ++i) { - if (cache[i] != NULL) - opts.metric->free_signature(cache[i], opts.metric->payload); + for (i = 0; i < sigcache_size; ++i) { + if (sigcache[i] != NULL) + opts.metric->free_signature(sigcache[i], opts.metric->payload); } - git__free(cache); + git__free(sigcache); if (!given_opts || !given_opts->metric) git__free(opts.metric); diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index e6bf72c17..79f407057 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -755,7 +755,7 @@ void test_diff_rename__file_partial_exchange(void) git_buf_free(&c2); } -void test_diff_rename__file_split(void) +void test_diff_rename__rename_and_copy_from_same_source(void) { git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT; git_index *index; @@ -947,6 +947,7 @@ void test_diff_rename__rejected_match_can_match_others(void) cl_git_pass( git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + cl_git_pass(git_diff_find_similar(diff, &findopts)); cl_git_pass( @@ -967,10 +968,10 @@ static void write_similarity_file_two(const char *filename, size_t b_lines) size_t i; for (i = 0; i < b_lines; i++) - git_buf_printf(&contents, "%0.2d - bbbbb\r\n", (i+1)); + git_buf_printf(&contents, "%0.2d - bbbbb\r\n", (int)(i+1)); for (i = b_lines; i < 50; i++) - git_buf_printf(&contents, "%0.2d - aaaaa%s", (i+1), (i == 49 ? "" : "\r\n")); + git_buf_printf(&contents, "%0.2d - aaaaa%s", (int)(i+1), (i == 49 ? "" : "\r\n")); cl_git_pass( git_futils_writebuffer(&contents, filename, O_RDWR|O_CREAT, 0777)); @@ -1018,6 +1019,7 @@ void test_diff_rename__rejected_match_can_match_others_two(void) cl_git_pass( git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + cl_git_pass(git_diff_find_similar(diff, &findopts)); cl_git_pass( From c41281ad3af420bcfe4afae6acdbe95039290525 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Wed, 19 Jun 2013 13:36:59 +0200 Subject: [PATCH 360/384] CMakeLists: fix zlib linker setup b53671a (Search for zlib unconditional, 2012-12-18) changed things around to always (even on windows, that's what the subject refers to) call FIND_PACKAGE(ZLIB). However, it did not correctly handle the case where ZLIB_LIBRARY is cached, either by the user setting it manually or by an earlier search. In that case, the IF(ZLIB_FOUND) would not trigger (that variable is not cached) and we'd instead use the built-in version. 000e689 (CMake: don't try to use bundled zlib when the system's path is in the cache, 2013-05-12) tried to fix that, but it actually made the problem worse: now with ZLIB_LIBRARY cached, _neither_ of the blocks would execute, resulting in a linker error for me when trying to build such a doubly-configured setup. To fix the issue, we just trust CMake to do the right thing. If ZLIB_LIBRARY is set (either from user or cache) then the find_library in FindZLIB.cmake will use that instead of searching again. So we can unconditionally (for real this time) call FIND_PACKAGE(ZLIB), and just check its result. --- CMakeLists.txt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bdc46d0b3..34d56b3fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,17 +136,15 @@ IF(WIN32 OR AMIGA) ENDIF() # Optional external dependency: zlib -IF(NOT ZLIB_LIBRARY) - # It's optional, but FIND_PACKAGE gives a warning that looks more like an - # error. - FIND_PACKAGE(ZLIB QUIET) -ENDIF() +# It's optional, but FIND_PACKAGE gives a warning that looks more like an +# error. +FIND_PACKAGE(ZLIB QUIET) IF (ZLIB_FOUND) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) LINK_LIBRARIES(${ZLIB_LIBRARIES}) # Fake the message CMake would have shown MESSAGE("-- Found zlib: ${ZLIB_LIBRARY}") -ELSEIF (NOT ZLIB_LIBRARY) +ELSE() MESSAGE( "zlib was not found; using bundled 3rd-party sources." ) INCLUDE_DIRECTORIES(deps/zlib) ADD_DEFINITIONS(-DNO_VIZ -DSTDC -DNO_GZIP) From e91f9a8f28ca58c5ff0450749a57d233a5512f2d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 19 Jun 2013 15:20:59 -0700 Subject: [PATCH 361/384] Add higher level pathspec API Right now, setting up a pathspec to be parsed and processed requires several data structures and a couple of API calls. This adds a new high level data structure that contains all the items that you'll need and high-level APIs that do all of the setup and all of the teardown. This will make it easier to use pathspecs in various places with less repeated code. --- src/pathspec.c | 25 +++++++++++++++++++++++++ src/pathspec.h | 14 ++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/pathspec.c b/src/pathspec.c index 35c79ce82..f029836d0 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -166,3 +166,28 @@ bool git_pathspec_match_path( return false; } + +int git_pathspec_context_init( + git_pathspec_context *ctxt, const git_strarray *paths) +{ + int error = 0; + + memset(ctxt, 0, sizeof(*ctxt)); + + ctxt->prefix = git_pathspec_prefix(paths); + + if ((error = git_pool_init(&ctxt->pool, 1, 0)) < 0 || + (error = git_pathspec_init(&ctxt->pathspec, paths, &ctxt->pool)) < 0) + git_pathspec_context_free(ctxt); + + return error; +} + +void git_pathspec_context_free( + git_pathspec_context *ctxt) +{ + git__free(ctxt->prefix); + git_pathspec_free(&ctxt->pathspec); + git_pool_clear(&ctxt->pool); + memset(ctxt, 0, sizeof(*ctxt)); +} diff --git a/src/pathspec.h b/src/pathspec.h index 43a94baad..f6509df4c 100644 --- a/src/pathspec.h +++ b/src/pathspec.h @@ -37,4 +37,18 @@ extern bool git_pathspec_match_path( bool casefold, const char **matched_pathspec); +/* easy pathspec setup */ + +typedef struct { + char *prefix; + git_vector pathspec; + git_pool pool; +} git_pathspec_context; + +extern int git_pathspec_context_init( + git_pathspec_context *ctxt, const git_strarray *paths); + +extern void git_pathspec_context_free( + git_pathspec_context *ctxt); + #endif From 85b8b18b6a07b74dd2631c3a647ca758660bf298 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 19 Jun 2013 15:22:48 -0700 Subject: [PATCH 362/384] Add fn to check pathspec for ignored files Command line Git sometimes generates an error message if given a pathspec that contains an exact match to an ignored file (provided --force isn't also given). This adds an internal function that makes it easy to check it that has happened. Right now, I'm not creating a public API for this because that would get a little more complicated with a need for callbacks for all invalid paths. --- src/ignore.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ignore.h | 9 ++++++++ 2 files changed, 67 insertions(+) diff --git a/src/ignore.c b/src/ignore.c index e150b9585..cc90b0c61 100644 --- a/src/ignore.c +++ b/src/ignore.c @@ -340,3 +340,61 @@ cleanup: return error; } + +int git_ignore__check_pathspec_for_exact_ignores( + git_repository *repo, + git_vector *vspec, + bool no_fnmatch) +{ + int error = 0; + size_t i; + git_attr_fnmatch *match; + int ignored; + git_buf path = GIT_BUF_INIT; + const char *wd, *filename; + git_index *idx; + + if ((error = git_repository__ensure_not_bare( + repo, "validate pathspec")) < 0 || + (error = git_repository_index(&idx, repo)) < 0) + return error; + + wd = git_repository_workdir(repo); + + git_vector_foreach(vspec, i, match) { + /* skip wildcard matches (if they are being used) */ + if ((match->flags & GIT_ATTR_FNMATCH_HASWILD) != 0 && + !no_fnmatch) + continue; + + filename = match->pattern; + + /* if file is already in the index, it's fine */ + if (git_index_get_bypath(idx, filename, 0) != NULL) + continue; + + if ((error = git_buf_joinpath(&path, wd, filename)) < 0) + break; + + /* is there a file on disk that matches this exactly? */ + if (!git_path_isfile(path.ptr)) + continue; + + /* is that file ignored? */ + if ((error = git_ignore_path_is_ignored(&ignored, repo, filename)) < 0) + break; + + if (ignored) { + giterr_set(GITERR_INVALID, "pathspec contains ignored file '%s'", + filename); + error = GIT_EINVALIDSPEC; + break; + } + } + + git_index_free(idx); + git_buf_free(&path); + + return error; +} + diff --git a/src/ignore.h b/src/ignore.h index e00e4a8c8..cc114b001 100644 --- a/src/ignore.h +++ b/src/ignore.h @@ -41,4 +41,13 @@ extern void git_ignore__free(git_ignores *ign); extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored); +/* command line Git sometimes generates an error message if given a + * pathspec that contains an exact match to an ignored file (provided + * --force isn't also given). This makes it easy to check it that has + * happened. Returns GIT_EINVALIDSPEC if the pathspec contains ignored + * exact matches (that are not already present in the index). + */ +extern int git_ignore__check_pathspec_for_exact_ignores( + git_repository *repo, git_vector *pathspec, bool no_fnmatch); + #endif From f30fff45a752cb0781067ad48c283e49345a5813 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 19 Jun 2013 15:27:25 -0700 Subject: [PATCH 363/384] Add index pathspec-based operations This adds three new public APIs for manipulating the index: 1. `git_index_add_all` is similar to `git add -A` and will add files in the working directory that match a pathspec to the index while honoring ignores, etc. 2. `git_index_remove_all` removes files from the index that match a pathspec. 3. `git_index_update_all` updates entries in the index based on the current contents of the working directory, either added the new information or removing the entry from the index. --- include/git2/index.h | 115 +++++++++++++++++ src/diff.c | 6 +- src/index.c | 215 +++++++++++++++++++++++++++++++- tests-clar/index/addall.c | 254 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 586 insertions(+), 4 deletions(-) create mode 100644 tests-clar/index/addall.c diff --git a/include/git2/index.h b/include/git2/index.h index 58b0243e0..399d7c9a8 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -11,6 +11,7 @@ #include "indexer.h" #include "types.h" #include "oid.h" +#include "strarray.h" /** * @file git2/index.h @@ -125,6 +126,18 @@ typedef enum { GIT_INDEXCAP_FROM_OWNER = ~0u } git_indexcap_t; +/** Callback for APIs that add/remove/update files matching pathspec */ +typedef int (*git_index_matched_path_cb)( + const char *path, const char *matched_pathspec, void *payload); + +/** Flags for APIs that add files matching pathspec */ +typedef enum { + GIT_INDEX_ADD_DEFAULT = 0, + GIT_INDEX_ADD_FORCE = (1u << 0), + GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH = (1u << 1), + GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2), +} git_index_add_option_t; + /** @name Index File Functions * * These functions work on the index file itself. @@ -420,6 +433,108 @@ GIT_EXTERN(int) git_index_add_bypath(git_index *index, const char *path); */ GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path); +/** + * Add or update index entries matching files in the working directory. + * + * This method will fail in bare index instances. + * + * The `pathspec` is a list of file names or shell glob patterns that will + * matched against files in the repository's working directory. Each file + * that matches will be added to the index (either updating an existing + * entry or adding a new entry). You can disable glob expansion and force + * exact matching with the `GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH` flag. + * + * Files that are ignored will be skipped (unlike `git_index_add_bypath`). + * If a file is already tracked in the index, then it *will* be updated + * even if it is ignored. Pass the `GIT_INDEX_ADD_FORCE` flag to + * skip the checking of ignore rules. + * + * To emulate `git add -A` and generate an error if the pathspec contains + * the exact path of an ignored file (when not using FORCE), add the + * `GIT_INDEX_ADD_CHECK_PATHSPEC` flag. This checks that each entry + * in the `pathspec` that is an exact match to a filename on disk is + * either not ignored or already in the index. If this check fails, the + * function will return GIT_EINVALIDSPEC. + * + * To emulate `git add -A` with the "dry-run" option, just use a callback + * function that always returns a positive value. See below for details. + * + * If any files are currently the result of a merge conflict, those files + * will no longer be marked as conflicting. The data about the conflicts + * will be moved to the "resolve undo" (REUC) section. + * + * If you provide a callback function, it will be invoked on each matching + * item in the working directory immediately *before* it is added to / + * updated in the index. Returning zero will add the item to the index, + * greater than zero will skip the item, and less than zero will abort the + * scan and cause GIT_EUSER to be returned. + * + * @param index an existing index object + * @param pathspec array of path patterns + * @param flags combination of git_index_add_option_t flags + * @param callback notification callback for each added/updated path (also + * gets index of matching pathspec entry); can be NULL; + * return 0 to add, >0 to skip, <0 to abort scan. + * @param payload payload passed through to callback function + * @return 0 or an error code + */ +GIT_EXTERN(int) git_index_add_all( + git_index *index, + const git_strarray *pathspec, + unsigned int flags, + git_index_matched_path_cb callback, + void *payload); + +/** + * Remove all matching index entries. + * + * If you provide a callback function, it will be invoked on each matching + * item in the index immediately *before* it is removed. Return 0 to + * remove the item, > 0 to skip the item, and < 0 to abort the scan. + * + * @param index An existing index object + * @param pathspec array of path patterns + * @param callback notification callback for each removed path (also + * gets index of matching pathspec entry); can be NULL; + * return 0 to add, >0 to skip, <0 to abort scan. + * @param payload payload passed through to callback function + * @return 0 or an error code + */ +GIT_EXTERN(int) git_index_remove_all( + git_index *index, + const git_strarray *pathspec, + git_index_matched_path_cb callback, + void *payload); + +/** + * Update all index entries to match the working directory + * + * This method will fail in bare index instances. + * + * This scans the existing index entries and synchronizes them with the + * working directory, deleting them if the corresponding working directory + * file no longer exists otherwise updating the information (including + * adding the latest version of file to the ODB if needed). + * + * If you provide a callback function, it will be invoked on each matching + * item in the index immediately *before* it is updated (either refreshed + * or removed depending on working directory state). Return 0 to proceed + * with updating the item, > 0 to skip the item, and < 0 to abort the scan. + * + * @param index An existing index object + * @param pathspec array of path patterns + * @param callback notification callback for each updated path (also + * gets index of matching pathspec entry); can be NULL; + * return 0 to add, >0 to skip, <0 to abort scan. + * @param payload payload passed through to callback function + * @return 0 or an error code + */ +GIT_EXTERN(int) git_index_update_all( + git_index *index, + const git_strarray *pathspec, + git_index_matched_path_cb callback, + void *payload); + /** * Find the first position of any entries which point to given * path in the Git index. diff --git a/src/diff.c b/src/diff.c index fa2c5c71d..633601699 100644 --- a/src/diff.c +++ b/src/diff.c @@ -675,8 +675,10 @@ static int maybe_modified( } } - /* if oids and modes match, then file is unmodified */ - else if (git_oid_equal(&oitem->oid, &nitem->oid) && omode == nmode) + /* if oids and modes match (and are valid), then file is unmodified */ + else if (git_oid_equal(&oitem->oid, &nitem->oid) && + omode == nmode && + !git_oid_iszero(&oitem->oid)) status = GIT_DELTA_UNMODIFIED; /* if we have an unknown OID and a workdir iterator, then check some diff --git a/src/index.c b/src/index.c index 560a257e7..e65dc052c 100644 --- a/src/index.c +++ b/src/index.c @@ -15,6 +15,8 @@ #include "hash.h" #include "iterator.h" #include "pathspec.h" +#include "ignore.h" + #include "git2/odb.h" #include "git2/oid.h" #include "git2/blob.h" @@ -997,7 +999,7 @@ static int index_conflict__get_byindex( int stage, len = 0; assert(ancestor_out && our_out && their_out && index); - + *ancestor_out = NULL; *our_out = NULL; *their_out = NULL; @@ -1010,7 +1012,7 @@ static int index_conflict__get_byindex( stage = GIT_IDXENTRY_STAGE(conflict_entry); path = conflict_entry->path; - + switch (stage) { case 3: *their_out = conflict_entry; @@ -2044,3 +2046,212 @@ git_repository *git_index_owner(const git_index *index) { return INDEX_OWNER(index); } + +int git_index_add_all( + git_index *index, + const git_strarray *paths, + unsigned int flags, + git_index_matched_path_cb cb, + void *payload) +{ + int error; + git_repository *repo; + git_iterator *wditer = NULL; + const git_index_entry *wd = NULL; + git_index_entry *entry; + git_pathspec_context ps; + const char *match; + size_t existing; + bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0; + int ignorecase; + git_oid blobid; + + assert(index); + + if (INDEX_OWNER(index) == NULL) + return create_index_error(-1, + "Could not add paths to index. " + "Index is not backed up by an existing repository."); + + repo = INDEX_OWNER(index); + if ((error = git_repository__ensure_not_bare(repo, "index add all")) < 0) + return error; + + if (git_repository__cvar(&ignorecase, repo, GIT_CVAR_IGNORECASE) < 0) + return -1; + + if ((error = git_pathspec_context_init(&ps, paths)) < 0) + return error; + + /* optionally check that pathspec doesn't mention any ignored files */ + if ((flags & GIT_INDEX_ADD_CHECK_PATHSPEC) != 0 && + (flags & GIT_INDEX_ADD_FORCE) == 0 && + (error = git_ignore__check_pathspec_for_exact_ignores( + repo, &ps.pathspec, no_fnmatch)) < 0) + goto cleanup; + + if ((error = git_iterator_for_workdir( + &wditer, repo, 0, ps.prefix, ps.prefix)) < 0) + goto cleanup; + + while (!(error = git_iterator_advance(&wd, wditer))) { + + /* check if path actually matches */ + if (!git_pathspec_match_path( + &ps.pathspec, wd->path, no_fnmatch, ignorecase, &match)) + continue; + + /* skip ignored items that are not already in the index */ + if ((flags & GIT_INDEX_ADD_FORCE) == 0 && + git_iterator_current_is_ignored(wditer) && + index_find(&existing, index, wd->path, 0) < 0) + continue; + + /* issue notification callback if requested */ + if (cb && (error = cb(wd->path, match, payload)) != 0) { + if (error > 0) /* return > 0 means skip this one */ + continue; + if (error < 0) { /* return < 0 means abort */ + giterr_clear(); + error = GIT_EUSER; + break; + } + } + + /* TODO: Should we check if the file on disk is already an exact + * match to the file in the index and skip this work if it is? + */ + + /* write the blob to disk and get the oid */ + if ((error = git_blob_create_fromworkdir(&blobid, repo, wd->path)) < 0) + break; + + /* make the new entry to insert */ + if ((entry = index_entry_dup(wd)) == NULL) { + error = -1; + break; + } + entry->oid = blobid; + + /* add working directory item to index */ + if ((error = index_insert(index, entry, 1)) < 0) { + index_entry_free(entry); + break; + } + + git_tree_cache_invalidate_path(index->tree, wd->path); + + /* add implies conflict resolved, move conflict entries to REUC */ + if ((error = index_conflict_to_reuc(index, wd->path)) < 0) { + if (error != GIT_ENOTFOUND) + break; + giterr_clear(); + } + } + + if (error == GIT_ITEROVER) + error = 0; + +cleanup: + git_iterator_free(wditer); + git_pathspec_context_free(&ps); + + return error; +} + +enum { + INDEX_ACTION_NONE = 0, + INDEX_ACTION_UPDATE = 1, + INDEX_ACTION_REMOVE = 2, +}; + +static int index_apply_to_all( + git_index *index, + int action, + const git_strarray *paths, + git_index_matched_path_cb cb, + void *payload) +{ + int error = 0; + size_t i; + git_pathspec_context ps; + const char *match; + + assert(index); + + if ((error = git_pathspec_context_init(&ps, paths)) < 0) + return error; + + git_vector_sort(&index->entries); + + for (i = 0; !error && i < index->entries.length; ++i) { + git_index_entry *entry = git_vector_get(&index->entries, i); + + /* check if path actually matches */ + if (!git_pathspec_match_path( + &ps.pathspec, entry->path, false, index->ignore_case, &match)) + continue; + + /* issue notification callback if requested */ + if (cb && (error = cb(entry->path, match, payload)) != 0) { + if (error > 0) { /* return > 0 means skip this one */ + error = 0; + continue; + } + if (error < 0) { /* return < 0 means abort */ + giterr_clear(); + error = GIT_EUSER; + break; + } + } + + switch (action) { + case INDEX_ACTION_NONE: + break; + case INDEX_ACTION_UPDATE: + error = git_index_add_bypath(index, entry->path); + + if (error == GIT_ENOTFOUND) { + giterr_clear(); + + error = git_index_remove_bypath(index, entry->path); + + if (!error) /* back up foreach if we removed this */ + i--; + } + break; + case INDEX_ACTION_REMOVE: + if (!(error = git_index_remove_bypath(index, entry->path))) + i--; /* back up foreach if we removed this */ + break; + default: + giterr_set(GITERR_INVALID, "Unknown index action %d", action); + error = -1; + break; + } + } + + git_pathspec_context_free(&ps); + + return error; +} + +int git_index_remove_all( + git_index *index, + const git_strarray *pathspec, + git_index_matched_path_cb cb, + void *payload) +{ + return index_apply_to_all( + index, INDEX_ACTION_REMOVE, pathspec, cb, payload); +} + +int git_index_update_all( + git_index *index, + const git_strarray *pathspec, + git_index_matched_path_cb cb, + void *payload) +{ + return index_apply_to_all( + index, INDEX_ACTION_UPDATE, pathspec, cb, payload); +} diff --git a/tests-clar/index/addall.c b/tests-clar/index/addall.c new file mode 100644 index 000000000..33873cb7a --- /dev/null +++ b/tests-clar/index/addall.c @@ -0,0 +1,254 @@ +#include "clar_libgit2.h" +#include "../status/status_helpers.h" +#include "posix.h" + +git_repository *g_repo = NULL; + +void test_index_addall__initialize(void) +{ +} + +void test_index_addall__cleanup(void) +{ + git_repository_free(g_repo); + g_repo = NULL; +} + +#define STATUS_INDEX_FLAGS \ + (GIT_STATUS_INDEX_NEW | GIT_STATUS_INDEX_MODIFIED | \ + GIT_STATUS_INDEX_DELETED | GIT_STATUS_INDEX_RENAMED | \ + GIT_STATUS_INDEX_TYPECHANGE) + +#define STATUS_WT_FLAGS \ + (GIT_STATUS_WT_NEW | GIT_STATUS_WT_MODIFIED | \ + GIT_STATUS_WT_DELETED | GIT_STATUS_WT_TYPECHANGE | \ + GIT_STATUS_WT_RENAMED) + +typedef struct { + size_t index_adds; + size_t index_dels; + size_t index_mods; + size_t wt_adds; + size_t wt_dels; + size_t wt_mods; + size_t ignores; +} index_status_counts; + +static int index_status_cb( + const char *path, unsigned int status_flags, void *payload) +{ + index_status_counts *vals = payload; + + /* cb_status__print(path, status_flags, NULL); */ + + GIT_UNUSED(path); + + if (status_flags & GIT_STATUS_INDEX_NEW) + vals->index_adds++; + if (status_flags & GIT_STATUS_INDEX_MODIFIED) + vals->index_mods++; + if (status_flags & GIT_STATUS_INDEX_DELETED) + vals->index_dels++; + if (status_flags & GIT_STATUS_INDEX_TYPECHANGE) + vals->index_mods++; + + if (status_flags & GIT_STATUS_WT_NEW) + vals->wt_adds++; + if (status_flags & GIT_STATUS_WT_MODIFIED) + vals->wt_mods++; + if (status_flags & GIT_STATUS_WT_DELETED) + vals->wt_dels++; + if (status_flags & GIT_STATUS_WT_TYPECHANGE) + vals->wt_mods++; + + if (status_flags & GIT_STATUS_IGNORED) + vals->ignores++; + + return 0; +} + +static void check_status( + git_repository *repo, + size_t index_adds, size_t index_dels, size_t index_mods, + size_t wt_adds, size_t wt_dels, size_t wt_mods, size_t ignores) +{ + index_status_counts vals; + + memset(&vals, 0, sizeof(vals)); + + cl_git_pass(git_status_foreach(repo, index_status_cb, &vals)); + + cl_assert_equal_sz(index_adds, vals.index_adds); + cl_assert_equal_sz(index_dels, vals.index_dels); + cl_assert_equal_sz(index_mods, vals.index_mods); + cl_assert_equal_sz(wt_adds, vals.wt_adds); + cl_assert_equal_sz(wt_dels, vals.wt_dels); + cl_assert_equal_sz(wt_mods, vals.wt_mods); + cl_assert_equal_sz(ignores, vals.ignores); +} + +static void check_stat_data(git_index *index, const char *path, bool match) +{ + const git_index_entry *entry; + struct stat st; + + cl_must_pass(p_lstat(path, &st)); + + /* skip repo base dir name */ + while (*path != '/') + ++path; + ++path; + + entry = git_index_get_bypath(index, path, 0); + cl_assert(entry); + + if (match) { + cl_assert(st.st_ctime == entry->ctime.seconds); + cl_assert(st.st_mtime == entry->mtime.seconds); + cl_assert(st.st_size == entry->file_size); + cl_assert(st.st_uid == entry->uid); + cl_assert(st.st_gid == entry->gid); + cl_assert_equal_b(st.st_mode & ~0777, entry->mode & ~0777); + cl_assert_equal_b(st.st_mode & 0111, entry->mode & 0111); + } else { + /* most things will still match */ + cl_assert(st.st_size != entry->file_size); + /* would check mtime, but with second resolution it won't work :( */ + } +} + +static void commit_index_to_head( + git_repository *repo, + const char *commit_message) +{ + git_index *index; + git_oid tree_id, commit_id; + git_tree *tree; + git_signature *sig; + git_commit *parent = NULL; + + git_revparse_single((git_object **)&parent, repo, "HEAD"); + /* it is okay if looking up the HEAD fails */ + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_write_tree(&tree_id, index)); + git_index_free(index); + + cl_git_pass(git_tree_lookup(&tree, repo, &tree_id)); + + cl_git_pass(git_signature_now(&sig, "Testy McTester", "tt@tester.test")); + + cl_git_pass(git_commit_create_v( + &commit_id, repo, "HEAD", sig, sig, + NULL, commit_message, tree, parent ? 1 : 0, parent)); + + git_commit_free(parent); + git_tree_free(tree); + git_signature_free(sig); +} + +void test_index_addall__repo_lifecycle(void) +{ + int error; + git_index *index; + git_strarray paths = { NULL, 0 }; + char *strs[1]; + + cl_git_pass(git_repository_init(&g_repo, "addall", false)); + check_status(g_repo, 0, 0, 0, 0, 0, 0, 0); + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_mkfile("addall/file.foo", "a file"); + check_status(g_repo, 0, 0, 0, 1, 0, 0, 0); + + cl_git_mkfile("addall/.gitignore", "*.foo\n"); + check_status(g_repo, 0, 0, 0, 1, 0, 0, 1); + + cl_git_mkfile("addall/file.bar", "another file"); + check_status(g_repo, 0, 0, 0, 2, 0, 0, 1); + + strs[0] = "file.*"; + paths.strings = strs; + paths.count = 1; + + cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); + check_stat_data(index, "addall/file.bar", true); + check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); + + cl_git_rewritefile("addall/file.bar", "new content for file"); + check_stat_data(index, "addall/file.bar", false); + check_status(g_repo, 1, 0, 0, 1, 0, 1, 1); + + cl_git_mkfile("addall/file.zzz", "yet another one"); + cl_git_mkfile("addall/other.zzz", "yet another one"); + cl_git_mkfile("addall/more.zzz", "yet another one"); + check_status(g_repo, 1, 0, 0, 4, 0, 1, 1); + + cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); + check_stat_data(index, "addall/file.bar", true); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + + cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); + check_stat_data(index, "addall/file.zzz", true); + check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); + + commit_index_to_head(g_repo, "first commit"); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); + + /* attempt to add an ignored file - does nothing */ + strs[0] = "file.foo"; + cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); + + /* add with check - should generate error */ + error = git_index_add_all( + index, &paths, GIT_INDEX_ADD_CHECK_PATHSPEC, NULL, NULL); + cl_assert_equal_i(GIT_EINVALIDSPEC, error); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 1); + + /* add with force - should allow */ + cl_git_pass(git_index_add_all( + index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL)); + check_stat_data(index, "addall/file.foo", true); + check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); + + /* now it's in the index, so regular add should work */ + cl_git_rewritefile("addall/file.foo", "new content for file"); + check_stat_data(index, "addall/file.foo", false); + check_status(g_repo, 1, 0, 0, 3, 0, 1, 0); + + cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); + check_stat_data(index, "addall/file.foo", true); + check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); + + cl_git_pass(git_index_add_bypath(index, "more.zzz")); + check_stat_data(index, "addall/more.zzz", true); + check_status(g_repo, 2, 0, 0, 2, 0, 0, 0); + + cl_git_rewritefile("addall/file.zzz", "new content for file"); + check_status(g_repo, 2, 0, 0, 2, 0, 1, 0); + + cl_git_pass(git_index_add_bypath(index, "file.zzz")); + check_stat_data(index, "addall/file.zzz", true); + check_status(g_repo, 2, 0, 1, 2, 0, 0, 0); + + strs[0] = "*.zzz"; + cl_git_pass(git_index_remove_all(index, &paths, NULL, NULL)); + check_status(g_repo, 1, 1, 0, 4, 0, 0, 0); + + cl_git_pass(git_index_add_bypath(index, "file.zzz")); + check_status(g_repo, 1, 0, 1, 3, 0, 0, 0); + + commit_index_to_head(g_repo, "second commit"); + check_status(g_repo, 0, 0, 0, 3, 0, 0, 0); + + cl_must_pass(p_unlink("addall/file.zzz")); + check_status(g_repo, 0, 0, 0, 3, 1, 0, 0); + + /* update_all should be able to remove entries */ + cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); + check_status(g_repo, 0, 1, 0, 3, 0, 0, 0); + + git_index_free(index); +} From 7863523a1be51981bafee9d13b3344fb4ff47347 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 19 Jun 2013 15:54:19 -0700 Subject: [PATCH 364/384] Add tests and fix use of freed memory This adds some tests for updating the index and having it remove items to make sure that the iteration over the index still works even as earlier items are removed. In testing with valgrind, this found a path that would use the path string from the index entry after it had been freed. The bug fix is simply to copy the path of the index entry before doing any actual index manipulation. --- src/index.c | 12 +++++++++--- tests-clar/index/addall.c | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/index.c b/src/index.c index e65dc052c..d5568528b 100644 --- a/src/index.c +++ b/src/index.c @@ -2176,6 +2176,7 @@ static int index_apply_to_all( size_t i; git_pathspec_context ps; const char *match; + git_buf path = GIT_BUF_INIT; assert(index); @@ -2205,23 +2206,27 @@ static int index_apply_to_all( } } + /* index manipulation may alter entry, so don't depend on it */ + if ((error = git_buf_sets(&path, entry->path)) < 0) + break; + switch (action) { case INDEX_ACTION_NONE: break; case INDEX_ACTION_UPDATE: - error = git_index_add_bypath(index, entry->path); + error = git_index_add_bypath(index, path.ptr); if (error == GIT_ENOTFOUND) { giterr_clear(); - error = git_index_remove_bypath(index, entry->path); + error = git_index_remove_bypath(index, path.ptr); if (!error) /* back up foreach if we removed this */ i--; } break; case INDEX_ACTION_REMOVE: - if (!(error = git_index_remove_bypath(index, entry->path))) + if (!(error = git_index_remove_bypath(index, path.ptr))) i--; /* back up foreach if we removed this */ break; default: @@ -2231,6 +2236,7 @@ static int index_apply_to_all( } } + git_buf_free(&path); git_pathspec_context_free(&ps); return error; diff --git a/tests-clar/index/addall.c b/tests-clar/index/addall.c index 33873cb7a..fca6e77fa 100644 --- a/tests-clar/index/addall.c +++ b/tests-clar/index/addall.c @@ -132,6 +132,7 @@ static void commit_index_to_head( cl_git_pass(git_repository_index(&index, repo)); cl_git_pass(git_index_write_tree(&tree_id, index)); + cl_git_pass(git_index_write(index)); /* not needed, but might as well */ git_index_free(index); cl_git_pass(git_tree_lookup(&tree, repo, &tree_id)); @@ -250,5 +251,24 @@ void test_index_addall__repo_lifecycle(void) cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); check_status(g_repo, 0, 1, 0, 3, 0, 0, 0); + strs[0] = "*"; + cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); + check_status(g_repo, 3, 1, 0, 0, 0, 0, 0); + + /* must be able to remove at any position while still updating other files */ + cl_must_pass(p_unlink("addall/.gitignore")); + cl_git_rewritefile("addall/file.zzz", "reconstructed file"); + cl_git_rewritefile("addall/more.zzz", "altered file reality"); + check_status(g_repo, 3, 1, 0, 1, 1, 1, 0); + + cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); + check_status(g_repo, 2, 1, 0, 1, 0, 0, 0); + /* this behavior actually matches 'git add -u' where "file.zzz" has + * been removed from the index, so when you go to update, even though + * it exists in the HEAD, it is not re-added to the index, leaving it + * as a DELETE when comparing HEAD to index and as an ADD comparing + * index to worktree + */ + git_index_free(index); } From 852ded96982ae70acb63c3940fae08ea29e40fee Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 20 Jun 2013 11:37:58 -0700 Subject: [PATCH 365/384] Fix bug in diff untracked dir scan When scanning untracked directories looking for non-ignored files there was a bug where an empty directory would generate a false error. --- src/diff.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/diff.c b/src/diff.c index 633601699..3846a5e1b 100644 --- a/src/diff.c +++ b/src/diff.c @@ -786,10 +786,15 @@ static int diff_scan_inside_untracked_dir( /* need to recurse into non-ignored directories */ if (!is_ignored && S_ISDIR(info->nitem->mode)) { - if ((error = git_iterator_advance_into( - &info->nitem, info->new_iter)) < 0) - break; - continue; + error = git_iterator_advance_into(&info->nitem, info->new_iter); + + if (!error) + continue; + else if (error == GIT_ENOTFOUND) { + error = 0; + is_ignored = true; /* treat empty as ignored */ + } else + break; /* real error, must stop */ } /* found a non-ignored item - treat parent dir as untracked */ From cf300bb9e50c65e4140f7e204243b34f2898fa95 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 20 Jun 2013 11:39:31 -0700 Subject: [PATCH 366/384] Initial implementation of status example --- examples/Makefile | 2 +- examples/status.c | 294 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 examples/status.c diff --git a/examples/Makefile b/examples/Makefile index c5d555566..140cc4da9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -3,7 +3,7 @@ CC = gcc CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers LFLAGS = -L../build -lgit2 -lz -APPS = general showindex diff rev-list cat-file +APPS = general showindex diff rev-list cat-file status all: $(APPS) diff --git a/examples/status.c b/examples/status.c new file mode 100644 index 000000000..2378c78b6 --- /dev/null +++ b/examples/status.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2011-2012 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include +#include +#include +#include + +enum { + FORMAT_DEFAULT = 0, + FORMAT_LONG = 1, + FORMAT_SHORT = 2, + FORMAT_PORCELAIN = 3, +}; +#define MAX_PATHSPEC 8 + +/* + * This example demonstrates the use of `git_status_foreach()` to roughly + * simulate the output of running `git status`. It should serve as a simple + * example of how to get basic status information. + * + * This does not have: + * - Robust error handling + * - Any real command line parsing + * - Colorized or paginated output formatting + * + */ + +static void check(int error, const char *message, const char *extra) +{ + const git_error *lg2err; + const char *lg2msg = "", *lg2spacer = ""; + + if (!error) + return; + + if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) { + lg2msg = lg2err->message; + lg2spacer = " - "; + } + + if (extra) + fprintf(stderr, "%s '%s' [%d]%s%s\n", + message, extra, error, lg2spacer, lg2msg); + else + fprintf(stderr, "%s [%d]%s%s\n", + message, error, lg2spacer, lg2msg); + + exit(1); +} + +static void fail(const char *message) +{ + check(-1, message, NULL); +} + +static void show_branch(git_repository *repo, int format) +{ + int error = 0; + const char *branch = NULL; + git_reference *head = NULL; + + error = git_repository_head(&head, repo); + + if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND) + branch = NULL; + else if (!error) { + branch = git_reference_name(head); + if (!strncmp(branch, "refs/heads/", strlen("refs/heads/"))) + branch += strlen("refs/heads/"); + } else + check(error, "failed to get current branch", NULL); + + if (format == FORMAT_LONG) + printf("# %s\n", branch ? branch : "Not currently on any branch."); + else + printf("## %s\n", branch ? branch : "HEAD (no branch)"); + + git_reference_free(head); +} + +static void print_long(git_repository *repo, git_status_list *status) +{ + (void)repo; + (void)status; +} + +static void print_short(git_repository *repo, git_status_list *status) +{ + size_t i, maxi = git_status_list_entrycount(status); + const git_status_entry *s; + char istatus, wstatus; + const char *extra, *a, *b, *c; + + for (i = 0; i < maxi; ++i) { + s = git_status_byindex(status, i); + + if (s->status == GIT_STATUS_CURRENT) + continue; + + a = b = c = NULL; + istatus = wstatus = ' '; + extra = ""; + + if (s->status & GIT_STATUS_INDEX_NEW) + istatus = 'A'; + if (s->status & GIT_STATUS_INDEX_MODIFIED) + istatus = 'M'; + if (s->status & GIT_STATUS_INDEX_DELETED) + istatus = 'D'; + if (s->status & GIT_STATUS_INDEX_RENAMED) + istatus = 'R'; + if (s->status & GIT_STATUS_INDEX_TYPECHANGE) + istatus = 'T'; + + if (s->status & GIT_STATUS_WT_NEW) { + if (istatus == ' ') + istatus = '?'; + wstatus = '?'; + } + if (s->status & GIT_STATUS_WT_MODIFIED) + wstatus = 'M'; + if (s->status & GIT_STATUS_WT_DELETED) + wstatus = 'D'; + if (s->status & GIT_STATUS_WT_RENAMED) + wstatus = 'R'; + if (s->status & GIT_STATUS_WT_TYPECHANGE) + wstatus = 'T'; + + if (s->status & GIT_STATUS_IGNORED) { + istatus = '!'; + wstatus = '!'; + } + + if (istatus == '?' && wstatus == '?') + continue; + + if (s->index_to_workdir && + s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT) + { + git_submodule *sm = NULL; + unsigned int smstatus = 0; + + if (!git_submodule_lookup( + &sm, repo, s->index_to_workdir->new_file.path) && + !git_submodule_status(&smstatus, sm)) + { + if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED) + extra = " (new commits)"; + else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) + extra = " (modified content)"; + else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) + extra = " (modified content)"; + else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED) + extra = " (untracked content)"; + } + } + + if (s->head_to_index) { + a = s->head_to_index->old_file.path; + b = s->head_to_index->new_file.path; + } + if (s->index_to_workdir) { + if (!a) + a = s->index_to_workdir->old_file.path; + if (!b) + b = s->index_to_workdir->old_file.path; + c = s->index_to_workdir->new_file.path; + } + + if (istatus == 'R') { + if (wstatus == 'R') + printf("%c%c %s %s %s%s\n", istatus, wstatus, a, b, c, extra); + else + printf("%c%c %s %s%s\n", istatus, wstatus, a, b, extra); + } else { + if (wstatus == 'R') + printf("%c%c %s %s%s\n", istatus, wstatus, a, c, extra); + else + printf("%c%c %s%s\n", istatus, wstatus, a, extra); + } + } + + for (i = 0; i < maxi; ++i) { + s = git_status_byindex(status, i); + + if (s->status == GIT_STATUS_WT_NEW) + printf("?? %s\n", s->index_to_workdir->old_file.path); + } +} + +int main(int argc, char *argv[]) +{ + git_repository *repo = NULL; + int i, npaths = 0, format = FORMAT_DEFAULT, zterm = 0, showbranch = 0; + git_status_options opt = GIT_STATUS_OPTIONS_INIT; + git_status_list *status; + char *repodir = ".", *pathspec[MAX_PATHSPEC]; + + opt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; + opt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + + for (i = 1; i < argc; ++i) { + if (argv[i][0] != '-') { + if (npaths < MAX_PATHSPEC) + pathspec[npaths++] = argv[i]; + else + fail("Example only supports a limited pathspec"); + } + else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--short")) + format = FORMAT_SHORT; + else if (!strcmp(argv[i], "--long")) + format = FORMAT_LONG; + else if (!strcmp(argv[i], "--porcelain")) + format = FORMAT_PORCELAIN; + else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--branch")) + showbranch = 1; + else if (!strcmp(argv[i], "-z")) { + zterm = 1; + if (format == FORMAT_DEFAULT) + format = FORMAT_PORCELAIN; + } + else if (!strcmp(argv[i], "--ignored")) + opt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED; + else if (!strcmp(argv[i], "-uno") || + !strcmp(argv[i], "--untracked-files=no")) + opt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED; + else if (!strcmp(argv[i], "-unormal") || + !strcmp(argv[i], "--untracked-files=normal")) + opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + else if (!strcmp(argv[i], "-uall") || + !strcmp(argv[i], "--untracked-files=all")) + opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + else if (!strcmp(argv[i], "--ignore-submodules=all")) + opt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES; + else if (!strncmp(argv[i], "--git-dir=", strlen("--git-dir="))) + repodir = argv[i] + strlen("--git-dir="); + else + check(-1, "Unsupported option", argv[i]); + } + + if (format == FORMAT_DEFAULT) + format = FORMAT_LONG; + if (format == FORMAT_LONG) + showbranch = 1; + if (npaths > 0) { + opt.pathspec.strings = pathspec; + opt.pathspec.count = npaths; + } + + /* + * Try to open the repository at the given path (or at the current + * directory if none was given). + */ + check(git_repository_open_ext(&repo, repodir, 0, NULL), + "Could not open repository", repodir); + + if (git_repository_is_bare(repo)) + fail("Cannot report status on bare repository"); + + /* + * Run status on the repository + * + * Because we want to simluate a full "git status" run and want to + * support some command line options, we use `git_status_foreach_ext()` + * instead of just the plain status call. This allows (a) iterating + * over the index and then the workdir and (b) extra flags that control + * which files are included. If you just want simple status (e.g. to + * enumerate files that are modified) then you probably don't need the + * extended API. + */ + check(git_status_list_new(&status, repo, &opt), + "Could not get status", NULL); + + if (showbranch) + show_branch(repo, format); + + if (format == FORMAT_LONG) + print_long(repo, status); + else + print_short(repo, status); + + git_status_list_free(status); + git_repository_free(repo); + + return 0; +} + From 22b6b82f2c0d95ce7a433394a6c0574a5714cf4c Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 20 Jun 2013 12:16:06 -0700 Subject: [PATCH 367/384] Add status flags to force output sort order Files in status will, be default, be sorted according to the case insensitivity of the filesystem that we're running on. However, in some cases, this is not desirable. Even on case insensitive file systems, 'git status' at the command line will generally use a case sensitive sort (like 'ls'). Some GUIs prefer to display a list of file case insensitively even on case-sensitive platforms. This adds two new flags: GIT_STATUS_OPT_SORT_CASE_SENSITIVELY and GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY that will override the default sort order of the status output and give the user control. This includes tests for exercising these new options and makes the examples/status.c program emulate core Git and always use a case sensitive sort. --- examples/status.c | 3 +- include/git2/status.h | 8 +++ src/attr_file.c | 2 +- src/diff.c | 12 ++--- src/index.c | 10 ++-- src/status.c | 12 ++++- src/submodule.c | 2 +- src/vector.h | 9 ++++ tests-clar/status/status_helpers.c | 3 ++ tests-clar/status/status_helpers.h | 1 + tests-clar/status/worktree.c | 80 ++++++++++++++++++++++++++++++ 11 files changed, 126 insertions(+), 16 deletions(-) diff --git a/examples/status.c b/examples/status.c index 2378c78b6..3c82640b2 100644 --- a/examples/status.c +++ b/examples/status.c @@ -203,7 +203,8 @@ int main(int argc, char *argv[]) opt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; + GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | + GIT_STATUS_OPT_SORT_CASE_SENSITIVELY; for (i = 1; i < argc; ++i) { if (argv[i][0] != '-') { diff --git a/include/git2/status.h b/include/git2/status.h index 282b606cb..63aea2f3b 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -111,6 +111,12 @@ typedef enum { * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates tha rename * detection should be run between the index and the working directory * and enabled GIT_STATUS_WT_RENAMED as a possible status flag. + * - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY overrides the native case + * sensitivity for the file system and forces the output to be in + * case-sensitive order + * - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY overrides the native case + * sensitivity for the file system and forces the output to be in + * case-insensitive order * * Calling `git_status_foreach()` is like calling the extended version * with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED, @@ -127,6 +133,8 @@ typedef enum { GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6), GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7), GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8), + GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9), + GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10), } git_status_opt_t; #define GIT_STATUS_OPT_DEFAULTS \ diff --git a/src/attr_file.c b/src/attr_file.c index d059cfec7..d880398e8 100644 --- a/src/attr_file.c +++ b/src/attr_file.c @@ -498,7 +498,7 @@ int git_attr_assignment__parse( assert(assigns && !assigns->length); - assigns->_cmp = sort_by_hash_and_name; + git_vector_set_cmp(assigns, sort_by_hash_and_name); while (*scan && *scan != '\n') { const char *name_start, *value_start; diff --git a/src/diff.c b/src/diff.c index 3846a5e1b..26e117402 100644 --- a/src/diff.c +++ b/src/diff.c @@ -365,7 +365,7 @@ static git_diff_list *diff_list_alloc( diff->pfxcomp = git__prefixcmp_icase; diff->entrycomp = git_index_entry__cmp_icase; - diff->deltas._cmp = git_diff_delta__casecmp; + git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp); } return diff; @@ -1165,7 +1165,7 @@ int git_diff_tree_to_index( d->pfxcomp = git__prefixcmp_icase; d->entrycomp = git_index_entry__cmp_icase; - d->deltas._cmp = git_diff_delta__casecmp; + git_vector_set_cmp(&d->deltas, git_diff_delta__casecmp); git_vector_sort(&d->deltas); } } @@ -1266,10 +1266,10 @@ int git_diff__paired_foreach( /* force case-sensitive delta sort */ if (icase_mismatch) { if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) { - head2idx->deltas._cmp = git_diff_delta__cmp; + git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp); git_vector_sort(&head2idx->deltas); } else { - idx2wd->deltas._cmp = git_diff_delta__cmp; + git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__cmp); git_vector_sort(&idx2wd->deltas); } } @@ -1301,10 +1301,10 @@ int git_diff__paired_foreach( /* restore case-insensitive delta sort */ if (icase_mismatch) { if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) { - head2idx->deltas._cmp = git_diff_delta__casecmp; + git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp); git_vector_sort(&head2idx->deltas); } else { - idx2wd->deltas._cmp = git_diff_delta__casecmp; + git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__casecmp); git_vector_sort(&idx2wd->deltas); } } diff --git a/src/index.c b/src/index.c index d5568528b..1d46779bf 100644 --- a/src/index.c +++ b/src/index.c @@ -290,16 +290,16 @@ void git_index__set_ignore_case(git_index *index, bool ignore_case) { index->ignore_case = ignore_case; - index->entries._cmp = ignore_case ? index_icmp : index_cmp; index->entries_cmp_path = ignore_case ? index_icmp_path : index_cmp_path; index->entries_search = ignore_case ? index_isrch : index_srch; index->entries_search_path = ignore_case ? index_isrch_path : index_srch_path; - index->entries.sorted = 0; + + git_vector_set_cmp(&index->entries, ignore_case ? index_icmp : index_cmp); git_vector_sort(&index->entries); - index->reuc._cmp = ignore_case ? reuc_icmp : reuc_cmp; index->reuc_search = ignore_case ? reuc_isrch : reuc_srch; - index->reuc.sorted = 0; + + git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp); git_vector_sort(&index->reuc); } @@ -2024,7 +2024,7 @@ int git_index_read_tree(git_index *index, const git_tree *tree) git_vector_sort(&index->entries); - entries._cmp = index->entries._cmp; + git_vector_set_cmp(&entries, index->entries._cmp); git_vector_swap(&entries, &index->entries); git_index_clear(index); diff --git a/src/status.c b/src/status.c index 375100a89..e520c1017 100644 --- a/src/status.c +++ b/src/status.c @@ -335,8 +335,16 @@ int git_status_list_new( status->head2idx, status->idx2wd, status_collect, status)) < 0) goto done; - if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 || - (flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0) + if (flags & GIT_STATUS_OPT_SORT_CASE_SENSITIVELY) + git_vector_set_cmp(&status->paired, status_entry_cmp); + if (flags & GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY) + git_vector_set_cmp(&status->paired, status_entry_icmp); + + if ((flags & + (GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | + GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR | + GIT_STATUS_OPT_SORT_CASE_SENSITIVELY | + GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY)) != 0) git_vector_sort(&status->paired); done: diff --git a/src/submodule.c b/src/submodule.c index af488b7f3..89eba2aa4 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -151,7 +151,7 @@ int git_submodule_foreach( int error; git_submodule *sm; git_vector seen = GIT_VECTOR_INIT; - seen._cmp = submodule_cmp; + git_vector_set_cmp(&seen, submodule_cmp); assert(repo && callback); diff --git a/src/vector.h b/src/vector.h index e2f729b83..1bda9c93d 100644 --- a/src/vector.h +++ b/src/vector.h @@ -78,4 +78,13 @@ void git_vector_remove_matching( int git_vector_resize_to(git_vector *v, size_t new_length); int git_vector_set(void **old, git_vector *v, size_t position, void *value); +/** Set the comparison function used for sorting the vector */ +GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp) +{ + if (cmp != v->_cmp) { + v->_cmp = cmp; + v->sorted = 0; + } +} + #endif diff --git a/tests-clar/status/status_helpers.c b/tests-clar/status/status_helpers.c index f073c2491..902b65c4f 100644 --- a/tests-clar/status/status_helpers.c +++ b/tests-clar/status/status_helpers.c @@ -6,6 +6,9 @@ int cb_status__normal( { status_entry_counts *counts = payload; + if (counts->debug) + cb_status__print(path, status_flags, NULL); + if (counts->entry_count >= counts->expected_entry_count) { counts->wrong_status_flags_count++; goto exit; diff --git a/tests-clar/status/status_helpers.h b/tests-clar/status/status_helpers.h index ae1469e79..f1f009e02 100644 --- a/tests-clar/status/status_helpers.h +++ b/tests-clar/status/status_helpers.h @@ -8,6 +8,7 @@ typedef struct { const unsigned int* expected_statuses; const char** expected_paths; int expected_entry_count; + bool debug; } status_entry_counts; /* cb_status__normal takes payload of "status_entry_counts *" */ diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index 7c27ee588..920671e13 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -743,3 +743,83 @@ void test_status_worktree__simple_delete_indexed(void) GIT_STATUS_WT_DELETED, git_status_byindex(status, 0)->status); git_status_list_free(status); } + +static const char *icase_paths[] = { "B", "c", "g", "H" }; +static unsigned int icase_statuses[] = { + GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED, +}; + +static const char *case_paths[] = { "B", "H", "c", "g" }; +static unsigned int case_statuses[] = { + GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_DELETED, GIT_STATUS_WT_MODIFIED, +}; + +void test_status_worktree__sorting_by_case(void) +{ + git_repository *repo = cl_git_sandbox_init("icase"); + git_index *index; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + bool native_ignore_case; + status_entry_counts counts; + + cl_git_pass(git_repository_index(&index, repo)); + native_ignore_case = + (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0; + git_index_free(index); + + memset(&counts, 0, sizeof(counts)); + counts.expected_entry_count = 0; + counts.expected_paths = NULL; + counts.expected_statuses = NULL; + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); + + cl_git_rewritefile("icase/B", "new stuff"); + cl_must_pass(p_unlink("icase/c")); + cl_git_rewritefile("icase/g", "new stuff"); + cl_must_pass(p_unlink("icase/H")); + + memset(&counts, 0, sizeof(counts)); + counts.expected_entry_count = 4; + if (native_ignore_case) { + counts.expected_paths = icase_paths; + counts.expected_statuses = icase_statuses; + } else { + counts.expected_paths = case_paths; + counts.expected_statuses = case_statuses; + } + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); + + opts.flags = GIT_STATUS_OPT_SORT_CASE_SENSITIVELY; + + memset(&counts, 0, sizeof(counts)); + counts.expected_entry_count = 4; + counts.expected_paths = case_paths; + counts.expected_statuses = case_statuses; + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); + + opts.flags = GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY; + + memset(&counts, 0, sizeof(counts)); + counts.expected_entry_count = 4; + counts.expected_paths = icase_paths; + counts.expected_statuses = icase_statuses; + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); +} From f18f772a8ec0cdff9315216886383dadce1379b5 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 20 Jun 2013 14:27:14 -0700 Subject: [PATCH 368/384] Add example implementation of long format status --- examples/status.c | 147 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 2 deletions(-) diff --git a/examples/status.c b/examples/status.c index 3c82640b2..10b1ca8e5 100644 --- a/examples/status.c +++ b/examples/status.c @@ -76,7 +76,8 @@ static void show_branch(git_repository *repo, int format) check(error, "failed to get current branch", NULL); if (format == FORMAT_LONG) - printf("# %s\n", branch ? branch : "Not currently on any branch."); + printf("# On branch %s\n", + branch ? branch : "Not currently on any branch."); else printf("## %s\n", branch ? branch : "HEAD (no branch)"); @@ -85,8 +86,150 @@ static void show_branch(git_repository *repo, int format) static void print_long(git_repository *repo, git_status_list *status) { + size_t i, maxi = git_status_list_entrycount(status); + const git_status_entry *s; + int header = 0, changes_in_index = 0; + int changed_in_workdir = 0, rm_in_workdir = 0; + const char *old_path, *new_path; + (void)repo; - (void)status; + + /* print index changes */ + + for (i = 0; i < maxi; ++i) { + char *istatus = NULL; + + s = git_status_byindex(status, i); + + if (s->status == GIT_STATUS_CURRENT) + continue; + + if (s->status & GIT_STATUS_WT_DELETED) + rm_in_workdir = 1; + + if (s->status & GIT_STATUS_INDEX_NEW) + istatus = "new file: "; + if (s->status & GIT_STATUS_INDEX_MODIFIED) + istatus = "modified: "; + if (s->status & GIT_STATUS_INDEX_DELETED) + istatus = "deleted: "; + if (s->status & GIT_STATUS_INDEX_RENAMED) + istatus = "renamed: "; + if (s->status & GIT_STATUS_INDEX_TYPECHANGE) + istatus = "typechange:"; + + if (istatus == NULL) + continue; + + if (!header) { + printf("# Changes to be committed:\n"); + printf("# (use \"git reset HEAD ...\" to unstage)\n"); + printf("#\n"); + header = 1; + } + + old_path = s->head_to_index->old_file.path; + new_path = s->head_to_index->new_file.path; + + if (old_path && new_path && strcmp(old_path, new_path)) + printf("#\t%s %s -> %s\n", istatus, old_path, new_path); + else + printf("#\t%s %s\n", istatus, old_path ? old_path : new_path); + } + + if (header) { + changes_in_index = 1; + printf("#\n"); + } + header = 0; + + /* print workdir changes to tracked files */ + + for (i = 0; i < maxi; ++i) { + char *wstatus = NULL; + + s = git_status_byindex(status, i); + + if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL) + continue; + + if (s->status & GIT_STATUS_WT_MODIFIED) + wstatus = "modified: "; + if (s->status & GIT_STATUS_WT_DELETED) + wstatus = "deleted: "; + if (s->status & GIT_STATUS_WT_RENAMED) + wstatus = "renamed: "; + if (s->status & GIT_STATUS_WT_TYPECHANGE) + wstatus = "typechange:"; + + if (wstatus == NULL) + continue; + + if (!header) { + printf("# Changes not staged for commit:\n"); + printf("# (use \"git add%s ...\" to update what will be committed)\n", rm_in_workdir ? "/rm" : ""); + printf("# (use \"git checkout -- ...\" to discard changes in working directory)\n"); + printf("#\n"); + header = 1; + } + + old_path = s->index_to_workdir->old_file.path; + new_path = s->index_to_workdir->new_file.path; + + if (old_path && new_path && strcmp(old_path, new_path)) + printf("#\t%s %s -> %s\n", wstatus, old_path, new_path); + else + printf("#\t%s %s\n", wstatus, old_path ? old_path : new_path); + } + + if (header) { + changed_in_workdir = 1; + printf("#\n"); + } + header = 0; + + /* print untracked files */ + + header = 0; + + for (i = 0; i < maxi; ++i) { + s = git_status_byindex(status, i); + + if (s->status == GIT_STATUS_WT_NEW) { + + if (!header) { + printf("# Untracked files:\n"); + printf("# (use \"git add ...\" to include in what will be committed)\n"); + printf("#\n"); + header = 1; + } + + printf("#\t%s\n", s->index_to_workdir->old_file.path); + } + } + + header = 0; + + /* print ignored files */ + + for (i = 0; i < maxi; ++i) { + s = git_status_byindex(status, i); + + if (s->status == GIT_STATUS_IGNORED) { + + if (!header) { + printf("# Ignored files:\n"); + printf("# (use \"git add -f ...\" to include in what will be committed)\n"); + printf("#\n"); + header = 1; + } + + printf("#\t%s\n", s->index_to_workdir->old_file.path); + } + } + + if (!changes_in_index && changed_in_workdir) + printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"); } static void print_short(git_repository *repo, git_status_list *status) From 9280855787fcac1e333b6e7e66d7ba0cd2120a0c Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 20 Jun 2013 15:10:42 -0700 Subject: [PATCH 369/384] Fix comment and copyright in example --- examples/status.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/examples/status.c b/examples/status.c index 10b1ca8e5..689098415 100644 --- a/examples/status.c +++ b/examples/status.c @@ -1,10 +1,9 @@ /* - * Copyright (C) 2011-2012 the libgit2 contributors + * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ - #include #include #include @@ -19,15 +18,21 @@ enum { #define MAX_PATHSPEC 8 /* - * This example demonstrates the use of `git_status_foreach()` to roughly - * simulate the output of running `git status`. It should serve as a simple - * example of how to get basic status information. + * This example demonstrates the use of the libgit2 status APIs, + * particularly the `git_status_list` object, to roughly simulate the + * output of running `git status`. It serves as a simple example of + * using those APIs to get basic status information. * * This does not have: * - Robust error handling - * - Any real command line parsing * - Colorized or paginated output formatting * + * This does have: + * - Examples of translating command line arguments to the status + * options settings to mimic `git status` results. + * - A sample status formatter that matches the default "long" format + * from `git status` + * - A sample status formatter that matches the "short" format */ static void check(int error, const char *message, const char *extra) From 94ef2a353cdf4c29323c9935026d8435e8f098bd Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 20 Jun 2013 15:15:10 -0700 Subject: [PATCH 370/384] Add test for fixed diff bug Add test for bug fixed in 852ded96982ae70acb63c3940fae08ea29e40fee Sorry, I wrote that bug fix and forgot to check in a test at the same time. Here is one that fails on the old version of the code and now works. --- tests-clar/diff/workdir.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c index 18182ea96..6a2504d09 100644 --- a/tests-clar/diff/workdir.c +++ b/tests-clar/diff/workdir.c @@ -1109,6 +1109,26 @@ void test_diff_workdir__untracked_directory_scenarios(void) git_diff_list_free(diff); + /* empty directory in empty directory */ + + cl_git_pass(p_mkdir("status/subdir/directory/empty", 0777)); + + memset(&exp, 0, sizeof(exp)); + exp.names = files1; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + + git_diff_list_free(diff); + /* directory with only ignored files */ cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777)); From dacce80b128501e9821f254c2edb1e93906eac0b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 20 Jun 2013 19:05:38 -0500 Subject: [PATCH 371/384] test asserting checkout should not recreate deleted files --- tests-clar/checkout/tree.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index 462a46c83..7a1c797a2 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -442,6 +442,36 @@ void test_checkout_tree__checking_out_a_conflicting_content_change_returns_EMERG assert_conflict("branch_file.txt", "hello\n", "5b5b025", "c47800c"); } +void test_checkout_tree__donot_update_deleted_file_by_default(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_oid old_id, new_id; + git_commit *old_commit = NULL, *new_commit = NULL; + git_index *index = NULL; + + opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_oid_fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + cl_git_pass(git_commit_lookup(&old_commit, g_repo, &old_id)); + cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD)); + + cl_git_pass(p_unlink("testrepo/branch_file.txt")); + cl_git_pass(git_index_remove_bypath(index ,"branch_file.txt")); + cl_git_pass(git_index_write(index)); + + cl_assert(!git_path_exists("testrepo/branch_file.txt")); + + cl_git_pass(git_oid_fromstr(&new_id, "099fabac3a9ea935598528c27f866e34089c2eff")); + cl_git_pass(git_commit_lookup(&new_commit, g_repo, &new_id)); + cl_git_fail(git_checkout_tree(g_repo, (git_object *)new_commit, &opts)); + + git_commit_free(old_commit); + git_commit_free(new_commit); + git_index_free(index); +} + void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void) { git_index *index = NULL; From 36fd9e30651cf0d6b0ef58452ba2974a3544d4d1 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 21 Jun 2013 11:20:54 -0700 Subject: [PATCH 372/384] Fix checkout of modified file when missing from wd This fixes the checkout case when a file is modified between the baseline and the target and yet missing in the working directory. The logic for that case appears to have been wrong. This also adds a useful checkout notify callback to the checkout test helpers that will count notifications and also has a debug mode to visualize what checkout thinks that it's doing. --- include/git2/checkout.h | 2 + src/checkout.c | 4 +- tests-clar/checkout/checkout_helpers.c | 95 ++++++++++++++++++++++++++ tests-clar/checkout/checkout_helpers.h | 17 +++++ tests-clar/checkout/tree.c | 11 +++ 5 files changed, 128 insertions(+), 1 deletion(-) diff --git a/include/git2/checkout.h b/include/git2/checkout.h index 6798bf31c..f49e87566 100644 --- a/include/git2/checkout.h +++ b/include/git2/checkout.h @@ -183,6 +183,8 @@ typedef enum { GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2), GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3), GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4), + + GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFFu } git_checkout_notify_t; /** Checkout notification callback function */ diff --git a/src/checkout.c b/src/checkout.c index ede0be8e8..065bb50fd 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -220,9 +220,11 @@ static int checkout_action_no_wd( action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE); break; case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */ - case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */ action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; + case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */ + action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, CONFLICT); + break; case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/ if (delta->new_file.mode == GIT_FILEMODE_TREE) action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); diff --git a/tests-clar/checkout/checkout_helpers.c b/tests-clar/checkout/checkout_helpers.c index ab93a89bd..8da024dda 100644 --- a/tests-clar/checkout/checkout_helpers.c +++ b/tests-clar/checkout/checkout_helpers.c @@ -91,3 +91,98 @@ void check_file_contents_nocr_at_line( { check_file_contents_internal(path, expected, true, file, line, msg); } + +int checkout_count_callback( + git_checkout_notify_t why, + const char *path, + const git_diff_file *baseline, + const git_diff_file *target, + const git_diff_file *workdir, + void *payload) +{ + checkout_counts *ct = payload; + + GIT_UNUSED(baseline); GIT_UNUSED(target); GIT_UNUSED(workdir); + + if (why & GIT_CHECKOUT_NOTIFY_CONFLICT) { + ct->n_conflicts++; + + if (ct->debug) { + if (workdir) { + if (baseline) { + if (target) + fprintf(stderr, "M %s (conflicts with M %s)\n", + workdir->path, target->path); + else + fprintf(stderr, "M %s (conflicts with D %s)\n", + workdir->path, baseline->path); + } else { + if (target) + fprintf(stderr, "Existing %s (conflicts with A %s)\n", + workdir->path, target->path); + else + fprintf(stderr, "How can an untracked file be a conflict (%s)\n", workdir->path); + } + } else { + if (baseline) { + if (target) + fprintf(stderr, "D %s (conflicts with M %s)\n", + target->path, baseline->path); + else + fprintf(stderr, "D %s (conflicts with D %s)\n", + baseline->path, baseline->path); + } else { + if (target) + fprintf(stderr, "How can an added file with no workdir be a conflict (%s)\n", target->path); + else + fprintf(stderr, "How can a nonexistent file be a conflict (%s)\n", path); + } + } + } + } + + if (why & GIT_CHECKOUT_NOTIFY_DIRTY) { + ct->n_dirty++; + + if (ct->debug) { + if (workdir) + fprintf(stderr, "M %s\n", workdir->path); + else + fprintf(stderr, "D %s\n", baseline->path); + } + } + + if (why & GIT_CHECKOUT_NOTIFY_UPDATED) { + ct->n_updates++; + + if (ct->debug) { + if (baseline) { + if (target) + fprintf(stderr, "update: M %s\n", path); + else + fprintf(stderr, "update: D %s\n", path); + } else { + if (target) + fprintf(stderr, "update: A %s\n", path); + else + fprintf(stderr, "update: this makes no sense %s\n", path); + } + } + } + + if (why & GIT_CHECKOUT_NOTIFY_UNTRACKED) { + ct->n_untracked++; + + if (ct->debug) + fprintf(stderr, "? %s\n", path); + } + + if (why & GIT_CHECKOUT_NOTIFY_IGNORED) { + ct->n_ignored++; + + if (ct->debug) + fprintf(stderr, "I %s\n", path); + } + + return 0; +} diff --git a/tests-clar/checkout/checkout_helpers.h b/tests-clar/checkout/checkout_helpers.h index 34053809d..0e8da31d1 100644 --- a/tests-clar/checkout/checkout_helpers.h +++ b/tests-clar/checkout/checkout_helpers.h @@ -19,3 +19,20 @@ extern void check_file_contents_nocr_at_line( #define check_file_contents_nocr(PATH,EXP) \ check_file_contents_nocr_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH) + +typedef struct { + int n_conflicts; + int n_dirty; + int n_updates; + int n_untracked; + int n_ignored; + int debug; +} checkout_counts; + +extern int checkout_count_callback( + git_checkout_notify_t why, + const char *path, + const git_diff_file *baseline, + const git_diff_file *target, + const git_diff_file *workdir, + void *payload); diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index 7a1c797a2..5835ab05b 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -448,9 +448,15 @@ void test_checkout_tree__donot_update_deleted_file_by_default(void) git_oid old_id, new_id; git_commit *old_commit = NULL, *new_commit = NULL; git_index *index = NULL; + checkout_counts ct; opts.checkout_strategy = GIT_CHECKOUT_SAFE; + memset(&ct, 0, sizeof(ct)); + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &ct; + cl_git_pass(git_repository_index(&index, g_repo)); cl_git_pass(git_oid_fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); @@ -465,8 +471,13 @@ void test_checkout_tree__donot_update_deleted_file_by_default(void) cl_git_pass(git_oid_fromstr(&new_id, "099fabac3a9ea935598528c27f866e34089c2eff")); cl_git_pass(git_commit_lookup(&new_commit, g_repo, &new_id)); + + cl_git_fail(git_checkout_tree(g_repo, (git_object *)new_commit, &opts)); + cl_assert_equal_i(1, ct.n_conflicts); + cl_assert_equal_i(1, ct.n_updates); + git_commit_free(old_commit); git_commit_free(new_commit); git_index_free(index); From 9094ae5a3c12ee99743498cb8e895d18b932e4dd Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 21 Jun 2013 11:51:16 -0700 Subject: [PATCH 373/384] Add target directory to checkout This adds the ability for checkout to write to a target directory instead of having to use the working directory of the repository. This makes it easier to do exports of repository data and the like. This is similar to, but not quite the same as, the --prefix option to `git checkout-index` (this will always be treated as a directory name, not just as a simple text prefix). As part of this, the workdir iterator was extended to take the path to the working directory as a parameter and fallback on the git_repository_workdir result only if it's not specified. Fixes #1332 --- include/git2/checkout.h | 2 ++ src/checkout.c | 25 ++++++++++++++++++------- src/iterator.c | 12 ++++++++---- src/iterator.h | 15 +++++++++++++-- tests-clar/checkout/index.c | 28 ++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 13 deletions(-) diff --git a/include/git2/checkout.h b/include/git2/checkout.h index f49e87566..a086408c7 100644 --- a/include/git2/checkout.h +++ b/include/git2/checkout.h @@ -236,6 +236,8 @@ typedef struct git_checkout_opts { git_strarray paths; git_tree *baseline; /** expected content of workdir, defaults to HEAD */ + + const char *target_directory; /** alternative checkout path to workdir */ } git_checkout_opts; #define GIT_CHECKOUT_OPTS_VERSION 1 diff --git a/src/checkout.c b/src/checkout.c index 065bb50fd..e3ae38710 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -858,7 +858,7 @@ static int checkout_submodule( return 0; if ((error = git_futils_mkdir( - file->path, git_repository_workdir(data->repo), + file->path, data->opts.target_directory, data->opts.dir_mode, GIT_MKDIR_PATH)) < 0) return error; @@ -1030,7 +1030,7 @@ static int checkout_deferred_remove(git_repository *repo, const char *path) { #if 0 int error = git_futils_rmdir_r( - path, git_repository_workdir(repo), GIT_RMDIR_EMPTY_PARENTS); + path, data->opts.target_directory, GIT_RMDIR_EMPTY_PARENTS); if (error == GIT_ENOTFOUND) { error = 0; @@ -1163,7 +1163,8 @@ static int checkout_data_init( return -1; } - if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0) + if ((!proposed || !proposed->target_directory) && + (error = git_repository__ensure_not_bare(repo, "checkout")) < 0) return error; data->repo = repo; @@ -1176,6 +1177,13 @@ static int checkout_data_init( else memmove(&data->opts, proposed, sizeof(git_checkout_opts)); + if (!data->opts.target_directory) + data->opts.target_directory = git_repository_workdir(repo); + else if (!git_path_isdir(data->opts.target_directory) && + (error = git_futils_mkdir(data->opts.target_directory, NULL, + GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0) + goto cleanup; + /* refresh config and index content unless NO_REFRESH is given */ if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) { git_config *cfg; @@ -1238,7 +1246,8 @@ static int checkout_data_init( if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 || (error = git_pool_init(&data->pool, 1, 0)) < 0 || - (error = git_buf_puts(&data->path, git_repository_workdir(repo))) < 0) + (error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 || + (error = git_path_to_dir(&data->path)) < 0) goto cleanup; data->workdir_len = git_buf_len(&data->path); @@ -1286,11 +1295,13 @@ int git_checkout_iterator( GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 || - (error = git_iterator_for_workdir( - &workdir, data.repo, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND, + (error = git_iterator_for_workdir_ext( + &workdir, data.repo, data.opts.target_directory, + iterflags | GIT_ITERATOR_DONT_AUTOEXPAND, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_tree( - &baseline, data.opts.baseline, iterflags, data.pfx, data.pfx)) < 0) + &baseline, data.opts.baseline, + iterflags, data.pfx, data.pfx)) < 0) goto cleanup; /* Should not have case insensitivity mismatch */ diff --git a/src/iterator.c b/src/iterator.c index 76b0e41d0..5917f63fd 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1321,9 +1321,10 @@ static void workdir_iterator__free(git_iterator *self) git_ignore__free(&wi->ignores); } -int git_iterator_for_workdir( +int git_iterator_for_workdir_ext( git_iterator **out, git_repository *repo, + const char *repo_workdir, git_iterator_flag_t flags, const char *start, const char *end) @@ -1331,8 +1332,11 @@ int git_iterator_for_workdir( int error; workdir_iterator *wi; - if (git_repository__ensure_not_bare(repo, "scan working directory") < 0) - return GIT_EBAREREPO; + if (!repo_workdir) { + if (git_repository__ensure_not_bare(repo, "scan working directory") < 0) + return GIT_EBAREREPO; + repo_workdir = git_repository_workdir(repo); + } /* initialize as an fs iterator then do overrides */ wi = git__calloc(1, sizeof(workdir_iterator)); @@ -1352,7 +1356,7 @@ int git_iterator_for_workdir( return error; } - return fs_iterator__initialize(out, &wi->fi, git_repository_workdir(repo)); + return fs_iterator__initialize(out, &wi->fi, repo_workdir); } diff --git a/src/iterator.h b/src/iterator.h index 493ff4b2a..ea88fa6a2 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -79,15 +79,26 @@ extern int git_iterator_for_index( const char *start, const char *end); +extern int git_iterator_for_workdir_ext( + git_iterator **out, + git_repository *repo, + const char *repo_workdir, + git_iterator_flag_t flags, + const char *start, + const char *end); + /* workdir iterators will match the ignore_case value from the index of the * repository, unless you override with a non-zero flag value */ -extern int git_iterator_for_workdir( +GIT_INLINE(int) git_iterator_for_workdir( git_iterator **out, git_repository *repo, git_iterator_flag_t flags, const char *start, - const char *end); + const char *end) +{ + return git_iterator_for_workdir_ext(out, repo, NULL, flags, start, end); +} /* for filesystem iterators, you have to explicitly pass in the ignore_case * behavior that you desire diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c index a3a0f8fda..16584ce22 100644 --- a/tests-clar/checkout/index.c +++ b/tests-clar/checkout/index.c @@ -506,3 +506,31 @@ void test_checkout_index__issue_1397(void) check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf"); } + +void test_checkout_index__target_directory(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + checkout_counts cts; + memset(&cts, 0, sizeof(cts)); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + opts.target_directory = "alternative"; + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &cts; + + /* create some files that *would* conflict if we were using the wd */ + cl_git_mkfile("testrepo/README", "I'm in the way!\n"); + cl_git_mkfile("testrepo/new.txt", "my new file\n"); + + cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + + cl_assert_equal_i(0, cts.n_untracked); + cl_assert_equal_i(0, cts.n_ignored); + cl_assert_equal_i(4, cts.n_updates); + + check_file_contents("./alternative/README", "hey there\n"); + check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents("./alternative/new.txt", "my new file\n"); +} From 6a15e8d23ad3e8c419c88b98732ca32addd2887c Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 21 Jun 2013 12:26:36 -0700 Subject: [PATCH 374/384] Loosen ensure_not_bare rules in checkout With the new target directory option to checkout, the non-bareness of the repository should be checked much later in the parameter validation process - actually that check was already in place, but I was doing it redundantly in the checkout APIs. This removes the now unnecessary early check for bare repos. It also adds some other parameter validation and makes it so that implied parameters can actually be passed as NULL (i.e. if you pass a git_index, you don't have to pass the git_repository - we can get it from index). --- src/checkout.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index e3ae38710..8f9ec64e4 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1369,8 +1369,19 @@ int git_checkout_index( int error; git_iterator *index_i; - if ((error = git_repository__ensure_not_bare(repo, "checkout index")) < 0) - return error; + if (!index && !repo) { + giterr_set(GITERR_CHECKOUT, + "Must provide either repository or index to checkout"); + return -1; + } + if (index && repo && git_index_owner(index) != repo) { + giterr_set(GITERR_CHECKOUT, + "Index to checkout does not match repository"); + return -1; + } + + if (!repo) + repo = git_index_owner(index); if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0) return error; @@ -1394,8 +1405,19 @@ int git_checkout_tree( git_tree *tree = NULL; git_iterator *tree_i = NULL; - if ((error = git_repository__ensure_not_bare(repo, "checkout tree")) < 0) - return error; + if (!treeish && !repo) { + giterr_set(GITERR_CHECKOUT, + "Must provide either repository or tree to checkout"); + return -1; + } + if (treeish && repo && git_object_owner(treeish) != repo) { + giterr_set(GITERR_CHECKOUT, + "Object to checkout does not match repository"); + return -1; + } + + if (!repo) + repo = git_object_owner(treeish); if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) { giterr_set( @@ -1420,8 +1442,7 @@ int git_checkout_head( git_tree *head = NULL; git_iterator *head_i = NULL; - if ((error = git_repository__ensure_not_bare(repo, "checkout head")) < 0) - return error; + assert(repo); if (!(error = checkout_lookup_head_tree(&head, repo)) && !(error = git_iterator_for_tree(&head_i, head, 0, NULL, NULL))) From d4f98ba4f124a836ed964a71137a6dae28358704 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 21 Jun 2013 12:29:03 -0700 Subject: [PATCH 375/384] Addition checkout target directory tests This adds additonal tests of the checkout target directory option including using it to dump data from bare repos. --- tests-clar/checkout/index.c | 71 +++++++++++++++++++++++++++++++++++++ tests-clar/checkout/tree.c | 44 +++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c index 16584ce22..c7500db1d 100644 --- a/tests-clar/checkout/index.c +++ b/tests-clar/checkout/index.c @@ -515,6 +515,7 @@ void test_checkout_index__target_directory(void) opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; opts.target_directory = "alternative"; + cl_assert(!git_path_isdir("alternative")); opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; opts.notify_cb = checkout_count_callback; @@ -533,4 +534,74 @@ void test_checkout_index__target_directory(void) check_file_contents("./alternative/README", "hey there\n"); check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n"); check_file_contents("./alternative/new.txt", "my new file\n"); + + cl_git_pass(git_futils_rmdir_r( + "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_checkout_index__target_directory_from_bare(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_index *index; + git_object *head = NULL; + checkout_counts cts; + memset(&cts, 0, sizeof(cts)); + + test_checkout_index__cleanup(); + + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_assert(git_repository_is_bare(g_repo)); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_revparse_single(&head, g_repo, "HEAD^{tree}")); + cl_git_pass(git_index_read_tree(index, (const git_tree *)head)); + cl_git_pass(git_index_write(index)); + git_index_free(index); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &cts; + + /* fail to checkout a bare repo */ + cl_git_fail(git_checkout_index(g_repo, NULL, &opts)); + + opts.target_directory = "alternative"; + cl_assert(!git_path_isdir("alternative")); + + cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + + cl_assert_equal_i(0, cts.n_untracked); + cl_assert_equal_i(0, cts.n_ignored); + cl_assert_equal_i(3, cts.n_updates); + + check_file_contents("./alternative/README", "hey there\n"); + check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents("./alternative/new.txt", "my new file\n"); + + cl_git_pass(git_futils_rmdir_r( + "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_checkout_index__can_get_repo_from_index(void) +{ + git_index *index; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + + cl_assert_equal_i(false, git_path_isfile("./testrepo/README")); + cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt")); + cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt")); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_checkout_index(NULL, index, &opts)); + + check_file_contents("./testrepo/README", "hey there\n"); + check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n"); + check_file_contents("./testrepo/new.txt", "my new file\n"); + + git_index_free(index); } diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index 5835ab05b..0e65f28c8 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -596,6 +596,8 @@ void test_checkout_tree__fails_when_dir_in_use(void) cl_git_pass(p_chdir("../..")); cl_assert(git_path_is_empty_dir("testrepo/a")); + + git_object_free(obj); #endif } @@ -628,5 +630,47 @@ void test_checkout_tree__can_continue_when_dir_in_use(void) cl_git_pass(p_chdir("../..")); cl_assert(git_path_is_empty_dir("testrepo/a")); + + git_object_free(obj); #endif } + +void test_checkout_tree__target_directory_from_bare(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_oid oid; + checkout_counts cts; + memset(&cts, 0, sizeof(cts)); + + test_checkout_tree__cleanup(); /* cleanup default checkout */ + + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_assert(git_repository_is_bare(g_repo)); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &cts; + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD")); + cl_git_pass(git_object_lookup(&g_object, g_repo, &oid, GIT_OBJ_ANY)); + + cl_git_fail(git_checkout_tree(g_repo, g_object, &opts)); + + opts.target_directory = "alternative"; + cl_assert(!git_path_isdir("alternative")); + + cl_git_pass(git_checkout_tree(g_repo, g_object, &opts)); + + cl_assert_equal_i(0, cts.n_untracked); + cl_assert_equal_i(0, cts.n_ignored); + cl_assert_equal_i(3, cts.n_updates); + + check_file_contents("./alternative/README", "hey there\n"); + check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents("./alternative/new.txt", "my new file\n"); + + cl_git_pass(git_futils_rmdir_r( + "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); +} From a7ea40955e5bd881508e2a99e2b73b1a1fbf78e3 Mon Sep 17 00:00:00 2001 From: Sven Strickroth Date: Sun, 23 Jun 2013 01:25:34 +0200 Subject: [PATCH 376/384] Do not redefine WC_ERR_INVALID_CHARS WC_ERR_INVALID_CHARS might be already defined by the Windows SDK. Signed-off-by: Sven Strickroth --- src/win32/error.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/win32/error.c b/src/win32/error.c index 4a9a0631f..a62a07e82 100644 --- a/src/win32/error.c +++ b/src/win32/error.c @@ -12,7 +12,9 @@ # include #endif +#ifndef WC_ERR_INVALID_CHARS #define WC_ERR_INVALID_CHARS 0x80 +#endif char *git_win32_get_error_message(DWORD error_code) { From 8294e8cfff284a05084f7f9b91931e136a0c119d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Sat, 22 Jun 2013 17:15:31 -0700 Subject: [PATCH 377/384] Constrain mkdir calls to avoid extra mkdirs This updates the calls that make the subdirectories for objects to use a base directory above which git_futils_mkdir won't walk any higher. This prevents attempts to mkdir all the way up to the root of the filesystem. Also, this moves the objects_dir into the loose backend structure and removes the separate allocation, plus does some preformatting of the objects_dir value to guarantee a trailing slash, etc. --- src/odb_loose.c | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/odb_loose.c b/src/odb_loose.c index e78172cf6..76ed8e232 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -33,7 +33,9 @@ typedef struct loose_backend { int object_zlib_level; /** loose object zlib compression level. */ int fsync_object_files; /** loose object file fsync flag. */ - char *objects_dir; + + size_t objects_dirlen; + char objects_dir[GIT_FLEX_ARRAY]; } loose_backend; /* State structure for exploring directories, @@ -56,24 +58,30 @@ typedef struct { * ***********************************************************/ -static int object_file_name(git_buf *name, const char *dir, const git_oid *id) +static int object_file_name( + git_buf *name, const loose_backend *be, const git_oid *id) { - git_buf_sets(name, dir); - - /* expand length for 40 hex sha1 chars + 2 * '/' + '\0' */ - if (git_buf_grow(name, git_buf_len(name) + GIT_OID_HEXSZ + 3) < 0) + /* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */ + if (git_buf_grow(name, be->objects_dirlen + GIT_OID_HEXSZ + 3) < 0) return -1; + git_buf_set(name, be->objects_dir, be->objects_dirlen); git_path_to_dir(name); /* loose object filename: aa/aaa... (41 bytes) */ - git_oid_pathfmt(name->ptr + git_buf_len(name), id); + git_oid_pathfmt(name->ptr + name->size, id); name->size += GIT_OID_HEXSZ + 1; name->ptr[name->size] = '\0'; return 0; } +static int object_mkdir(const git_buf *name, const loose_backend *be) +{ + return git_futils_mkdir( + name->ptr + be->objects_dirlen, be->objects_dir, GIT_OBJECT_DIR_MODE, + GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR); +} static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj) { @@ -457,7 +465,7 @@ static int locate_object( loose_backend *backend, const git_oid *oid) { - int error = object_file_name(object_location, backend->objects_dir, oid); + int error = object_file_name(object_location, backend, oid); if (!error && !git_path_exists(object_location->ptr)) return GIT_ENOTFOUND; @@ -769,8 +777,8 @@ static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream) int error = 0; if (git_filebuf_hash(oid, &stream->fbuf) < 0 || - object_file_name(&final_path, backend->objects_dir, oid) < 0 || - git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE) < 0) + object_file_name(&final_path, backend, oid) < 0 || + object_mkdir(&final_path, backend) < 0) error = -1; /* * Don't try to add an existing object to the repository. This @@ -880,8 +888,8 @@ static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const v git_filebuf_write(&fbuf, header, header_len); git_filebuf_write(&fbuf, data, len); - if (object_file_name(&final_path, backend->objects_dir, oid) < 0 || - git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE) < 0 || + if (object_file_name(&final_path, backend, oid) < 0 || + object_mkdir(&final_path, backend) < 0 || git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE) < 0) error = -1; @@ -898,7 +906,6 @@ static void loose_backend__free(git_odb_backend *_backend) assert(_backend); backend = (loose_backend *)_backend; - git__free(backend->objects_dir); git__free(backend); } @@ -909,13 +916,20 @@ int git_odb_backend_loose( int do_fsync) { loose_backend *backend; + size_t objects_dirlen; - backend = git__calloc(1, sizeof(loose_backend)); + assert(backend_out && objects_dir); + + objects_dirlen = strlen(objects_dir); + + backend = git__calloc(1, sizeof(loose_backend) + objects_dirlen + 2); GITERR_CHECK_ALLOC(backend); backend->parent.version = GIT_ODB_BACKEND_VERSION; - backend->objects_dir = git__strdup(objects_dir); - GITERR_CHECK_ALLOC(backend->objects_dir); + backend->objects_dirlen = objects_dirlen; + memcpy(backend->objects_dir, objects_dir, objects_dirlen); + if (backend->objects_dir[backend->objects_dirlen - 1] != '/') + backend->objects_dir[backend->objects_dirlen++] = '/'; if (compression_level < 0) compression_level = Z_BEST_SPEED; From 3d3ea4dc564922a3662298a7cfc2fc8b24149901 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Sat, 22 Jun 2013 20:58:32 -0700 Subject: [PATCH 378/384] Add O_CLOEXEC to open calls --- src/fileops.c | 6 ++++-- src/posix.c | 4 ++-- src/posix.h | 3 +++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index ae240fcd2..1f58fa5cd 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -61,9 +61,11 @@ int git_futils_creat_locked(const char *path, const mode_t mode) wchar_t buf[GIT_WIN_PATH]; git__utf8_to_16(buf, GIT_WIN_PATH, path); - fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); + fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | + O_EXCL | O_BINARY | O_CLOEXEC, mode); #else - fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | + O_EXCL | O_BINARY | O_CLOEXEC, mode); #endif if (fd < 0) { diff --git a/src/posix.c b/src/posix.c index 5d526d33c..b75109b83 100644 --- a/src/posix.c +++ b/src/posix.c @@ -111,12 +111,12 @@ int p_open(const char *path, int flags, ...) va_end(arg_list); } - return open(path, flags | O_BINARY, mode); + return open(path, flags | O_BINARY | O_CLOEXEC, mode); } int p_creat(const char *path, mode_t mode) { - return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode); + return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC, mode); } int p_getcwd(char *buffer_out, size_t size) diff --git a/src/posix.h b/src/posix.h index 719c8a04c..40bcc1ab0 100644 --- a/src/posix.h +++ b/src/posix.h @@ -25,6 +25,9 @@ #if !defined(O_BINARY) #define O_BINARY 0 #endif +#if !defined(O_CLOEXEC) +#define O_CLOEXEC 0 +#endif typedef int git_file; From e1967164574816b8bf6740ea17d08eeb26c091d2 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Mon, 24 Jun 2013 15:33:41 +0200 Subject: [PATCH 379/384] Fixed most documentation header bugs Fixed a few header @param and @return typos with the help of -Wdocumentation in Xcode. The following warnings have not been fixed: common.h:213 - Not sure how the documentation format is for '...' notes.h:102 - Correct @param name but empty text notes.h:111 - Correct @param name but empty text pack.h:140 - @return missing text pack.h:148 - @return missing text --- include/git2/attr.h | 2 +- include/git2/commit.h | 2 +- include/git2/config.h | 6 +++--- include/git2/diff.h | 10 +++++----- include/git2/errors.h | 2 +- include/git2/index.h | 2 +- include/git2/indexer.h | 2 +- include/git2/merge.h | 2 +- include/git2/oid.h | 6 +++--- include/git2/pack.h | 2 +- include/git2/refs.h | 12 ++++++------ include/git2/refspec.h | 2 +- include/git2/remote.h | 4 ++-- include/git2/repository.h | 2 +- include/git2/stash.h | 2 +- include/git2/submodule.h | 4 ++-- include/git2/tree.h | 8 ++++---- 17 files changed, 35 insertions(+), 35 deletions(-) diff --git a/include/git2/attr.h b/include/git2/attr.h index 0d8a910f2..f256ff861 100644 --- a/include/git2/attr.h +++ b/include/git2/attr.h @@ -162,7 +162,7 @@ GIT_EXTERN(int) git_attr_get( * Then you could loop through the 3 values to get the settings for * the three attributes you asked about. * - * @param values An array of num_attr entries that will have string + * @param values_out An array of num_attr entries that will have string * pointers written into it for the values of the attributes. * You should not modify or free the values that are written * into this array (although of course, you should free the diff --git a/include/git2/commit.h b/include/git2/commit.h index 20b345f84..544d21d87 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -235,7 +235,7 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor( * * @param parent_count Number of parents for this commit * - * @param parents[] Array of `parent_count` pointers to `git_commit` + * @param parents Array of `parent_count` pointers to `git_commit` * objects that will be used as the parents for this commit. This * array may be NULL if `parent_count` is 0 (root commit). All the * given commits must be owned by the `repo`. diff --git a/include/git2/config.h b/include/git2/config.h index 59b4307be..827d43544 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -119,7 +119,7 @@ GIT_EXTERN(int) git_config_find_xdg(char *out, size_t length); * If /etc/gitconfig doesn't exist, it will look for * %PROGRAMFILES%\Git\etc\gitconfig. - * @param global_config_path Buffer to store the path in + * @param out Buffer to store the path in * @param length size of the buffer in bytes * @return 0 if a system configuration file has been * found. Its path will be stored in `buffer`. @@ -335,8 +335,8 @@ GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, c * @param name the variable's name * @param regexp regular expression to filter which variables we're * interested in. Use NULL to indicate all - * @param fn the function to be called on each value of the variable - * @param data opaque pointer to pass to the callback + * @param callback the function to be called on each value of the variable + * @param payload opaque pointer to pass to the callback */ GIT_EXTERN(int) git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload); diff --git a/include/git2/diff.h b/include/git2/diff.h index f352f2843..43029c49c 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -747,7 +747,7 @@ GIT_EXTERN(int) git_diff_print_raw( * letters for your own purposes. This function does just that. By the * way, unmodified will return a space (i.e. ' '). * - * @param delta_t The git_delta_t value to look up + * @param status The git_delta_t value to look up * @return The single character label for that code */ GIT_EXTERN(char) git_diff_status_char(git_delta_t status); @@ -918,7 +918,7 @@ GIT_EXTERN(int) git_diff_patch_num_lines_in_hunk( * @param new_lineno Line number in new file or -1 if line is deleted * @param patch The patch to look in * @param hunk_idx The index of the hunk - * @param line_of_index The index of the line in the hunk + * @param line_of_hunk The index of the line in the hunk * @return 0 on success, <0 on failure */ GIT_EXTERN(int) git_diff_patch_get_line_in_hunk( @@ -1017,7 +1017,7 @@ GIT_EXTERN(int) git_diff_blobs( * @param old_as_path Treat old blob as if it had this filename; can be NULL * @param new_blob Blob for new side of diff, or NULL for empty blob * @param new_as_path Treat new blob as if it had this filename; can be NULL - * @param options Options for diff, or NULL for default options + * @param opts Options for diff, or NULL for default options * @return 0 on success or error code < 0 */ GIT_EXTERN(int) git_diff_patch_from_blobs( @@ -1048,7 +1048,7 @@ GIT_EXTERN(int) git_diff_patch_from_blobs( * @param options Options for diff, or NULL for default options * @param file_cb Callback for "file"; made once if there is a diff; can be NULL * @param hunk_cb Callback for each hunk in diff; can be NULL - * @param line_cb Callback for each line in diff; can be NULL + * @param data_cb Callback for each line in diff; can be NULL * @param payload Payload passed to each callback function * @return 0 on success, GIT_EUSER on non-zero callback return, or error code */ @@ -1078,7 +1078,7 @@ GIT_EXTERN(int) git_diff_blob_to_buffer( * @param buffer Raw data for new side of diff, or NULL for empty * @param buffer_len Length of raw data for new side of diff * @param buffer_as_path Treat buffer as if it had this filename; can be NULL - * @param options Options for diff, or NULL for default options + * @param opts Options for diff, or NULL for default options * @return 0 on success or error code < 0 */ GIT_EXTERN(int) git_diff_patch_from_blob_and_buffer( diff --git a/include/git2/errors.h b/include/git2/errors.h index caf9e62b8..2032a436a 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -100,7 +100,7 @@ GIT_EXTERN(void) giterr_clear(void); * * @param error_class One of the `git_error_t` enum above describing the * general subsystem that is responsible for the error. - * @param message The formatted error message to keep + * @param string The formatted error message to keep */ GIT_EXTERN(void) giterr_set_str(int error_class, const char *string); diff --git a/include/git2/index.h b/include/git2/index.h index 399d7c9a8..51694aded 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -646,7 +646,7 @@ GIT_EXTERN(int) git_index_conflict_next( /** * Frees a `git_index_conflict_iterator`. * - * @param it pointer to the iterator + * @param iterator pointer to the iterator */ GIT_EXTERN(void) git_index_conflict_iterator_free( git_index_conflict_iterator *iterator); diff --git a/include/git2/indexer.h b/include/git2/indexer.h index 262dcd154..4db072c9b 100644 --- a/include/git2/indexer.h +++ b/include/git2/indexer.h @@ -21,7 +21,7 @@ typedef struct git_indexer_stream git_indexer_stream; * @param out where to store the indexer instance * @param path to the directory where the packfile should be stored * @param progress_cb function to call with progress information - * @param progress_payload payload for the progress callback + * @param progress_cb_payload payload for the progress callback */ GIT_EXTERN(int) git_indexer_stream_new( git_indexer_stream **out, diff --git a/include/git2/merge.h b/include/git2/merge.h index ce3ed8ed2..cef6f775b 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -141,7 +141,7 @@ GIT_EXTERN(int) git_merge_head_from_oid( /** * Frees a `git_merge_head` * - * @param the merge head to free + * @param head merge head to free */ GIT_EXTERN(void) git_merge_head_free( git_merge_head *head); diff --git a/include/git2/oid.h b/include/git2/oid.h index 018cead4a..662338d93 100644 --- a/include/git2/oid.h +++ b/include/git2/oid.h @@ -85,7 +85,7 @@ GIT_EXTERN(void) git_oid_fromraw(git_oid *out, const unsigned char *raw); * needed for an oid encoded in hex (40 bytes). Only the * oid digits are written; a '\\0' terminator must be added * by the caller if it is required. - * @param oid oid structure to format. + * @param id oid structure to format. */ GIT_EXTERN(void) git_oid_fmt(char *out, const git_oid *id); @@ -96,7 +96,7 @@ GIT_EXTERN(void) git_oid_fmt(char *out, const git_oid *id); * If the number of bytes is > GIT_OID_HEXSZ, extra bytes * will be zeroed; if not, a '\0' terminator is NOT added. * @param n number of characters to write into out string - * @param oid oid structure to format. + * @param id oid structure to format. */ GIT_EXTERN(void) git_oid_nfmt(char *out, size_t n, const git_oid *id); @@ -118,7 +118,7 @@ GIT_EXTERN(void) git_oid_pathfmt(char *out, const git_oid *id); /** * Format a git_oid into a newly allocated c-string. * - * @param oid the oid structure to format + * @param id the oid structure to format * @return the c-string; NULL if memory is exhausted. Caller must * deallocate the string with git__free(). */ diff --git a/include/git2/pack.h b/include/git2/pack.h index 242bddd25..cc1f48add 100644 --- a/include/git2/pack.h +++ b/include/git2/pack.h @@ -112,7 +112,7 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid * @param pb The packbuilder * @param path to the directory where the packfile and index should be stored * @param progress_cb function to call with progress information from the indexer (optional) - * @param progress_payload payload for the progress callback (optional) + * @param progress_cb_payload payload for the progress callback (optional) * * @return 0 or an error code */ diff --git a/include/git2/refs.h b/include/git2/refs.h index 1b6184be5..795f7ab27 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -62,7 +62,7 @@ GIT_EXTERN(int) git_reference_name_to_id( * * @param out pointer in which to store the reference * @param repo the repository in which to look - * @param shrothand the short name for the reference + * @param shorthand the short name for the reference * @return 0 or an error code */ GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, const char *shorthand); @@ -198,7 +198,7 @@ GIT_EXTERN(const char *) git_reference_name(const git_reference *ref); * If a direct reference is passed as an argument, a copy of that * reference is returned. This copy must be manually freed too. * - * @param resolved_ref Pointer to the peeled reference + * @param out Pointer to the peeled reference * @param ref The reference * @return 0 or an error code */ @@ -266,7 +266,7 @@ GIT_EXTERN(int) git_reference_set_target( * the reflog if it exists. * * @param ref The reference to rename - * @param name The new name for the reference + * @param new_name The new name for the reference * @param force Overwrite an existing reference * @return 0 on success, EINVALIDSPEC, EEXISTS or an error code * @@ -375,7 +375,7 @@ GIT_EXTERN(int) git_reference_iterator_glob_new( * * @param out pointer in which to store the reference * @param iter the iterator - * @param 0, GIT_ITEROVER if there are no more; or an error code + * @return 0, GIT_ITEROVER if there are no more; or an error code */ GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter); @@ -506,9 +506,9 @@ GIT_EXTERN(int) git_reference_normalize_name( * If you pass `GIT_OBJ_ANY` as the target type, then the object * will be peeled until a non-tag object is met. * - * @param peeled Pointer to the peeled git_object + * @param out Pointer to the peeled git_object * @param ref The reference to be processed - * @param target_type The type of the requested object (GIT_OBJ_COMMIT, + * @param type The type of the requested object (GIT_OBJ_COMMIT, * GIT_OBJ_TAG, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_ANY). * @return 0 on success, GIT_EAMBIGUOUS, GIT_ENOTFOUND or an error code */ diff --git a/include/git2/refspec.h b/include/git2/refspec.h index c0b410cbf..d96b83ce2 100644 --- a/include/git2/refspec.h +++ b/include/git2/refspec.h @@ -55,7 +55,7 @@ GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec); /** * Get the refspec's direction. * - * @param the refspec + * @param spec refspec * @return GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH */ GIT_EXTERN(git_direction) git_refspec_direction(const git_refspec *spec); diff --git a/include/git2/remote.h b/include/git2/remote.h index 3f43916b5..45d15d0a3 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -259,7 +259,7 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void * @param progress_cb function to call with progress information. Be aware that * this is called inline with network and indexing operations, so performance * may be affected. - * @param progress_payload payload for the progress callback + * @param payload payload for the progress callback * @return 0 or an error code */ GIT_EXTERN(int) git_remote_download( @@ -320,7 +320,7 @@ GIT_EXTERN(int) git_remote_update_tips(git_remote *remote); * Return whether a string is a valid remote URL * * @param url the url to check - * @param 1 if the url is valid, 0 otherwise + * @return 1 if the url is valid, 0 otherwise */ GIT_EXTERN(int) git_remote_valid_url(const char *url); diff --git a/include/git2/repository.h b/include/git2/repository.h index 4fbd913b1..2164cfac1 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -519,7 +519,7 @@ typedef int (*git_repository_mergehead_foreach_cb)(const git_oid *oid, * * @param repo A repository object * @param callback Callback function - * @param apyload Pointer to callback data (optional) + * @param payload Pointer to callback data (optional) * @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error */ GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo, diff --git a/include/git2/stash.h b/include/git2/stash.h index cf8bc9d4c..68d1b5413 100644 --- a/include/git2/stash.h +++ b/include/git2/stash.h @@ -89,7 +89,7 @@ typedef int (*git_stash_cb)( * * @param repo Repository where to find the stash. * - * @param callabck Callback to invoke per found stashed state. The most recent + * @param callback Callback to invoke per found stashed state. The most recent * stash state will be enumerated first. * * @param payload Extra parameter to callback function. diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 004665050..91b5300ae 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -481,7 +481,7 @@ GIT_EXTERN(int) git_submodule_sync(git_submodule *submodule); * function will return distinct `git_repository` objects. This will only * work if the submodule is checked out into the working directory. * - * @param subrepo Pointer to the submodule repo which was opened + * @param repo Pointer to the submodule repo which was opened * @param submodule Submodule to be opened * @return 0 on success, <0 if submodule repo could not be opened. */ @@ -531,7 +531,7 @@ GIT_EXTERN(int) git_submodule_status( * This can be useful if you want to know if the submodule is present in the * working directory at this point in time, etc. * - * @param status Combination of first four `GIT_SUBMODULE_STATUS` flags + * @param location_status Combination of first four `GIT_SUBMODULE_STATUS` flags * @param submodule Submodule for which to get status * @return 0 on success, <0 on error */ diff --git a/include/git2/tree.h b/include/git2/tree.h index d673f50c4..65d8cc16e 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -38,7 +38,7 @@ GIT_EXTERN(int) git_tree_lookup( * * @see git_object_lookup_prefix * - * @param tree pointer to the looked up tree + * @param out pointer to the looked up tree * @param repo the repo to use when locating the tree. * @param id identity of the tree to locate. * @param len the length of the short identifier @@ -136,7 +136,7 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byoid( * * @param out Pointer where to store the tree entry * @param root Previously loaded tree which is the root of the relative path - * @param subtree_path Path to the contained entry + * @param path Path to the contained entry * @return 0 on success; GIT_ENOTFOUND if the path does not exist */ GIT_EXTERN(int) git_tree_entry_bypath( @@ -212,7 +212,7 @@ GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entr * * You must call `git_object_free()` on the object when you are done with it. * - * @param object pointer to the converted object + * @param object_out pointer to the converted object * @param repo repository where to lookup the pointed object * @param entry a tree entry * @return 0 or an error code @@ -251,7 +251,7 @@ GIT_EXTERN(void) git_treebuilder_clear(git_treebuilder *bld); /** * Get the number of entries listed in a treebuilder * - * @param tree a previously loaded treebuilder. + * @param bld a previously loaded treebuilder. * @return the number of entries in the treebuilder */ GIT_EXTERN(unsigned int) git_treebuilder_entrycount(git_treebuilder *bld); From 32c12ea6a9cafd76a746af2e2be9366c95752f5b Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 24 Jun 2013 09:19:24 -0700 Subject: [PATCH 380/384] Work around reparse point stat issues In theory, p_stat should never return an S_ISLNK result, but due to the current implementation on Windows with mount points it is possible that it will. For now, work around that by allowing a link in the path to a directory being created. If it is really a problem, then the issue will be caught on the next iteration of the loop, but typically this will be the right thing to do. --- src/fileops.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fileops.c b/src/fileops.c index ae240fcd2..95b15c604 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -348,7 +348,8 @@ int git_futils_mkdir( int tmp_errno = errno; /* ignore error if directory already exists */ - if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { + if (p_stat(make_path.ptr, &st) < 0 || + !(S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) { errno = tmp_errno; giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); goto done; From f3f4c6b5bea91351a7bdb1d94e76924e76d0fcee Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 24 Jun 2013 11:56:35 -0700 Subject: [PATCH 381/384] Fix checkout tests on Windows --- tests-clar/checkout/index.c | 11 ++++++++--- tests-clar/checkout/tree.c | 9 ++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c index c7500db1d..9d8b321ae 100644 --- a/tests-clar/checkout/index.c +++ b/tests-clar/checkout/index.c @@ -26,6 +26,10 @@ void test_checkout_index__initialize(void) void test_checkout_index__cleanup(void) { cl_git_sandbox_cleanup(); + + /* try to remove alternative dir */ + if (git_path_isdir("alternative")) + git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES); } void test_checkout_index__cannot_checkout_a_bare_repository(void) @@ -576,9 +580,10 @@ void test_checkout_index__target_directory_from_bare(void) cl_assert_equal_i(0, cts.n_ignored); cl_assert_equal_i(3, cts.n_updates); - check_file_contents("./alternative/README", "hey there\n"); - check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n"); - check_file_contents("./alternative/new.txt", "my new file\n"); + /* files will have been filtered if needed, so strip CR */ + check_file_contents_nocr("./alternative/README", "hey there\n"); + check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents_nocr("./alternative/new.txt", "my new file\n"); cl_git_pass(git_futils_rmdir_r( "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index 0e65f28c8..e4bfbce06 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -24,6 +24,9 @@ void test_checkout_tree__cleanup(void) g_object = NULL; cl_git_sandbox_cleanup(); + + if (git_path_isdir("alternative")) + git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES); } void test_checkout_tree__cannot_checkout_a_non_treeish(void) @@ -667,9 +670,9 @@ void test_checkout_tree__target_directory_from_bare(void) cl_assert_equal_i(0, cts.n_ignored); cl_assert_equal_i(3, cts.n_updates); - check_file_contents("./alternative/README", "hey there\n"); - check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n"); - check_file_contents("./alternative/new.txt", "my new file\n"); + check_file_contents_nocr("./alternative/README", "hey there\n"); + check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents_nocr("./alternative/new.txt", "my new file\n"); cl_git_pass(git_futils_rmdir_r( "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); From 8c510b8313bd933e6d3b44f3ce88999d349a8dd7 Mon Sep 17 00:00:00 2001 From: Arthur Schreiber Date: Mon, 24 Jun 2013 21:02:42 +0200 Subject: [PATCH 382/384] Fix a leak in the local transport code. --- src/transports/local.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/transports/local.c b/src/transports/local.c index 4bf1c876a..550060958 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -571,6 +571,8 @@ static void local_cancel(git_transport *transport) static int local_close(git_transport *transport) { transport_local *t = (transport_local *)transport; + size_t i; + git_remote_head *head; t->connected = 0; @@ -584,18 +586,6 @@ static int local_close(git_transport *transport) t->url = NULL; } - return 0; -} - -static void local_free(git_transport *transport) -{ - transport_local *t = (transport_local *)transport; - size_t i; - git_remote_head *head; - - /* Close the transport, if it's still open. */ - local_close(transport); - git_vector_foreach(&t->refs, i, head) { git__free(head->name); git__free(head); @@ -603,6 +593,16 @@ static void local_free(git_transport *transport) git_vector_free(&t->refs); + return 0; +} + +static void local_free(git_transport *transport) +{ + transport_local *t = (transport_local *)transport; + + /* Close the transport, if it's still open. */ + local_close(transport); + /* Free the transport */ git__free(t); } From c0e58e430b5befcdbcd937193e420d86fd3658a4 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 25 Jun 2013 00:12:19 +0200 Subject: [PATCH 383/384] test-rename: This is not a decimal, silly --- tests-clar/diff/rename.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c index 79f407057..2b1873bd5 100644 --- a/tests-clar/diff/rename.c +++ b/tests-clar/diff/rename.c @@ -968,10 +968,10 @@ static void write_similarity_file_two(const char *filename, size_t b_lines) size_t i; for (i = 0; i < b_lines; i++) - git_buf_printf(&contents, "%0.2d - bbbbb\r\n", (int)(i+1)); + git_buf_printf(&contents, "%02d - bbbbb\r\n", (int)(i+1)); for (i = b_lines; i < 50; i++) - git_buf_printf(&contents, "%0.2d - aaaaa%s", (int)(i+1), (i == 49 ? "" : "\r\n")); + git_buf_printf(&contents, "%02d - aaaaa%s", (int)(i+1), (i == 49 ? "" : "\r\n")); cl_git_pass( git_futils_writebuffer(&contents, filename, O_RDWR|O_CREAT, 0777)); From eddc1f1ed78898a4ca41480045b1d0d5b075e773 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 25 Jun 2013 00:14:45 +0200 Subject: [PATCH 384/384] libgit2 v0.19.0 "gut merge" Minor point release! We got a lot of rather large features that we wanted to get settled in: - New (threadsafe) cache for objects - Iterator for Status - New Merge APIs - SSH support on *NIX - Function context on diff - Namespaces support - Index add/update/remove with wildcard support - Iterator for References - Fetch and push refspecs for Remotes - Rename support in Status - New 'sys/` namespace for external headers with low-level APIs As always, this comes with hundreds of bug fixes and performance improvements. We're faster and better than ever. And we haven't broken many APIs this time! Build stuff. --- include/git2/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/git2/version.h b/include/git2/version.h index 630d51526..d8a915fac 100644 --- a/include/git2/version.h +++ b/include/git2/version.h @@ -7,9 +7,9 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "0.18.0" +#define LIBGIT2_VERSION "0.19.0" #define LIBGIT2_VER_MAJOR 0 -#define LIBGIT2_VER_MINOR 18 +#define LIBGIT2_VER_MINOR 19 #define LIBGIT2_VER_REVISION 0 #endif