From 2a3067765f071377f2534c6b4cc33bdd048342d0 Mon Sep 17 00:00:00 2001 From: Tony Kelman Date: Fri, 22 May 2015 14:02:44 -0700 Subject: [PATCH 01/61] build maint branches on appveyor equivalent of ca183d27366ba75597a9806b746e42a074df7795 --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 9df687ffd53..166fa56b1e1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,7 @@ version: '{build}' branches: only: - master + - /^maint.*/ environment: GITTEST_INVASIVE_FS_STRUCTURE: 1 GITTEST_INVASIVE_FS_SIZE: 1 From 462865d7025807beab1323f85ceb055ed9ea2bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Sun, 24 May 2015 13:18:30 +0200 Subject: [PATCH 02/61] cred_helpers: Add 'const' qualifiers to git_cred_userpass_payload Make both username & password in git_cred_userpass_payload 'const'. The values are not altered anywhere, and the extra qualifier allows clients to assign 'const' values there. --- include/git2/cred_helpers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/git2/cred_helpers.h b/include/git2/cred_helpers.h index 1d8809211ce..840c2253696 100644 --- a/include/git2/cred_helpers.h +++ b/include/git2/cred_helpers.h @@ -22,8 +22,8 @@ GIT_BEGIN_DECL * Payload for git_cred_stock_userpass_plaintext. */ typedef struct git_cred_userpass_payload { - char *username; - char *password; + const char *username; + const char *password; } git_cred_userpass_payload; From 7a8b85032f4390b6b14e55e0074d864fe742ca3b Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 17 Mar 2015 09:19:15 -0700 Subject: [PATCH 03/61] Add support to read ssh keys from memory. --- include/git2/transport.h | 28 +++++++++++++++++++++++ src/transports/cred.c | 48 +++++++++++++++++++++++++++++++++++++++- src/transports/ssh.c | 19 ++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/include/git2/transport.h b/include/git2/transport.h index 99fd09a1b3b..57293d92fe1 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -108,6 +108,15 @@ typedef enum { * it will ask via this credential type. */ GIT_CREDTYPE_USERNAME = (1u << 5), + +#ifdef GIT_SSH_MEMORY_CREDENTIALS + /** + * Credentials read from memory. + * + * Only available for libssh2+OpenSSL for now. + */ + GIT_CREDTYPE_SSH_MEMORY = (1u << 6), +#endif } git_credtype_t; /* The base structure for all credential types */ @@ -290,6 +299,25 @@ GIT_EXTERN(int) git_cred_default_new(git_cred **out); */ GIT_EXTERN(int) git_cred_username_new(git_cred **cred, const char *username); +#ifdef GIT_SSH_MEMORY_CREDENTIALS +/** + * Create a new ssh key credential object reading the keys from memory. + * + * @param out The newly created credential object. + * @param username username to use to authenticate. + * @param publickey The public key of the credential. + * @param privatekey 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_key_memory_new( + git_cred **out, + const char *username, + const char *publickey, + const char *privatekey, + const char *passphrase); +#endif + /** * Signature of a function which acquires a credential object. * diff --git a/src/transports/cred.c b/src/transports/cred.c index 8163d311509..68007fb95b0 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -9,6 +9,14 @@ #include "smart.h" #include "git2/cred_helpers.h" +static int git_cred_ssh_key_type_new( + git_cred **cred, + const char *username, + const char *publickey, + const char *privatekey, + const char *passphrase, + git_credtype_t credtype); + int git_cred_has_username(git_cred *cred) { if (cred->credtype == GIT_CREDTYPE_DEFAULT) @@ -31,6 +39,9 @@ const char *git_cred__username(git_cred *cred) return c->username; } case GIT_CREDTYPE_SSH_KEY: +#ifdef GIT_SSH_MEMORY_CREDENTIALS + case GIT_CREDTYPE_SSH_MEMORY: +#endif { git_cred_ssh_key *c = (git_cred_ssh_key *) cred; return c->username; @@ -174,6 +185,41 @@ int git_cred_ssh_key_new( const char *publickey, const char *privatekey, const char *passphrase) +{ + return git_cred_ssh_key_type_new( + cred, + username, + publickey, + privatekey, + passphrase, + GIT_CREDTYPE_SSH_KEY); +} + +#ifdef GIT_SSH_MEMORY_CREDENTIALS +int git_cred_ssh_key_memory_new( + git_cred **cred, + const char *username, + const char *publickey, + const char *privatekey, + const char *passphrase) +{ + return git_cred_ssh_key_type_new( + cred, + username, + publickey, + privatekey, + passphrase, + GIT_CREDTYPE_SSH_MEMORY); +} +#endif + +static int git_cred_ssh_key_type_new( + git_cred **cred, + const char *username, + const char *publickey, + const char *privatekey, + const char *passphrase, + git_credtype_t credtype) { git_cred_ssh_key *c; @@ -182,7 +228,7 @@ int git_cred_ssh_key_new( c = git__calloc(1, sizeof(git_cred_ssh_key)); GITERR_CHECK_ALLOC(c); - c->parent.credtype = GIT_CREDTYPE_SSH_KEY; + c->parent.credtype = credtype; c->parent.free = ssh_key_free; c->username = git__strdup(username); diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 55f715b1d69..0a7422791f1 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -370,6 +370,22 @@ static int _git_ssh_authenticate_session( session, c->username, c->prompt_callback); break; } +#ifdef GIT_SSH_MEMORY_CREDENTIALS + case GIT_CREDTYPE_SSH_MEMORY: { + git_cred_ssh_key *c = (git_cred_ssh_key *)cred; + + rc = libssh2_userauth_publickey_frommemory( + session, + c->username, + strlen(c->username), + c->publickey, + strlen(c->publickey), + c->privatekey, + strlen(c->privatekey), + c->passphrase); + break; + } +#endif default: rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED; } @@ -740,6 +756,9 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) { *out |= GIT_CREDTYPE_SSH_KEY; *out |= GIT_CREDTYPE_SSH_CUSTOM; +#ifdef GIT_SSH_MEMORY_CREDENTIALS + *out |= GIT_CREDTYPE_SSH_MEMORY; +#endif ptr += strlen(SSH_AUTH_PUBLICKEY); continue; } From 08e6b875c11e71b27154d4fb0d36d56ac40aba33 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 19 Mar 2015 14:57:15 -0700 Subject: [PATCH 04/61] Return an error when ssh memory credentials are not supported. To not modify the external api. --- include/git2/transport.h | 2 -- src/transports/cred.c | 8 ++++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/git2/transport.h b/include/git2/transport.h index 57293d92fe1..4008615ab95 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -299,7 +299,6 @@ GIT_EXTERN(int) git_cred_default_new(git_cred **out); */ GIT_EXTERN(int) git_cred_username_new(git_cred **cred, const char *username); -#ifdef GIT_SSH_MEMORY_CREDENTIALS /** * Create a new ssh key credential object reading the keys from memory. * @@ -316,7 +315,6 @@ GIT_EXTERN(int) git_cred_ssh_key_memory_new( const char *publickey, const char *privatekey, const char *passphrase); -#endif /** * Signature of a function which acquires a credential object. diff --git a/src/transports/cred.c b/src/transports/cred.c index 68007fb95b0..c1f305d77c4 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -195,7 +195,6 @@ int git_cred_ssh_key_new( GIT_CREDTYPE_SSH_KEY); } -#ifdef GIT_SSH_MEMORY_CREDENTIALS int git_cred_ssh_key_memory_new( git_cred **cred, const char *username, @@ -203,6 +202,7 @@ int git_cred_ssh_key_memory_new( const char *privatekey, const char *passphrase) { +#ifdef GIT_SSH_MEMORY_CREDENTIALS return git_cred_ssh_key_type_new( cred, username, @@ -210,8 +210,12 @@ int git_cred_ssh_key_memory_new( privatekey, passphrase, GIT_CREDTYPE_SSH_MEMORY); -} +#else + giterr_set(GITERR_INVALID, + "This version of libgit2 was not built with ssh memory credentials."); + return -1; #endif +} static int git_cred_ssh_key_type_new( git_cred **cred, From 1679ec1204b53580a7b5cd6165865854c912dc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Sun, 24 May 2015 18:27:15 +0200 Subject: [PATCH 05/61] cmake: Add CMake check for libssh2 memory credential passing support --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 181d30127d6..c3c7a154398 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -270,6 +270,11 @@ IF (LIBSSH2_FOUND) LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS}) SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} libssh2") SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES}) + + CHECK_LIBRARY_EXISTS(${LIBSSH2_LIBRARIES} libssh2_userauth_publickey_frommemory "" HAVE_LIBSSH2_MEMORY_CREDENTIALS) + IF (HAVE_LIBSSH2_MEMORY_CREDENTIALS) + ADD_DEFINITIONS(-DGIT_SSH_MEMORY_CREDENTIALS) + ENDIF() ELSE() MESSAGE(STATUS "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.") ENDIF() From f7142b5e4a9ce3f389f7851aac2fc98ce7bef764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Sun, 24 May 2015 18:38:47 +0200 Subject: [PATCH 06/61] cred: Declare GIT_CREDTYPE_SSH_MEMORY unconditionally Declare GIT_CREDTYPE_SSH_MEMORY to have consistent API independently of whether libgit2 was built with or without in-memory key passing support. Or rather, to have it at all since build-time definitions are not stored in headers. --- include/git2/transport.h | 2 -- src/transports/cred.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/include/git2/transport.h b/include/git2/transport.h index 4008615ab95..2eeebd56589 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -109,14 +109,12 @@ typedef enum { */ GIT_CREDTYPE_USERNAME = (1u << 5), -#ifdef GIT_SSH_MEMORY_CREDENTIALS /** * Credentials read from memory. * * Only available for libssh2+OpenSSL for now. */ GIT_CREDTYPE_SSH_MEMORY = (1u << 6), -#endif } git_credtype_t; /* The base structure for all credential types */ diff --git a/src/transports/cred.c b/src/transports/cred.c index c1f305d77c4..006cd2c528e 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -39,9 +39,7 @@ const char *git_cred__username(git_cred *cred) return c->username; } case GIT_CREDTYPE_SSH_KEY: -#ifdef GIT_SSH_MEMORY_CREDENTIALS case GIT_CREDTYPE_SSH_MEMORY: -#endif { git_cred_ssh_key *c = (git_cred_ssh_key *) cred; return c->username; From 2629fc874da258d76a63a99566a5ba457c9d5d30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Sun, 24 May 2015 22:33:55 +0200 Subject: [PATCH 07/61] cred: Check for null values when getting key from memory The public key field is optional and as such can take NULL. Account for that and do not call strlen() on NULL values. Also assert() for non-NULL values of username & private key. --- src/transports/ssh.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 0a7422791f1..58f1aeb64ff 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -374,12 +374,15 @@ static int _git_ssh_authenticate_session( case GIT_CREDTYPE_SSH_MEMORY: { git_cred_ssh_key *c = (git_cred_ssh_key *)cred; + assert(c->username); + assert(c->privatekey); + rc = libssh2_userauth_publickey_frommemory( session, c->username, strlen(c->username), c->publickey, - strlen(c->publickey), + c->publickey ? strlen(c->publickey) : 0, c->privatekey, strlen(c->privatekey), c->passphrase); From 8085adf82cbb57487d3ac9acdf0b5bcba92a9a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 27 May 2015 18:44:40 +0200 Subject: [PATCH 08/61] test: Add a test for in-memory SSH private key cred_cb --- tests/online/clone.c | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/online/clone.c b/tests/online/clone.c index 1930a8ba3d6..fa0dbd69ca3 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -501,6 +501,74 @@ void test_online_clone__ssh_cert(void) cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, "ssh://localhost/foo", "./foo", &g_options)); } +static char *read_key_file(const char *path) +{ + FILE *f; + char *buf; + long key_length; + + if (!path || !*path) + return NULL; + + cl_assert((f = fopen(path, "r")) != NULL); + cl_assert(fseek(f, 0, SEEK_END) != -1); + cl_assert((key_length = ftell(f)) != -1); + cl_assert(fseek(f, 0, SEEK_SET) != -1); + cl_assert((buf = malloc(key_length)) != NULL); + cl_assert(fread(buf, key_length, 1, f) == 1); + fclose(f); + + return buf; +} + +static int ssh_memory_cred_cb(git_cred **cred, const char *url, const char *user_from_url, + unsigned int allowed_types, void *payload) +{ + const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); + const char *pubkey_path = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY"); + const char *privkey_path = cl_getenv("GITTEST_REMOTE_SSH_KEY"); + const char *passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); + + GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload); + + if (allowed_types & GIT_CREDTYPE_USERNAME) + return git_cred_username_new(cred, remote_user); + + if (allowed_types & GIT_CREDTYPE_SSH_KEY) + { + char *pubkey = read_key_file(pubkey_path); + char *privkey = read_key_file(privkey_path); + + int ret = git_cred_ssh_key_memory_new(cred, remote_user, pubkey, privkey, passphrase); + + if (privkey) + free(privkey); + if (pubkey) + free(pubkey); + return ret; + } + + giterr_set(GITERR_NET, "unexpected cred type"); + return -1; +} + +void test_online_clone__ssh_memory_auth(void) +{ + const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); + const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); + const char *privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY"); + +#ifndef GIT_SSH_MEMORY_CREDENTIALS + clar__skip(); +#endif + if (!remote_url || !remote_user || !privkey || strncmp(remote_url, "ssh://", 5) != 0) + clar__skip(); + + g_options.fetch_opts.callbacks.credentials = ssh_memory_cred_cb; + + cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options)); +} + void test_online_clone__url_with_no_path_returns_EINVALIDSPEC(void) { cl_git_fail_with(git_clone(&g_repo, "http://github.com", "./foo", &g_options), From 69560cc089c557efe23792e4e98fc7f5ddbb497a Mon Sep 17 00:00:00 2001 From: Edgar Aroutiounian Date: Thu, 28 May 2015 15:19:53 -0400 Subject: [PATCH 09/61] Changed README to use new OCaml bindings to git --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d9cd2d1ee3..4ee1fdc2ad1 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ Here are the bindings to libgit2 that are currently available: * Objective-C * objective-git * OCaml - * libgit2-ocaml + * ocaml-libgit2 * Parrot Virtual Machine * parrot-libgit2 * Perl From 885b94aac06f17c55bd6f8df318e0cffb0104efa Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 28 May 2015 15:26:13 -0400 Subject: [PATCH 10/61] Rename GIT_EMERGECONFLICT to GIT_ECONFLICT We do not error on "merge conflicts"; on the contrary, merge conflicts are a normal part of merging. We only error on "checkout conflicts", where a change exists in the index or the working directory that would otherwise be overwritten by performing the checkout. This *may* happen during merge (after the production of the new index that we're going to checkout) but it could happen during any checkout. --- CHANGELOG.md | 3 +++ include/git2/errors.h | 2 +- src/checkout.c | 6 +++--- src/merge.c | 2 +- src/stash.c | 2 +- tests/checkout/tree.c | 14 +++++++------- tests/stash/apply.c | 10 +++++----- 7 files changed, 21 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99e3759029f..3ca9bc6ec56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -229,6 +229,9 @@ support for HTTPS connections insead of OpenSSL. index. Specifically, time and file size are 32 bits intead of 64, as these values are truncated. +* `GIT_EMERGECONFLICT` is now `GIT_ECONFLICT`, which more accurately + describes the nature of the error. + v0.22 ------ diff --git a/include/git2/errors.h b/include/git2/errors.h index 31fc6035a41..dda0f8a572a 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -38,7 +38,7 @@ typedef enum { GIT_EUNMERGED = -10, /**< Merge in progress prevented operation */ GIT_ENONFASTFORWARD = -11, /**< Reference was not fast-forwardable */ GIT_EINVALIDSPEC = -12, /**< Name/ref spec was not in a valid format */ - GIT_EMERGECONFLICT = -13, /**< Merge conflicts prevented operation */ + GIT_ECONFLICT = -13, /**< Checkout conflicts prevented operation */ GIT_ELOCKED = -14, /**< Lock file prevented operation */ GIT_EMODIFIED = -15, /**< Reference value does not match expected */ GIT_EAUTH = -16, /**< Authentication error */ diff --git a/src/checkout.c b/src/checkout.c index 6a1d2813615..cc73e483fc2 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1292,7 +1292,7 @@ static int checkout_get_actions( (int)counts[CHECKOUT_ACTION__CONFLICT], counts[CHECKOUT_ACTION__CONFLICT] == 1 ? "conflict prevents" : "conflicts prevent"); - error = GIT_EMERGECONFLICT; + error = GIT_ECONFLICT; goto fail; } @@ -2062,7 +2062,7 @@ static int checkout_write_merge( if (result.path == NULL || result.mode == 0) { giterr_set(GITERR_CHECKOUT, "Could not merge contents of file"); - error = GIT_EMERGECONFLICT; + error = GIT_ECONFLICT; goto done; } @@ -2357,7 +2357,7 @@ static int checkout_data_init( /* cannot checkout if unresolved conflicts exist */ if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) == 0 && git_index_has_conflicts(data->index)) { - error = GIT_EMERGECONFLICT; + error = GIT_ECONFLICT; giterr_set(GITERR_CHECKOUT, "unresolved conflicts exist in the index"); goto cleanup; diff --git a/src/merge.c b/src/merge.c index eaf7eee1f8e..28fca003807 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2509,7 +2509,7 @@ int git_merge__check_result(git_repository *repo, git_index *index_new) if ((conflicts = index_conflicts + wd_conflicts) > 0) { giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge", conflicts, (conflicts != 1) ? "s" : ""); - error = GIT_EMERGECONFLICT; + error = GIT_ECONFLICT; } done: diff --git a/src/stash.c b/src/stash.c index 0c5cd1d2afa..a7a288c2558 100644 --- a/src/stash.c +++ b/src/stash.c @@ -761,7 +761,7 @@ int git_stash_apply( goto cleanup; if (git_index_has_conflicts(unstashed_index)) { - error = GIT_EMERGECONFLICT; + error = GIT_ECONFLICT; goto cleanup; } } diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c index 46232318696..be4019822f4 100644 --- a/tests/checkout/tree.c +++ b/tests/checkout/tree.c @@ -306,7 +306,7 @@ void test_checkout_tree__conflict_on_ignored_when_not_overwriting(void) cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir( GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, false)); - cl_assert_equal_i(GIT_EMERGECONFLICT, error); + cl_assert_equal_i(GIT_ECONFLICT, error); } void test_checkout_tree__can_overwrite_ignored_by_default(void) @@ -327,7 +327,7 @@ void test_checkout_tree__conflict_on_ignored_folder_when_not_overwriting(void) cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir( GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, true)); - cl_assert_equal_i(GIT_EMERGECONFLICT, error); + cl_assert_equal_i(GIT_ECONFLICT, error); } void test_checkout_tree__can_overwrite_ignored_folder_by_default(void) @@ -512,7 +512,7 @@ void assert_conflict( g_opts.checkout_strategy = GIT_CHECKOUT_SAFE; cl_assert_equal_i( - GIT_EMERGECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts)); + GIT_ECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts)); /* Stage the conflicting change */ cl_git_pass(git_index_add_bypath(index, entry_path)); @@ -520,10 +520,10 @@ void assert_conflict( git_index_free(index); cl_assert_equal_i( - GIT_EMERGECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts)); + GIT_ECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts)); } -void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECONFLICT(void) +void test_checkout_tree__checking_out_a_conflicting_type_change_returns_ECONFLICT(void) { /* * 099faba adds a symlink named 'link_to_new.txt' @@ -533,7 +533,7 @@ void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECO assert_conflict("link_to_new.txt", "old.txt", "a65fedf", "099faba"); } -void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECONFLICT_2(void) +void test_checkout_tree__checking_out_a_conflicting_type_change_returns_ECONFLICT_2(void) { /* * cf80f8d adds a directory named 'a/' @@ -543,7 +543,7 @@ void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECO assert_conflict("a", "hello\n", "a4a7dce", "cf80f8d"); } -void test_checkout_tree__checking_out_a_conflicting_content_change_returns_EMERGECONFLICT(void) +void test_checkout_tree__checking_out_a_conflicting_content_change_returns_ECONFLICT(void) { /* * c47800c adds a symlink named 'branch_file.txt' diff --git a/tests/stash/apply.c b/tests/stash/apply.c index 42186b6fb85..e9b56f8c61a 100644 --- a/tests/stash/apply.c +++ b/tests/stash/apply.c @@ -118,7 +118,7 @@ void test_stash_apply__conflict_index_with_reinstate_index(void) cl_git_pass(git_index_add_bypath(repo_index, "who")); cl_git_pass(git_index_write(repo_index)); - cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_EMERGECONFLICT); + cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT); cl_assert_equal_i(git_index_has_conflicts(repo_index), 0); assert_status(repo, "what", GIT_STATUS_CURRENT); @@ -133,7 +133,7 @@ void test_stash_apply__conflict_untracked_with_default(void) cl_git_mkfile("stash/when", "nothing\n"); - cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_EMERGECONFLICT); + cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT); cl_assert_equal_i(git_index_has_conflicts(repo_index), 0); assert_status(repo, "what", GIT_STATUS_CURRENT); @@ -150,7 +150,7 @@ void test_stash_apply__conflict_untracked_with_reinstate_index(void) cl_git_mkfile("stash/when", "nothing\n"); - cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_EMERGECONFLICT); + cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT); cl_assert_equal_i(git_index_has_conflicts(repo_index), 0); assert_status(repo, "what", GIT_STATUS_CURRENT); @@ -163,7 +163,7 @@ void test_stash_apply__conflict_workdir_with_default(void) { cl_git_rewritefile("stash/what", "ciao\n"); - cl_git_fail_with(git_stash_apply(repo, 0, NULL), GIT_EMERGECONFLICT); + cl_git_fail_with(git_stash_apply(repo, 0, NULL), GIT_ECONFLICT); cl_assert_equal_i(git_index_has_conflicts(repo_index), 0); assert_status(repo, "what", GIT_STATUS_WT_MODIFIED); @@ -180,7 +180,7 @@ void test_stash_apply__conflict_workdir_with_reinstate_index(void) cl_git_rewritefile("stash/what", "ciao\n"); - cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_EMERGECONFLICT); + cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT); cl_assert_equal_i(git_index_has_conflicts(repo_index), 0); assert_status(repo, "what", GIT_STATUS_WT_MODIFIED); From a5670d4f2da38b9f0fde73d44ea8898372c39e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 29 May 2015 20:09:46 +0200 Subject: [PATCH 11/61] CHANGELOG: fill in a few missing entries --- CHANGELOG.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ca9bc6ec56..6f426825d3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,10 @@ support for HTTPS connections insead of OpenSSL. for understanding whether a side of the diff exists in the presence of a conflict. +* The constructor for a write-stream into the odb now takes + `git_off_t` instead of `size_t` for the size of the blob, which + allows putting large files into the odb on 32-bit systems. + ### API additions * The `git_merge_options` gained a `file_flags` member. @@ -94,6 +98,10 @@ support for HTTPS connections insead of OpenSSL. * The enum `git_fetch_prune_t` has been added, letting you specify the pruning behaviour for a fetch. +* A push operation will notify the caller of what updates it indends + to perform on the remote, which provides similar information to + git's pre-push hook. + * `git_stash_apply()` can now apply a stashed state from the stash list, placing the data into the working directory and index. @@ -117,6 +125,10 @@ support for HTTPS connections insead of OpenSSL. a given index entry has a non-zero stage entry, indicating that it is one side of a conflict. +* It is now possible to pass a keypair via a buffer instead of a + path. For this, `GIT_CREDTYPE_SSH_MEMORY` and + `git_cred_ssh_key_memory_new()` have been added. + ### API removals * `git_remote_save()` and `git_remote_clear_refspecs()` have been @@ -180,11 +192,11 @@ support for HTTPS connections insead of OpenSSL. if it is called immediately after creating a rebase session but before you have applied the first patch. -* `git_rebase_options` now contains an optional pointer to - `git_checkout_options` that will be used for functions that modify - the working directory, namely `git_checkout_init`, `git_checkout_next` - and `git_checkout_abort`. As a result, `git_rebase_open` now also - takes a `git_rebase_options` and only the `git_rebase_init` and +* `git_rebase_options` now contains a `git_checkout_options` struct + that will be used for functions that modify the working directory, + namely `git_checkout_init`, `git_checkout_next` and + `git_checkout_abort`. As a result, `git_rebase_open` now also takes + a `git_rebase_options` and only the `git_rebase_init` and `git_rebase_open` functions take a `git_rebase_options`, where they will persist the options to subsequent `git_rebase` calls. @@ -192,6 +204,9 @@ support for HTTPS connections insead of OpenSSL. `fetch_opts` field instead of remote callbacks in `remote_callbacks`. +* The remote callbacks has gained a new member `push_negotiation` + which gets called before sending the update commands to the server. + * The following functions now longer act on a remote instance but change the repository's configuration. Their signatures have changed accordingly: From 006548da91bfe05375ae0e786c6c13e9bad85a40 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 29 May 2015 16:07:51 -0400 Subject: [PATCH 12/61] git__strcasecmp: treat input bytes as unsigned Treat input bytes as unsigned before doing arithmetic on them, lest we look at some non-ASCII byte (like a UTF-8 character) as a negative value and perform the comparison incorrectly. --- src/util.c | 4 ++-- tests/core/string.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/util.c b/src/util.c index 6bb7d03ee4f..b8d6cf58c2f 100644 --- a/src/util.c +++ b/src/util.c @@ -173,7 +173,7 @@ int git__strcasecmp(const char *a, const char *b) { while (*a && *b && tolower(*a) == tolower(*b)) ++a, ++b; - return (tolower(*a) - tolower(*b)); + return ((unsigned char)tolower(*a) - (unsigned char)tolower(*b)); } int git__strcasesort_cmp(const char *a, const char *b) @@ -193,7 +193,7 @@ int git__strcasesort_cmp(const char *a, const char *b) } if (*a || *b) - return tolower(*a) - tolower(*b); + return (unsigned char)tolower(*a) - (unsigned char)tolower(*b); return cmp; } diff --git a/tests/core/string.c b/tests/core/string.c index ec95756859f..c6c2d95ac68 100644 --- a/tests/core/string.c +++ b/tests/core/string.c @@ -39,3 +39,41 @@ void test_core_string__2(void) cl_assert(git__strcasesort_cmp("BAR", "foo") < 0); cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0); } + +void test_core_string__strcmp(void) +{ + cl_assert(git__strcmp("", "") == 0); + cl_assert(git__strcmp("foo", "foo") == 0); + cl_assert(git__strcmp("Foo", "foo") < 0); + cl_assert(git__strcmp("foo", "FOO") > 0); + cl_assert(git__strcmp("foo", "fOO") > 0); + + cl_assert(strcmp("rt\303\202of", "rt dev\302\266h") > 0); + cl_assert(strcmp("e\342\202\254ghi=", "et") > 0); + cl_assert(strcmp("rt dev\302\266h", "rt\303\202of") < 0); + cl_assert(strcmp("et", "e\342\202\254ghi=") < 0); + + cl_assert(git__strcmp("rt\303\202of", "rt dev\302\266h") > 0); + cl_assert(git__strcmp("e\342\202\254ghi=", "et") > 0); + cl_assert(git__strcmp("rt dev\302\266h", "rt\303\202of") < 0); + cl_assert(git__strcmp("et", "e\342\202\254ghi=") < 0); +} + +void test_core_string__strcasecmp(void) +{ + cl_assert(git__strcasecmp("", "") == 0); + cl_assert(git__strcasecmp("foo", "foo") == 0); + cl_assert(git__strcasecmp("foo", "Foo") == 0); + cl_assert(git__strcasecmp("foo", "FOO") == 0); + cl_assert(git__strcasecmp("foo", "fOO") == 0); + + cl_assert(strcasecmp("rt\303\202of", "rt dev\302\266h") > 0); + cl_assert(strcasecmp("e\342\202\254ghi=", "et") > 0); + cl_assert(strcasecmp("rt dev\302\266h", "rt\303\202of") < 0); + cl_assert(strcasecmp("et", "e\342\202\254ghi=") < 0); + + cl_assert(git__strcasecmp("rt\303\202of", "rt dev\302\266h") > 0); + cl_assert(git__strcasecmp("e\342\202\254ghi=", "et") > 0); + cl_assert(git__strcasecmp("rt dev\302\266h", "rt\303\202of") < 0); + cl_assert(git__strcasecmp("et", "e\342\202\254ghi=") < 0); +} From 75a4636f502908ddd406a69a4b065e29b79276da Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 29 May 2015 16:56:38 -0400 Subject: [PATCH 13/61] git__tolower: a tolower() that isn't dumb Some brain damaged tolower() implementations appear to want to take the locale into account, and this may require taking some insanely aggressive lock on the locale and slowing down what should be the most trivial of trivial calls for people who just want to downcase ASCII. --- src/config_file.c | 4 ++-- src/fnmatch.c | 10 +++++----- src/iterator.c | 8 ++++---- src/netops.c | 6 +++--- src/path.c | 2 +- src/util.c | 18 +++++++++--------- src/util.h | 9 +++++++++ 7 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index d6a62b85c49..52a5376bd9a 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -156,7 +156,7 @@ int git_config_file_normalize_section(char *start, char *end) if (end && scan >= end) break; if (isalnum(*scan)) - *scan = (char)tolower(*scan); + *scan = (char)git__tolower(*scan); else if (*scan != '-' || scan == start) return GIT_EINVALIDSPEC; } @@ -1083,7 +1083,7 @@ static int parse_section_header(struct reader *reader, char **section_out) goto fail_parse; } - name[name_length++] = (char) tolower(c); + name[name_length++] = (char)git__tolower(c); } while ((c = line[pos++]) != ']'); diff --git a/src/fnmatch.c b/src/fnmatch.c index d7899e3e604..a2945b8db34 100644 --- a/src/fnmatch.c +++ b/src/fnmatch.c @@ -164,8 +164,8 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs) default: normal: if (c != *string && !((flags & FNM_CASEFOLD) && - (tolower((unsigned char)c) == - tolower((unsigned char)*string)))) + (git__tolower((unsigned char)c) == + git__tolower((unsigned char)*string)))) return (FNM_NOMATCH); ++string; break; @@ -190,7 +190,7 @@ rangematch(const char *pattern, char test, int flags, char **newp) ++pattern; if (flags & FNM_CASEFOLD) - test = (char)tolower((unsigned char)test); + test = (char)git__tolower((unsigned char)test); /* * A right bracket shall lose its special meaning and represent @@ -207,7 +207,7 @@ rangematch(const char *pattern, char test, int flags, char **newp) if (c == '/' && (flags & FNM_PATHNAME)) return (RANGE_NOMATCH); if ((flags & FNM_CASEFOLD)) - c = (char)tolower((unsigned char)c); + c = (char)git__tolower((unsigned char)c); if (*pattern == '-' && (c2 = *(pattern+1)) != EOS && c2 != ']') { pattern += 2; @@ -216,7 +216,7 @@ rangematch(const char *pattern, char test, int flags, char **newp) if (c2 == EOS) return (RANGE_ERROR); if (flags & FNM_CASEFOLD) - c2 = (char)tolower((unsigned char)c2); + c2 = (char)git__tolower((unsigned char)c2); if (c <= test && test <= c2) ok = 1; } else if (c == test) diff --git a/src/iterator.c b/src/iterator.c index c5c5fd7ceff..52b4cd72496 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1401,10 +1401,10 @@ GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path) 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]) != '.') + if (git__tolower(path->ptr[len - 1]) != 't' || + git__tolower(path->ptr[len - 2]) != 'i' || + git__tolower(path->ptr[len - 3]) != 'g' || + git__tolower(path->ptr[len - 4]) != '.') return false; return (len == 4 || path->ptr[len - 5] == '/'); diff --git a/src/netops.c b/src/netops.c index 6047cf1ac87..5e807559768 100644 --- a/src/netops.c +++ b/src/netops.c @@ -84,7 +84,7 @@ void gitno_consume_n(gitno_buffer *buf, size_t cons) int gitno__match_host(const char *pattern, const char *host) { for (;;) { - char c = tolower(*pattern++); + char c = git__tolower(*pattern++); if (c == '\0') return *host ? -1 : 0; @@ -102,7 +102,7 @@ int gitno__match_host(const char *pattern, const char *host) */ while(*host) { - char h = tolower(*host); + char h = git__tolower(*host); if (c == h) return gitno__match_host(pattern, host++); if (h == '.') @@ -112,7 +112,7 @@ int gitno__match_host(const char *pattern, const char *host) return -1; } - if (c != tolower(*host++)) + if (c != git__tolower(*host++)) return -1; } diff --git a/src/path.c b/src/path.c index 81b4d51df84..d79153599e4 100644 --- a/src/path.c +++ b/src/path.c @@ -1471,7 +1471,7 @@ static int32_t next_hfs_char(const char **in, size_t *len) * the ASCII range, which is perfectly fine, because the * git folder name can only be composed of ascii characters */ - return tolower(codepoint); + return git__tolower(codepoint); } return 0; /* NULL byte -- end of string */ } diff --git a/src/util.c b/src/util.c index b8d6cf58c2f..c62826420f6 100644 --- a/src/util.c +++ b/src/util.c @@ -171,9 +171,9 @@ int git__strcmp(const char *a, const char *b) int git__strcasecmp(const char *a, const char *b) { - while (*a && *b && tolower(*a) == tolower(*b)) + while (*a && *b && git__tolower(*a) == git__tolower(*b)) ++a, ++b; - return ((unsigned char)tolower(*a) - (unsigned char)tolower(*b)); + return ((unsigned char)git__tolower(*a) - (unsigned char)git__tolower(*b)); } int git__strcasesort_cmp(const char *a, const char *b) @@ -182,7 +182,7 @@ int git__strcasesort_cmp(const char *a, const char *b) while (*a && *b) { if (*a != *b) { - if (tolower(*a) != tolower(*b)) + if (git__tolower(*a) != git__tolower(*b)) break; /* use case in sort order even if not in equivalence */ if (!cmp) @@ -193,7 +193,7 @@ int git__strcasesort_cmp(const char *a, const char *b) } if (*a || *b) - return (unsigned char)tolower(*a) - (unsigned char)tolower(*b); + return (unsigned char)git__tolower(*a) - (unsigned char)git__tolower(*b); return cmp; } @@ -212,8 +212,8 @@ int git__strncasecmp(const char *a, const char *b, size_t sz) int al, bl; do { - al = (unsigned char)tolower(*a); - bl = (unsigned char)tolower(*b); + al = (unsigned char)git__tolower(*a); + bl = (unsigned char)git__tolower(*b); ++a, ++b; } while (--sz && al && al == bl); @@ -225,7 +225,7 @@ void git__strntolower(char *str, size_t len) size_t i; for (i = 0; i < len; ++i) { - str[i] = (char) tolower(str[i]); + str[i] = (char)git__tolower(str[i]); } } @@ -255,8 +255,8 @@ int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix) int s, p; while(str_n--) { - s = (unsigned char)tolower(*str++); - p = (unsigned char)tolower(*prefix++); + s = (unsigned char)git__tolower(*str++); + p = (unsigned char)git__tolower(*prefix++); if (s != p) return s - p; diff --git a/src/util.h b/src/util.h index be6534580d1..b2abbe6a652 100644 --- a/src/util.h +++ b/src/util.h @@ -275,6 +275,15 @@ extern char *git__strsep(char **end, const char *sep); extern void git__strntolower(char *str, size_t len); extern void git__strtolower(char *str); +#ifdef GIT_WIN32 +GIT_INLINE(int) git__tolower(int c) +{ + return (c >= 'A' && c <= 'Z') ? (c + 32) : c; +} +#else +# define git__tolower(a) tolower(a) +#endif + GIT_INLINE(const char *) git__next_line(const char *s) { while (*s && *s != '\n') s++; From bad33a5dfb4bc0b2dab5d8c9736508429170b24a Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 29 May 2015 17:39:11 -0400 Subject: [PATCH 14/61] git__tolower: test that some non-ASCII downcasing isn't --- tests/core/string.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/core/string.c b/tests/core/string.c index c6c2d95ac68..90e8fa027cf 100644 --- a/tests/core/string.c +++ b/tests/core/string.c @@ -52,11 +52,13 @@ void test_core_string__strcmp(void) cl_assert(strcmp("e\342\202\254ghi=", "et") > 0); cl_assert(strcmp("rt dev\302\266h", "rt\303\202of") < 0); cl_assert(strcmp("et", "e\342\202\254ghi=") < 0); + cl_assert(strcmp("\303\215", "\303\255") < 0); cl_assert(git__strcmp("rt\303\202of", "rt dev\302\266h") > 0); cl_assert(git__strcmp("e\342\202\254ghi=", "et") > 0); cl_assert(git__strcmp("rt dev\302\266h", "rt\303\202of") < 0); cl_assert(git__strcmp("et", "e\342\202\254ghi=") < 0); + cl_assert(git__strcmp("\303\215", "\303\255") < 0); } void test_core_string__strcasecmp(void) @@ -71,9 +73,11 @@ void test_core_string__strcasecmp(void) cl_assert(strcasecmp("e\342\202\254ghi=", "et") > 0); cl_assert(strcasecmp("rt dev\302\266h", "rt\303\202of") < 0); cl_assert(strcasecmp("et", "e\342\202\254ghi=") < 0); + cl_assert(strcasecmp("\303\215", "\303\255") < 0); cl_assert(git__strcasecmp("rt\303\202of", "rt dev\302\266h") > 0); cl_assert(git__strcasecmp("e\342\202\254ghi=", "et") > 0); cl_assert(git__strcasecmp("rt dev\302\266h", "rt\303\202of") < 0); cl_assert(git__strcasecmp("et", "e\342\202\254ghi=") < 0); + cl_assert(git__strcasecmp("\303\215", "\303\255") < 0); } From ec0c4c400172e47cb3324c1b78ebc739ee6cc184 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 4 May 2015 11:59:20 +0200 Subject: [PATCH 15/61] remote: apply insteadOf configuration. A remote's URLs are now modified according to the url.*.insteadOf and url.*.pushInsteadOf configurations. This allows a user to replace URL prefixes by setting the corresponding keys. E.g. "url.foo.insteadOf = bar" would replace the prefix "bar" with the new prefix "foo". --- include/git2/remote.h | 6 ++ src/remote.c | 75 ++++++++++++++++++++++-- tests/remote/insteadof.c | 60 +++++++++++++++++++ tests/resources/testrepo2/.gitted/config | 12 ++++ 4 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 tests/remote/insteadof.c diff --git a/include/git2/remote.h b/include/git2/remote.h index 02d73a0e620..ccd0b43f466 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -120,6 +120,9 @@ GIT_EXTERN(const char *) git_remote_name(const git_remote *remote); /** * Get the remote's url * + * If url.*.insteadOf has been configured for this URL, it will + * return the modified URL. + * * @param remote the remote * @return a pointer to the url */ @@ -128,6 +131,9 @@ GIT_EXTERN(const char *) git_remote_url(const git_remote *remote); /** * Get the remote's url for pushing * + * If url.*.pushInsteadOf has been configured for this URL, it + * will return the modified URL. + * * @param remote the remote * @return a pointer to the url or NULL if no special url for pushing is set */ diff --git a/src/remote.c b/src/remote.c index 43b34561dc7..b7acbb9c1a5 100644 --- a/src/remote.c +++ b/src/remote.c @@ -28,6 +28,7 @@ static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs); static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name); +char *apply_insteadof(git_config *config, const char *url, int direction); static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch) { @@ -207,7 +208,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n canonicalize_url(&canonical_url, url) < 0) goto on_error; - remote->url = git_buf_detach(&canonical_url); + remote->url = apply_insteadof(repo->_config, canonical_url.ptr, GIT_DIRECTION_FETCH); if (name != NULL) { remote->name = git__strdup(name); @@ -216,7 +217,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0) goto on_error; - if ((error = git_config_set_string(config, var.ptr, remote->url)) < 0) + if ((error = git_config_set_string(config, var.ptr, canonical_url.ptr)) < 0) goto on_error; } @@ -341,7 +342,7 @@ int git_remote_dup(git_remote **dest, git_remote *source) if (source->url != NULL) { remote->url = git__strdup(source->url); - GITERR_CHECK_ALLOC(remote->url); + GITERR_CHECK_ALLOC(remote->url); } if (source->pushurl != NULL) { @@ -456,7 +457,7 @@ int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO; if (found && strlen(val) > 0) { - remote->url = git__strdup(val); + remote->url = apply_insteadof(config, val, GIT_DIRECTION_FETCH); GITERR_CHECK_ALLOC(remote->url); } @@ -476,7 +477,7 @@ int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) } if (found && strlen(val) > 0) { - remote->pushurl = git__strdup(val); + remote->pushurl = apply_insteadof(config, val, GIT_DIRECTION_PUSH); GITERR_CHECK_ALLOC(remote->pushurl); } @@ -2421,3 +2422,67 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ git_remote_disconnect(remote); return error; } + +#define PREFIX "url" +#define SUFFIX_FETCH "insteadof" +#define SUFFIX_PUSH "pushinsteadof" + +char *apply_insteadof(git_config *config, const char *url, int direction) +{ + size_t match_length, prefix_length, suffix_length; + char *replacement = NULL; + const char *regexp; + + git_buf result = GIT_BUF_INIT; + git_config_entry *entry; + git_config_iterator *iter; + + assert(config); + assert(url); + assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH); + + /* Add 1 to prefix/suffix length due to the additional escaped dot */ + prefix_length = strlen(PREFIX) + 1; + if (direction == GIT_DIRECTION_FETCH) { + regexp = PREFIX "\\..*\\." SUFFIX_FETCH; + suffix_length = strlen(SUFFIX_FETCH) + 1; + } else { + regexp = PREFIX "\\..*\\." SUFFIX_PUSH; + suffix_length = strlen(SUFFIX_PUSH) + 1; + } + + git_config_iterator_glob_new(&iter, config, regexp); + + match_length = 0; + while (git_config_next(&entry, iter) == 0) { + size_t n, replacement_length; + + /* Check if entry value is a prefix of URL */ + if (git__prefixcmp(url, entry->value)) + continue; + /* Check if entry value is longer than previous + * prefixes */ + if ((n = strlen(entry->value)) <= match_length) + continue; + + git__free(replacement); + match_length = n; + + /* Cut off prefix and suffix of the value */ + replacement_length = + strlen(entry->name) - (prefix_length + suffix_length); + replacement = git__strndup(entry->name + prefix_length, + replacement_length); + } + + git_config_iterator_free(iter); + + if (match_length == 0) + return git__strdup(url); + + git_buf_printf(&result, "%s%s", replacement, url + match_length); + + git__free(replacement); + + return result.ptr; +} diff --git a/tests/remote/insteadof.c b/tests/remote/insteadof.c new file mode 100644 index 00000000000..c9b39f0984e --- /dev/null +++ b/tests/remote/insteadof.c @@ -0,0 +1,60 @@ +#include "clar_libgit2.h" +#include "remote.h" +#include "repository.h" + +#define REPO_PATH "testrepo2/.gitted" +#define REMOTE_ORIGIN "origin" +#define REMOTE_INSTEADOF "insteadof-test" + +static git_repository *g_repo; +static git_remote *g_remote; + +void test_remote_insteadof__initialize(void) +{ + g_repo = NULL; + g_remote = NULL; +} + +void test_remote_insteadof__cleanup(void) +{ + git_repository_free(g_repo); + git_remote_free(g_remote); +} + +void test_remote_insteadof__url_insteadof_not_applicable(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH))); + cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_ORIGIN)); + + cl_assert_equal_s( + git_remote_url(g_remote), + "https://github.com/libgit2/false.git"); +} + +void test_remote_insteadof__url_insteadof_applicable(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH))); + cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_INSTEADOF)); + + cl_assert_equal_s( + git_remote_url(g_remote), + "http://github.com/libgit2/libgit2"); +} + +void test_remote_insteadof__pushurl_insteadof_not_applicable(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH))); + cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_ORIGIN)); + + cl_assert_equal_p(git_remote_pushurl(g_remote), NULL); +} + +void test_remote_insteadof__pushurl_insteadof_applicable(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH))); + cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_INSTEADOF)); + + cl_assert_equal_s( + git_remote_pushurl(g_remote), + "git@github.com:libgit2/libgit2"); +} diff --git a/tests/resources/testrepo2/.gitted/config b/tests/resources/testrepo2/.gitted/config index fc2433caf99..4af067f0401 100644 --- a/tests/resources/testrepo2/.gitted/config +++ b/tests/resources/testrepo2/.gitted/config @@ -8,7 +8,19 @@ [remote "origin"] url = https://github.com/libgit2/false.git fetch = +refs/heads/*:refs/remotes/origin/* +[remote "insteadof-test"] + url = http://example.com/libgit2/libgit2 + pushurl = http://github.com/libgit2/libgit2 + fetch = +refs/heads/*:refs/remotes/test/* [branch "master"] remote = origin merge = refs/heads/master rebase = true +[url "longer-non-prefix-match"] + insteadOf = ttp://example.com/li +[url "shorter-prefix"] + insteadOf = http://example.co +[url "http://github.com"] + insteadOf = http://example.com +[url "git@github.com:"] + pushInsteadOf = http://github.com/ From 771069e1fb9850153e85d4b7c76cbcbf35540cdc Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 21 May 2015 12:46:04 +0200 Subject: [PATCH 16/61] Add CHANGELOG entry for url.*.insteadOf feature. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f426825d3f..8d4b73342d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,10 @@ support for HTTPS connections insead of OpenSSL. `git_off_t` instead of `size_t` for the size of the blob, which allows putting large files into the odb on 32-bit systems. +* The remote's push and pull URLs now honor the url.$URL.insteadOf + configuration. This allows modifying URL prefixes to a custom + value via gitconfig. + ### API additions * The `git_merge_options` gained a `file_flags` member. From 9e88a823f61a3c1b298b0f33d1a260fdc5ee2fa7 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 21 May 2015 13:02:22 +0200 Subject: [PATCH 17/61] remote: test insteadOf for anonymous remotes --- tests/remote/insteadof.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/remote/insteadof.c b/tests/remote/insteadof.c index c9b39f0984e..05d4757cf0c 100644 --- a/tests/remote/insteadof.c +++ b/tests/remote/insteadof.c @@ -58,3 +58,15 @@ void test_remote_insteadof__pushurl_insteadof_applicable(void) git_remote_pushurl(g_remote), "git@github.com:libgit2/libgit2"); } + +void test_remote_insteadof__anonymous_remote(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH))); + cl_git_pass(git_remote_create_anonymous(&g_remote, g_repo, + "http://example.com/libgit2/libgit2")); + + cl_assert_equal_s( + git_remote_url(g_remote), + "http://github.com/libgit2/libgit2"); + cl_assert_equal_p(git_remote_pushurl(g_remote), NULL); +} From be5fda75879f7ed89c7a72adc257872d1ea13803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 31 May 2015 19:21:42 +0200 Subject: [PATCH 18/61] Include git2/transaction.h This was forgotten when the feature was implemented. --- include/git2.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/git2.h b/include/git2.h index a1e78376f43..ac4a63160f8 100644 --- a/include/git2.h +++ b/include/git2.h @@ -57,6 +57,7 @@ #include "git2/submodule.h" #include "git2/tag.h" #include "git2/transport.h" +#include "git2/transaction.h" #include "git2/tree.h" #include "git2/types.h" #include "git2/version.h" From 10940736887e279583e6b14d7e25de5db73768e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 1 Jun 2015 22:15:11 +0200 Subject: [PATCH 19/61] clone: fall back to copying when linking does not work We use heuristics to make a decent guess at when we can save time and space by linking object files during a clone. Unfortunately checking the device id isn't enough, as those would be the same during e.g. a bind-mount, but the OS still does not allow us to link between mounts of the same filesystem. If we fail to perform the links, fall back to copying the contents into a new file as a last attempt. --- src/clone.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/clone.c b/src/clone.c index 8b42ce7061b..070daf94dd2 100644 --- a/src/clone.c +++ b/src/clone.c @@ -532,8 +532,21 @@ static int clone_local_into(git_repository *repo, git_remote *remote, const git_ if (can_link(git_repository_path(src), git_repository_path(repo), link)) flags |= GIT_CPDIR_LINK_FILES; - if ((error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb), - flags, GIT_OBJECT_DIR_MODE)) < 0) + error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb), + flags, GIT_OBJECT_DIR_MODE); + + /* + * can_link() doesn't catch all variations, so if we hit an + * error and did want to link, let's try again without trying + * to link. + */ + if (error < 0 && link) { + flags &= ~GIT_CPDIR_LINK_FILES; + error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb), + flags, GIT_OBJECT_DIR_MODE); + } + + if (error < 0) goto cleanup; git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); From aa57231fca5897c0779d5bcfc5d9183b454792eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 2 Jun 2015 10:25:22 +0200 Subject: [PATCH 20/61] indexer: use lseek to extend the packfile We've been using `p_ftruncate()` to extend the packfile in order to mmap it and write the new data into it. This works well in the general case, but as truncation does not allocate space in the filesystem, it must do so when we write data to it. The only way the OS has to indicate a failure to allocate space is via SIGBUS which means we tried to write outside the file. This will cause everyone to crash as they don't expect to handle this signal. Switch to using `p_lseek()` and `p_write()` to extend the file in a way which tells the filesystem to allocate the space for the missing data. We can then be sure that we have space to write into. --- src/indexer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/indexer.c b/src/indexer.c index e39345c71e5..1890b0063c5 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -478,13 +478,14 @@ static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t static int append_to_pack(git_indexer *idx, const void *data, size_t size) { git_off_t current_size = idx->pack->mwf.size; + int fd = idx->pack->mwf.fd; if (!size) return 0; - /* add the extra space we need at the end */ - if (p_ftruncate(idx->pack->mwf.fd, current_size + size) < 0) { - giterr_set(GITERR_OS, "Failed to increase size of pack file '%s'", idx->pack->pack_name); + if (p_lseek(fd, current_size + size - 1, SEEK_SET) < 0 || + p_write(idx->pack->mwf.fd, data, 1) < 0) { + giterr_set(GITERR_OS, "cannot extend packfile '%s'", idx->pack->pack_name); return -1; } From d71e3b25321fa07349c9f1fbfc1b209449d7c3dd Mon Sep 17 00:00:00 2001 From: Marius Ungureanu Date: Tue, 2 Jun 2015 10:23:54 +0300 Subject: [PATCH 21/61] Change error when running out of ssh agent keys --- src/transports/ssh.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 58f1aeb64ff..5c8545fe14e 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -293,8 +293,14 @@ static int ssh_agent_auth(LIBSSH2_SESSION *session, git_cred_ssh_key *c) { if (rc < 0) goto shutdown; - if (rc == 1) + /* rc is set to 1 whenever the ssh agent ran out of keys to check. + * Set the error code to authentication failure rather than erroring + * out with an untranslatable error code. + */ + if (rc == 1) { + rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED; goto shutdown; + } rc = libssh2_agent_userauth(agent, c->username, curr); From 9f3c18e2ac082454c955761f10f4a0d06390c27f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Latour Date: Tue, 2 Jun 2015 08:36:15 -0700 Subject: [PATCH 22/61] Fixed build warnings on Xcode 6.1 --- src/odb.c | 2 +- src/transports/cred.c | 6 ++++++ tests/core/path.c | 2 +- tests/diff/index.c | 2 +- tests/diff/workdir.c | 2 +- tests/index/addall.c | 5 ++--- tests/stash/apply.c | 8 +++++++- tests/status/worktree.c | 4 ++-- 8 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/odb.c b/src/odb.c index deb9d5c8254..7a718f5d65b 100644 --- a/src/odb.c +++ b/src/odb.c @@ -53,7 +53,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_ int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type) { const char *type_str = git_object_type2string(obj_type); - int len = p_snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len); + int len = p_snprintf(hdr, n, "%s %lld", type_str, obj_len); assert(len > 0 && len <= (int)n); return len+1; } diff --git a/src/transports/cred.c b/src/transports/cred.c index 006cd2c528e..044b2a262f2 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -209,6 +209,12 @@ int git_cred_ssh_key_memory_new( passphrase, GIT_CREDTYPE_SSH_MEMORY); #else + GIT_UNUSED(cred); + GIT_UNUSED(username); + GIT_UNUSED(publickey); + GIT_UNUSED(privatekey); + GIT_UNUSED(passphrase); + giterr_set(GITERR_INVALID, "This version of libgit2 was not built with ssh memory credentials."); return -1; diff --git a/tests/core/path.c b/tests/core/path.c index 7d3e4938fd3..c3e622f0221 100644 --- a/tests/core/path.c +++ b/tests/core/path.c @@ -492,7 +492,7 @@ void test_core_path__13_cannot_prettify_a_non_existing_file(void) { git_buf p = GIT_BUF_INIT; - cl_must_pass(git_path_exists(NON_EXISTING_FILEPATH) == false); + cl_assert_equal_b(git_path_exists(NON_EXISTING_FILEPATH), false); cl_assert_equal_i(GIT_ENOTFOUND, git_path_prettify(&p, NON_EXISTING_FILEPATH, NULL)); cl_assert_equal_i(GIT_ENOTFOUND, git_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL)); diff --git a/tests/diff/index.c b/tests/diff/index.c index a544b83c70c..cf883f12aa4 100644 --- a/tests/diff/index.c +++ b/tests/diff/index.c @@ -168,7 +168,7 @@ static void do_conflicted_diff(diff_expects *exp, unsigned long flags) const char *a_commit = "26a125ee1bf"; /* the current HEAD */ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit); git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_index_entry ancestor = {0}, ours = {0}, theirs = {0}; + git_index_entry ancestor = {{0}}, ours = {{0}}, theirs = {{0}}; git_diff *diff = NULL; git_index *index; diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 460f1b5929b..65005f927fe 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -73,7 +73,7 @@ void test_diff_workdir__to_index_with_conflicts(void) git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff = NULL; git_index *index; - git_index_entry our_entry = {0}, their_entry = {0}; + git_index_entry our_entry = {{0}}, their_entry = {{0}}; diff_expects exp = {0}; g_repo = cl_git_sandbox_init("status"); diff --git a/tests/index/addall.c b/tests/index/addall.c index 211e7622824..f344cc6ae01 100644 --- a/tests/index/addall.c +++ b/tests/index/addall.c @@ -288,7 +288,6 @@ void test_index_addall__repo_lifecycle(void) void test_index_addall__files_in_folders(void) { git_index *index; - git_strarray paths = { NULL, 0 }; addall_create_test_repo(true); @@ -408,7 +407,7 @@ void test_index_addall__adds_conflicts(void) cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/branch")); cl_git_pass(git_annotated_commit_from_ref(&annotated, g_repo, ref)); - cl_git_pass(git_merge(g_repo, &annotated, 1, NULL, NULL)); + cl_git_pass(git_merge(g_repo, (const git_annotated_commit**)&annotated, 1, NULL, NULL)); check_status(g_repo, 0, 1, 2, 0, 0, 0, 0, 1); cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL)); @@ -433,7 +432,7 @@ void test_index_addall__removes_deleted_conflicted_files(void) cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/branch")); cl_git_pass(git_annotated_commit_from_ref(&annotated, g_repo, ref)); - cl_git_pass(git_merge(g_repo, &annotated, 1, NULL, NULL)); + cl_git_pass(git_merge(g_repo, (const git_annotated_commit**)&annotated, 1, NULL, NULL)); check_status(g_repo, 0, 1, 2, 0, 0, 0, 0, 1); cl_git_rmfile("merge-resolve/conflicting.txt"); diff --git a/tests/stash/apply.c b/tests/stash/apply.c index e9b56f8c61a..c242536be4c 100644 --- a/tests/stash/apply.c +++ b/tests/stash/apply.c @@ -254,6 +254,11 @@ int checkout_notify( { struct seen_paths *seen_paths = (struct seen_paths *)payload; + GIT_UNUSED(why); + GIT_UNUSED(baseline); + GIT_UNUSED(target); + GIT_UNUSED(workdir); + if (strcmp(path, "what") == 0) seen_paths->what = 1; else if (strcmp(path, "how") == 0) @@ -318,6 +323,8 @@ int aborting_progress_cb( git_stash_apply_progress_t progress, void *payload) { + GIT_UNUSED(payload); + if (progress == GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED) return -44; @@ -327,7 +334,6 @@ int aborting_progress_cb( void test_stash_apply__progress_cb_can_abort(void) { git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT; - git_stash_apply_progress_t progress = GIT_STASH_APPLY_PROGRESS_NONE; opts.progress_cb = aborting_progress_cb; diff --git a/tests/status/worktree.c b/tests/status/worktree.c index e272c0a108a..3b18ae6c03f 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -649,10 +649,10 @@ void test_status_worktree__conflict_has_no_oid(void) { git_repository *repo = cl_git_sandbox_init("status"); git_index *index; - git_index_entry entry = {0}; + git_index_entry entry = {{0}}; git_status_list *statuslist; const git_status_entry *status; - git_oid zero_id = {0}; + git_oid zero_id = {{0}}; entry.mode = 0100644; entry.path = "modified_file"; From 9334c86f49ae1394d106fcc18784b132070c4cd2 Mon Sep 17 00:00:00 2001 From: "Ariel O. Barria" Date: Wed, 3 Jun 2015 14:54:26 -0500 Subject: [PATCH 23/61] print_usage functions is defined but not used Use the previously created function to display a message when the arguments are not valid. ticket 3095 --- examples/describe.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/describe.c b/examples/describe.c index f7b4b1c200d..4cdf61f75ca 100644 --- a/examples/describe.c +++ b/examples/describe.c @@ -135,6 +135,8 @@ static void parse_options(describe_options *opts, int argc, char **argv) } else if (match_int_arg((int *)&opts->format_options.abbreviated_size, &args, "--abbrev", 0)) { } else if (match_int_arg((int *)&opts->describe_options.max_candidates_tags, &args, "--candidates", 0)) { } else if (match_str_arg(&opts->describe_options.pattern, &args, "--match")) { + } else { + print_usage(); } } From 8da44047050c16b563575cf973c1f33b6149f1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 6 Jun 2015 03:55:28 +0200 Subject: [PATCH 24/61] path: error out if the callback returns an error When the callback returns an error, we should stop immediately. This broke when trying to make sure we pass specific errors up the chain. This broke cancelling out of the loose backend's foreach. --- src/path.c | 6 ++++-- tests/odb/foreach.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/path.c b/src/path.c index 81b4d51df84..8f861e68e63 100644 --- a/src/path.c +++ b/src/path.c @@ -1066,8 +1066,10 @@ int git_path_direach( git_buf_truncate(path, wd_len); /* restore path */ /* Only set our own error if the callback did not set one already */ - if (error != 0 && !giterr_last()) { - giterr_set_after_callback(error); + if (error != 0) { + if (!giterr_last()) + giterr_set_after_callback(error); + break; } } diff --git a/tests/odb/foreach.c b/tests/odb/foreach.c index 75448a2f7f1..12b81b4f148 100644 --- a/tests/odb/foreach.c +++ b/tests/odb/foreach.c @@ -71,15 +71,35 @@ static int foreach_stop_cb(const git_oid *oid, void *data) return (*nobj == 1000) ? -321 : 0; } +static int foreach_stop_first_cb(const git_oid *oid, void *data) +{ + int *nobj = data; + (*nobj)++; + + GIT_UNUSED(oid); + + return -123; +} + void test_odb_foreach__interrupt_foreach(void) { int nobj = 0; + git_oid id; cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); git_repository_odb(&_odb, _repo); cl_assert_equal_i(-321, git_odb_foreach(_odb, foreach_stop_cb, &nobj)); cl_assert(nobj == 1000); + + git_odb_free(_odb); + git_repository_free(_repo); + + cl_git_pass(git_repository_init(&_repo, "onlyloose.git", true)); + git_repository_odb(&_odb, _repo); + + cl_git_pass(git_odb_write(&id, _odb, "", 0, GIT_OBJ_BLOB)); + cl_assert_equal_i(-123, git_odb_foreach(_odb, foreach_stop_first_cb, &nobj)); } void test_odb_foreach__files_in_objects_dir(void) From 2785544fb51ec0ee439510f070f769fe66ccfdc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 7 Jun 2015 10:45:39 +0200 Subject: [PATCH 25/61] remote: some error-handling issues from Coverity --- src/remote.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/remote.c b/src/remote.c index b7acbb9c1a5..63f6d346255 100644 --- a/src/remote.c +++ b/src/remote.c @@ -869,7 +869,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const { int error = -1; size_t i; - git_vector refs, specs, *to_active; + git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT; const git_remote_callbacks *cbs = NULL; assert(remote); @@ -2451,7 +2451,8 @@ char *apply_insteadof(git_config *config, const char *url, int direction) suffix_length = strlen(SUFFIX_PUSH) + 1; } - git_config_iterator_glob_new(&iter, config, regexp); + if (git_config_iterator_glob_new(&iter, config, regexp) < 0) + return NULL; match_length = 0; while (git_config_next(&entry, iter) == 0) { From 20f8edb7a5e438adcd3528ca10d233503e124022 Mon Sep 17 00:00:00 2001 From: Tim Hentenaar Date: Thu, 4 Jun 2015 02:22:10 +0200 Subject: [PATCH 26/61] global: Ensure we free our SSL context. --- src/global.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/global.c b/src/global.c index 9f1a0bf10c7..3f20bfd3195 100644 --- a/src/global.c +++ b/src/global.c @@ -116,6 +116,20 @@ static void init_ssl(void) #endif } +/** + * This function aims to clean-up the SSL context which + * we allocated. + */ +static void uninit_ssl(void) +{ +#ifdef GIT_OPENSSL + if (git__ssl_ctx) { + SSL_CTX_free(git__ssl_ctx); + git__ssl_ctx = NULL; + } +#endif +} + int git_openssl_set_locking(void) { #ifdef GIT_OPENSSL @@ -333,6 +347,7 @@ int git_libgit2_shutdown(void) /* Shut down any subsystems that have global state */ git__shutdown(); + uninit_ssl(); ptr = pthread_getspecific(_tls_key); pthread_setspecific(_tls_key, NULL); @@ -391,6 +406,7 @@ int git_libgit2_shutdown(void) git__shutdown(); git__global_state_cleanup(&__state); + uninit_ssl(); return 0; } From 81be2f467ce679e5e1a4b650ebdfc15ce3a9deb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 9 Jun 2015 16:01:29 +0200 Subject: [PATCH 27/61] ssh: move NULL check to the free function Let `ssh_stream_free()` take a NULL stream, as free functions should, and remove the check from the connection setup. The connection setup would not need the check anyhow, as we always have a stream by the time we reach this code. --- src/transports/ssh.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 5c8545fe14e..83af137f8b9 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -177,11 +177,12 @@ static int ssh_stream_write( static void ssh_stream_free(git_smart_subtransport_stream *stream) { ssh_stream *s = (ssh_stream *)stream; - ssh_subtransport *t = OWNING_SUBTRANSPORT(s); - int ret; + ssh_subtransport *t; - GIT_UNUSED(ret); + if (!stream) + return; + t = OWNING_SUBTRANSPORT(s); t->current_stream = NULL; if (s->channel) { @@ -621,8 +622,7 @@ static int _git_ssh_setup_conn( done: if (error < 0) { - if (*stream) - ssh_stream_free(*stream); + ssh_stream_free(*stream); if (session) libssh2_session_free(session); From 02980bdca1c3398396466adff993746cac34fc08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 9 Jun 2015 16:53:07 +0200 Subject: [PATCH 28/61] Initialize a few variables Coverity complains about the git_rawobj ones because we use a loop in which we keep remembering the old version, and we end up copying our object as the base, so we want to have the data pointer be NULL. --- src/indexer.c | 2 +- src/odb_pack.c | 2 +- src/rebase.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/indexer.c b/src/indexer.c index e39345c71e5..ef2ac3cbafe 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -822,7 +822,7 @@ static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats) progressed = 0; non_null = 0; git_vector_foreach(&idx->deltas, i, delta) { - git_rawobj obj; + git_rawobj obj = {NULL}; if (!delta) continue; diff --git a/src/odb_pack.c b/src/odb_pack.c index 1757cf920d2..735158d9604 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -383,7 +383,7 @@ static int pack_backend__read_internal( git_odb_backend *backend, const git_oid *oid) { struct git_pack_entry e; - git_rawobj raw; + git_rawobj raw = {NULL}; int error; if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 || diff --git a/src/rebase.c b/src/rebase.c index b636e7951df..8da7b4f7f0f 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -512,7 +512,7 @@ static int rebase_ensure_not_dirty( git_tree *head = NULL; git_index *index = NULL; git_diff *diff = NULL; - int error; + int error = 0; if (check_index) { if ((error = git_repository_head_tree(&head, repo)) < 0 || From 2d73075a41628634fa0c5572d760ad3aafffcf82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 10 Jun 2015 10:23:08 +0200 Subject: [PATCH 29/61] cache: add a check for a failed allocation Rather minimal change, but it's the kind of thing we should do. --- src/cache.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cache.c b/src/cache.c index 8dc9cbf9c99..2f3ad156316 100644 --- a/src/cache.c +++ b/src/cache.c @@ -68,6 +68,7 @@ int git_cache_init(git_cache *cache) { memset(cache, 0, sizeof(*cache)); cache->map = git_oidmap_alloc(); + GITERR_CHECK_ALLOC(cache->map); if (git_rwlock_init(&cache->lock)) { giterr_set(GITERR_OS, "Failed to initialize cache rwlock"); return -1; From ca2857d81b5783b03d32ddc9d6f338bfec8d93dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 10 Jun 2015 10:30:08 +0200 Subject: [PATCH 30/61] merge: actually increment the counts, not the pointers `merge_diff_list_count_candidates()` takes pointers to the source and target counts, but when it comes time to increase them, we're increasing the pointer, rather than the value it's pointing to. Dereference the value to increase. --- src/merge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/merge.c b/src/merge.c index 28fca003807..517d317de05 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1143,9 +1143,9 @@ static void merge_diff_list_count_candidates( 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++; + (*src_count)++; else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry)) - tgt_count++; + (*tgt_count)++; } } From 878293f7e1d62b951aaa7dc106ca9c6783112d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 10 Jun 2015 10:44:14 +0200 Subject: [PATCH 31/61] pack: use git_buf when building the index name The way we currently do it depends on the subtlety of strlen vs sizeof and the fact that .pack is one longer than .idx. Let's use a git_buf so we can express the manipulation we want much more clearly. --- src/pack.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/pack.c b/src/pack.c index 105d67510bc..cd652672119 100644 --- a/src/pack.c +++ b/src/pack.c @@ -319,9 +319,9 @@ 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 = 0; - size_t name_len, base_len; + size_t name_len; + git_buf idx_name = GIT_BUF_INIT; if (p->index_version > -1) return 0; @@ -329,22 +329,23 @@ static int pack_index_open(struct git_pack_file *p) 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) + git_buf_grow(&idx_name, name_len); + git_buf_put(&idx_name, p->pack_name, name_len - strlen(".pack")); + git_buf_puts(&idx_name, ".idx"); + if (git_buf_oom(&idx_name)) { + giterr_set_oom(); return -1; - - base_len = name_len - strlen(".pack"); - memcpy(idx_name, p->pack_name, base_len); - memcpy(idx_name + base_len, ".idx", sizeof(".idx")); + } if ((error = git_mutex_lock(&p->lock)) < 0) { - git__free(idx_name); + git_buf_free(&idx_name); return error; } if (p->index_version == -1) - error = pack_index_check(idx_name, p); + error = pack_index_check(idx_name.ptr, p); - git__free(idx_name); + git_buf_free(&idx_name); git_mutex_unlock(&p->lock); From 969d4b703c910a8fd045baafbcd243b4c9825316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 10 Jun 2015 10:59:56 +0200 Subject: [PATCH 32/61] object: correct the expected ID size in prefix lookup We take in a possibly partial ID by taking a length and working off of that to figure out whether to just look up the object or ask the backends for a prefix lookup. Unfortunately we've been checking the size against `GIT_OID_HEXSZ` which is the size of a *string* containing a full ID, whereas we need to check against the size we can have when it's a 20-byte array. Change the checks and comment to use `GIT_OID_RAWSZ` which is the correct size of a git_oid to have when full. --- src/object.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/object.c b/src/object.c index 1073559fd12..a178e0ed38a 100644 --- a/src/object.c +++ b/src/object.c @@ -129,10 +129,10 @@ int git_object_lookup_prefix( if (error < 0) return error; - if (len > GIT_OID_HEXSZ) - len = GIT_OID_HEXSZ; + if (len > GIT_OID_RAWSZ) + len = GIT_OID_RAWSZ; - if (len == GIT_OID_HEXSZ) { + if (len == GIT_OID_RAWSZ) { git_cached_obj *cached = NULL; /* We want to match the full id : we can first look up in the cache, @@ -172,9 +172,9 @@ int git_object_lookup_prefix( memcpy(short_oid.id, id->id, (len + 1) / 2); if (len % 2) short_oid.id[len / 2] &= 0xF0; - memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2); + memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_RAWSZ - len) / 2); - /* If len < GIT_OID_HEXSZ (a strict short oid was given), we have + /* If len < GIT_OID_RAWSZ (a strict short oid was given), we have * 2 options : * - We always search in the cache first. If we find that short oid is * ambiguous, we can stop. But in all the other cases, we must then From 0137aba56863634d81e407152df723d5ad4a97ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 10 Jun 2015 11:08:05 +0200 Subject: [PATCH 33/61] filter: close the descriptor in case of error When we hit an error writing to the next stream from a file, we jump to 'done' which currently skips over closing the file descriptor. Make sure to close the descriptor if it has been set to a valid value. --- src/filter.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/filter.c b/src/filter.c index c88fdd4ee95..3c6a0a9d816 100644 --- a/src/filter.c +++ b/src/filter.c @@ -887,7 +887,7 @@ int git_filter_list_stream_file( git_vector filter_streams = GIT_VECTOR_INIT; git_writestream *stream_start; ssize_t readlen; - int fd, error; + int fd = -1, error; if ((error = stream_list_init( &stream_start, &filter_streams, filters, target)) < 0 || @@ -909,9 +909,10 @@ int git_filter_list_stream_file( else if (readlen < 0) error = readlen; - p_close(fd); done: + if (fd >= 0) + p_close(fd); stream_list_free(&filter_streams); git_buf_free(&abspath); return error; From f284e72946306bdbb6a6670769d29310fa279545 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 10 Jun 2015 12:24:06 -0400 Subject: [PATCH 34/61] gitattributes: let clients use native line endings Use "text=auto" to ensure that we get LFs in the repository, but let clients have their native line endings in their worktree. --- .gitattributes | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index f90540b55b1..176a458f94e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ -*.c eol=lf -*.h eol=lf +* text=auto From 50456801c017e22696b9d756daf3a4a66e6b9bb8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Latour Date: Wed, 10 Jun 2015 10:09:10 -0700 Subject: [PATCH 35/61] Fixed handling of GIT_DELTA_CONFLICTED in git_diff_find_similar() git_diff_find_similar() now ignores git_diff_delta records with a status of GIT_DELTA_CONFLICTED, which fixes a crash due to assert() being hit. --- src/diff_tform.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/diff_tform.c b/src/diff_tform.c index 7d84635734f..03dd9c9f74c 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -676,11 +676,13 @@ static bool is_rename_target( return false; /* only consider ADDED, RENAMED, COPIED, and split MODIFIED as - * targets; maybe include UNTRACKED and IGNORED if requested. + * targets; maybe include UNTRACKED if requested. */ switch (delta->status) { case GIT_DELTA_UNMODIFIED: case GIT_DELTA_DELETED: + case GIT_DELTA_IGNORED: + case GIT_DELTA_CONFLICTED: return false; case GIT_DELTA_MODIFIED: @@ -707,9 +709,6 @@ static bool is_rename_target( return false; break; - case GIT_DELTA_IGNORED: - return false; - default: /* all other status values should be checked */ break; } @@ -735,6 +734,7 @@ static bool is_rename_source( case GIT_DELTA_UNTRACKED: case GIT_DELTA_UNREADABLE: case GIT_DELTA_IGNORED: + case GIT_DELTA_CONFLICTED: return false; case GIT_DELTA_DELETED: From 2eecc2886be68496bb9555e4c93e59e985a7d09f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 10 Jun 2015 14:43:49 -0400 Subject: [PATCH 36/61] Introduce `git_filter_list_contains` `git_filter_list_contains` can be used to query a filter list to determine if a given filter will be run. --- CHANGELOG.md | 3 ++ include/git2/filter.h | 16 ++++++++ src/filter.c | 24 +++++++++++- tests/filter/query.c | 91 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 tests/filter/query.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d4b73342d8..1fff2d499c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -133,6 +133,9 @@ support for HTTPS connections insead of OpenSSL. path. For this, `GIT_CREDTYPE_SSH_MEMORY` and `git_cred_ssh_key_memory_new()` have been added. +* `git_filter_list_contains` will indicate whether a particular + filter will be run in the given filter list. + ### API removals * `git_remote_save()` and `git_remote_clear_refspecs()` have been diff --git a/include/git2/filter.h b/include/git2/filter.h index dc59e63411b..1828903e4f1 100644 --- a/include/git2/filter.h +++ b/include/git2/filter.h @@ -95,6 +95,22 @@ GIT_EXTERN(int) git_filter_list_load( git_filter_mode_t mode, uint32_t flags); +/** + * Query the filter list to see if a given filter (by name) will run. + * The built-in filters "crlf" and "ident" can be queried, otherwise this + * is the name of the filter specified by the filter attribute. + * + * This will return 0 if the given filter is not in the list, or 1 if + * the filter will be applied. + * + * @param filters A loaded git_filter_list (or NULL) + * @param name The name of the filter to query + * @return 1 if the filter is in the list, 0 otherwise + */ +GIT_EXTERN(int) git_filter_list_contains( + git_filter_list *filters, + const char *name); + /** * Apply filter list to a data buffer. * diff --git a/src/filter.c b/src/filter.c index 3c6a0a9d816..e25d37c353c 100644 --- a/src/filter.c +++ b/src/filter.c @@ -27,6 +27,7 @@ struct git_filter_source { }; typedef struct { + const char *filter_name; git_filter *filter; void *payload; } git_filter_entry; @@ -526,7 +527,9 @@ int git_filter_list__load_ext( fe = git_array_alloc(fl->filters); GITERR_CHECK_ALLOC(fe); - fe->filter = fdef->filter; + + fe->filter = fdef->filter; + fe->filter_name = fdef->filter_name; fe->payload = payload; } } @@ -574,6 +577,25 @@ void git_filter_list_free(git_filter_list *fl) git__free(fl); } +int git_filter_list_contains( + git_filter_list *fl, + const char *name) +{ + size_t i; + + assert(name); + + if (!fl) + return 0; + + for (i = 0; i < fl->filters.size; i++) { + if (strcmp(fl->filters.ptr[i].filter_name, name) == 0) + return 1; + } + + return 0; +} + int git_filter_list_push( git_filter_list *fl, git_filter *filter, void *payload) { diff --git a/tests/filter/query.c b/tests/filter/query.c new file mode 100644 index 00000000000..6889d715bb3 --- /dev/null +++ b/tests/filter/query.c @@ -0,0 +1,91 @@ +#include "clar_libgit2.h" +#include "git2/sys/filter.h" +#include "crlf.h" +#include "buffer.h" + +static git_repository *g_repo = NULL; + +void test_filter_query__initialize(void) +{ + g_repo = cl_git_sandbox_init("crlf"); + + cl_git_mkfile("crlf/.gitattributes", + "*.txt text\n" + "*.bin binary\n" + "*.crlf text eol=crlf\n" + "*.lf text eol=lf\n" + "*.binident binary ident\n" + "*.ident text ident\n" + "*.identcrlf ident text eol=crlf\n" + "*.identlf ident text eol=lf\n" + "*.custom custom ident text\n"); +} + +void test_filter_query__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static int filter_for(const char *filename, const char *filter) +{ + git_filter_list *fl; + int filtered; + + cl_git_pass(git_filter_list_load( + &fl, g_repo, NULL, filename, GIT_FILTER_TO_WORKTREE, 0)); + filtered = git_filter_list_contains(fl, filter); + git_filter_list_free(fl); + + return filtered; +} + +void test_filter_query__filters(void) +{ + cl_assert_equal_i(1, filter_for("text.txt", "crlf")); + cl_assert_equal_i(0, filter_for("binary.bin", "crlf")); + + cl_assert_equal_i(1, filter_for("foo.lf", "crlf")); + cl_assert_equal_i(0, filter_for("foo.lf", "ident")); + + cl_assert_equal_i(1, filter_for("id.ident", "crlf")); + cl_assert_equal_i(1, filter_for("id.ident", "ident")); + + cl_assert_equal_i(0, filter_for("id.binident", "crlf")); + cl_assert_equal_i(1, filter_for("id.binident", "ident")); +} + +void test_filter_query__autocrlf_true_implies_crlf(void) +{ + cl_repo_set_bool(g_repo, "core.autocrlf", true); + cl_assert_equal_i(1, filter_for("not_in_gitattributes", "crlf")); + cl_assert_equal_i(1, filter_for("foo.txt", "crlf")); + cl_assert_equal_i(0, filter_for("foo.bin", "crlf")); + cl_assert_equal_i(1, filter_for("foo.lf", "crlf")); + + cl_repo_set_bool(g_repo, "core.autocrlf", false); + cl_assert_equal_i(0, filter_for("not_in_gitattributes", "crlf")); + cl_assert_equal_i(1, filter_for("foo.txt", "crlf")); + cl_assert_equal_i(0, filter_for("foo.bin", "crlf")); + cl_assert_equal_i(1, filter_for("foo.lf", "crlf")); +} + +void test_filter_query__unknown(void) +{ + cl_assert_equal_i(1, filter_for("foo.custom", "crlf")); + cl_assert_equal_i(1, filter_for("foo.custom", "ident")); + cl_assert_equal_i(0, filter_for("foo.custom", "custom")); +} + +void test_filter_query__custom(void) +{ + git_filter custom = { GIT_FILTER_VERSION }; + + cl_git_pass(git_filter_register( + "custom", &custom, 42)); + + cl_assert_equal_i(1, filter_for("foo.custom", "crlf")); + cl_assert_equal_i(1, filter_for("foo.custom", "ident")); + cl_assert_equal_i(1, filter_for("foo.custom", "custom")); + + git_filter_unregister("custom"); +} From 4f0f2b8473e06818e62ba05f3e88deeb2a498589 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 10 Jun 2015 16:36:38 -0400 Subject: [PATCH 37/61] Correct line endings on winhttp.def --- deps/winhttp/winhttp.def | 58 ++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/deps/winhttp/winhttp.def b/deps/winhttp/winhttp.def index 1b2d3f0eb44..eecce59c3ad 100644 --- a/deps/winhttp/winhttp.def +++ b/deps/winhttp/winhttp.def @@ -1,29 +1,29 @@ -LIBRARY WINHTTP -EXPORTS -WinHttpAddRequestHeaders@16 -WinHttpCheckPlatform@0 -WinHttpCloseHandle@4 -WinHttpConnect@16 -WinHttpCrackUrl@16 -WinHttpCreateUrl@16 -WinHttpDetectAutoProxyConfigUrl@8 -WinHttpGetDefaultProxyConfiguration@4 -WinHttpGetIEProxyConfigForCurrentUser@4 -WinHttpGetProxyForUrl@16 -WinHttpOpen@20 -WinHttpOpenRequest@28 -WinHttpQueryAuthSchemes@16 -WinHttpQueryDataAvailable@8 -WinHttpQueryHeaders@24 -WinHttpQueryOption@16 -WinHttpReadData@16 -WinHttpReceiveResponse@8 -WinHttpSendRequest@28 -WinHttpSetCredentials@24 -WinHttpSetDefaultProxyConfiguration@4 -WinHttpSetOption@16 -WinHttpSetStatusCallback@16 -WinHttpSetTimeouts@20 -WinHttpTimeFromSystemTime@8 -WinHttpTimeToSystemTime@8 -WinHttpWriteData@16 +LIBRARY WINHTTP +EXPORTS +WinHttpAddRequestHeaders@16 +WinHttpCheckPlatform@0 +WinHttpCloseHandle@4 +WinHttpConnect@16 +WinHttpCrackUrl@16 +WinHttpCreateUrl@16 +WinHttpDetectAutoProxyConfigUrl@8 +WinHttpGetDefaultProxyConfiguration@4 +WinHttpGetIEProxyConfigForCurrentUser@4 +WinHttpGetProxyForUrl@16 +WinHttpOpen@20 +WinHttpOpenRequest@28 +WinHttpQueryAuthSchemes@16 +WinHttpQueryDataAvailable@8 +WinHttpQueryHeaders@24 +WinHttpQueryOption@16 +WinHttpReadData@16 +WinHttpReceiveResponse@8 +WinHttpSendRequest@28 +WinHttpSetCredentials@24 +WinHttpSetDefaultProxyConfiguration@4 +WinHttpSetOption@16 +WinHttpSetStatusCallback@16 +WinHttpSetTimeouts@20 +WinHttpTimeFromSystemTime@8 +WinHttpTimeToSystemTime@8 +WinHttpWriteData@16 From 65d69fe854ceeb65eeaed7f38a1f4a4771532b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 11 Jun 2015 08:24:58 +0200 Subject: [PATCH 38/61] commit: ignore multiple author fields Some tools create multiple author fields. git is rather lax when parsing them, although fsck does complain about them. This means that they exist in the wild. As it's not too taxing to check for them, and there shouldn't be a noticeable slowdown when dealing with correct commits, add logic to skip over these extra fields when parsing the commit. --- src/commit.c | 10 ++++++++++ tests/commit/parse.c | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/src/commit.c b/src/commit.c index 84f24c6cf45..ce13bdb851e 100644 --- a/src/commit.c +++ b/src/commit.c @@ -309,6 +309,7 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj) const char *buffer_end = buffer_start + git_odb_object_size(odb_obj); git_oid parent_id; size_t header_len; + git_signature dummy_sig; buffer = buffer_start; @@ -337,6 +338,15 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj) if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0) return -1; + /* Some tools create multiple author fields, ignore the extra ones */ + while ((size_t)(buffer_end - buffer) >= strlen("author ") && !git__prefixcmp(buffer, "author ")) { + if (git_signature__parse(&dummy_sig, &buffer, buffer_end, "author ", '\n') < 0) + return -1; + + git__free(dummy_sig.name); + git__free(dummy_sig.email); + } + /* Always parse the committer; we need the commit time */ commit->committer = git__malloc(sizeof(git_signature)); GITERR_CHECK_ALLOC(commit->committer); diff --git a/tests/commit/parse.c b/tests/commit/parse.c index 41e162440e3..fa079f47099 100644 --- a/tests/commit/parse.c +++ b/tests/commit/parse.c @@ -262,6 +262,13 @@ gpgsig -----BEGIN PGP SIGNATURE-----\n\ -----END PGP SIGNATURE-----\n\ \n\ a simple commit which works\n", +/* some tools create two author entries */ +"tree 1810dff58d8a660512d4832e740f692884338ccd\n\ +author Vicent Marti 1273848544 +0200\n\ +author Helpful Coworker 1273848544 +0200\n\ +committer Vicent Marti 1273848544 +0200\n\ +\n\ +a simple commit which works", }; static int parse_commit(git_commit **out, const char *buffer) From 6d0a0acafaf45a034502fc0a8cbdd39851b238c1 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Latour Date: Thu, 11 Jun 2015 23:20:28 -0700 Subject: [PATCH 39/61] Fixed some Secure Transport issues on OS X The read and write callbacks passed to SSLSetIOFuncs() have been rewritten to match the implementation used on opensource.apple.com and other open source projects like VLC. This change also fixes a bug where the read callback could get into an infinite loop when 0 bytes were read. --- src/stransport_stream.c | 56 ++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/stransport_stream.c b/src/stransport_stream.c index 429aa2d5550..d316c376971 100644 --- a/src/stransport_stream.c +++ b/src/stransport_stream.c @@ -110,19 +110,26 @@ int stransport_certificate(git_cert **out, git_stream *stream) return 0; } +/* + * Contrary to typical network IO callbacks, Secure Transport write callback is + * expected to write *all* passed data, not just as much as it can, and any + * other case would be considered a failure. + * + * This behavior is actually not specified in the Apple documentation, but is + * required for things to work correctly (and incidentally, that's also how + * Apple implements it in its projects at opensource.apple.com). + * + * Libgit2 streams happen to already have this very behavior so this is just + * passthrough. + */ static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len) { git_stream *io = (git_stream *) conn; - ssize_t ret; - ret = git_stream_write(io, data, *len, 0); - if (ret < 0) { - *len = 0; - return -1; + if (git_stream_write(io, data, *len, 0) < 0) { + return -36; /* "ioErr" from MacErrors.h which is not available on iOS */ } - *len = ret; - return noErr; } @@ -141,29 +148,38 @@ ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int f return processed; } +/* + * Contrary to typical network IO callbacks, Secure Transport read callback is + * expected to read *exactly* the requested number of bytes, not just as much + * as it can, and any other case would be considered a failure. + * + * This behavior is actually not specified in the Apple documentation, but is + * required for things to work correctly (and incidentally, that's also how + * Apple implements it in its projects at opensource.apple.com). + */ static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len) { git_stream *io = (git_stream *) conn; + OSStatus error = noErr; + size_t off = 0; ssize_t ret; - size_t left, requested; - requested = left = *len; do { - ret = git_stream_read(io, data + (requested - left), left); + ret = git_stream_read(io, data + off, *len - off); if (ret < 0) { - *len = 0; - return -1; + error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */ + break; + } + if (ret == 0) { + error = errSSLClosedGraceful; + break; } - left -= ret; - } while (left); - - *len = requested; - - if (ret == 0) - return errSSLClosedGraceful; + off += ret; + } while (off < *len); - return noErr; + *len = off; + return error; } ssize_t stransport_read(git_stream *stream, void *data, size_t len) From 6042a050f97091615b5925698627b5c0e2aa56cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 12 Jun 2015 12:17:03 +0200 Subject: [PATCH 40/61] travis: fail if we fail the push tests These tests were not being taken into consideration for the failure of the test. They've been failing for a while now, but we hadn't noticed as Travis was reporting the builds successful. --- script/cibuild.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/cibuild.sh b/script/cibuild.sh index bdc3529313c..a11e7984f23 100755 --- a/script/cibuild.sh +++ b/script/cibuild.sh @@ -53,8 +53,8 @@ export GITTEST_REMOTE_SSH_PASSPHRASE="" if [ -e ./libgit2_clar ]; then ./libgit2_clar -sonline::push -sonline::clone::ssh_cert && - ./libgit2_clar -sonline::clone::ssh_with_paths + ./libgit2_clar -sonline::clone::ssh_with_paths || exit $? if [ "$TRAVIS_OS_NAME" = "linux" ]; then - ./libgit2_clar -sonline::clone::cred_callback + ./libgit2_clar -sonline::clone::cred_callback || exit $? fi fi From 0f69b41d2ecec60beafd1cbf1c50fdf3502200a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 12 Jun 2015 12:25:25 +0200 Subject: [PATCH 41/61] push: pass the callbacks during setup as well We need to pass these options in order to have the credentials callback set. --- tests/online/push.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/online/push.c b/tests/online/push.c index 04ad7a2eda5..6cd444320d9 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -319,6 +319,8 @@ void test_online_push__initialize(void) git_vector delete_specs = GIT_VECTOR_INIT; const git_remote_head **heads; size_t heads_len; + git_push_options push_opts = GIT_PUSH_OPTIONS_INIT; + git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT; _repo = cl_git_sandbox_init("push_src"); @@ -370,7 +372,7 @@ void test_online_push__initialize(void) record_callbacks_data_clear(&_record_cbs_data); - cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH, NULL)); + cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH, &_record_cbs)); /* Clean up previously pushed branches. Fails if receive.denyDeletes is * set on the remote. Also, on Git 1.7.0 and newer, you must run @@ -386,14 +388,16 @@ void test_online_push__initialize(void) delete_specs.length, }; - cl_git_pass(git_remote_upload(_remote, &arr, NULL)); + memcpy(&push_opts.callbacks, &_record_cbs, sizeof(git_remote_callbacks)); + cl_git_pass(git_remote_upload(_remote, &arr, &push_opts)); } git_remote_disconnect(_remote); git_vector_free(&delete_specs); /* Now that we've deleted everything, fetch from the remote */ - cl_git_pass(git_remote_fetch(_remote, NULL, NULL, NULL)); + memcpy(&fetch_opts.callbacks, &_record_cbs, sizeof(git_remote_callbacks)); + cl_git_pass(git_remote_fetch(_remote, NULL, &fetch_opts, NULL)); } void test_online_push__cleanup(void) From 352ee171524475e7160130439f889ed9ce7e705a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 12 Jun 2015 12:45:49 +0200 Subject: [PATCH 42/61] clone: set the credentials callback during testing --- tests/online/clone.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/online/clone.c b/tests/online/clone.c index fa0dbd69ca3..e63cf55f1a8 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -428,6 +428,7 @@ void test_online_clone__ssh_with_paths(void) g_options.remote_cb = custom_remote_ssh_with_paths; g_options.fetch_opts.callbacks.transport = git_transport_ssh_with_paths; + g_options.fetch_opts.callbacks.credentials = cred_cb; g_options.fetch_opts.callbacks.payload = &arr; cl_git_fail(git_clone(&g_repo, remote_url, "./foo", &g_options)); From ac7012a81f0bdc472a3d22393291eb7d130705d1 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 25 May 2015 20:36:29 -0400 Subject: [PATCH 43/61] binary diff: test index->workdir binary diffs --- tests/diff/binary.c | 99 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/tests/diff/binary.c b/tests/diff/binary.c index cb574a588ed..136c42c3d97 100644 --- a/tests/diff/binary.c +++ b/tests/diff/binary.c @@ -261,3 +261,102 @@ void test_diff_binary__delta_append(void) git_index_free(index); } + +void test_diff_binary__index_to_workdir(void) +{ + git_index *index; + git_diff *diff; + git_patch *patch; + git_buf actual = GIT_BUF_INIT; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + const char *expected = + "diff --git a/untimely.txt b/untimely.txt\n" \ + "index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \ + "GIT binary patch\n" \ + "delta 32\n" \ + "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ + "\n" \ + "delta 7\n" \ + "Oc%18D`@*{63ljhg(E~C7\n"; + + opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; + opts.id_abbrev = GIT_OID_HEXSZ; + + repo = cl_git_sandbox_init("renames"); + cl_git_pass(git_repository_index(&index, repo)); + + cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n"); + + cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, &opts)); + + cl_git_pass(git_patch_from_diff(&patch, diff, 0)); + cl_git_pass(git_patch_to_buf(&actual, patch)); + + cl_assert_equal_s(expected, actual.ptr); + + cl_git_pass(git_index_add_bypath(index, "untimely.txt")); + cl_git_pass(git_index_write(index)); + + test_patch( + "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13", + NULL, + &opts, + expected); + + git_buf_free(&actual); + git_patch_free(patch); + git_diff_free(diff); + git_index_free(index); +} + +static int print_cb( + const git_diff_delta *delta, + const git_diff_hunk *hunk, + const git_diff_line *line, + void *payload) +{ + git_buf *buf = (git_buf *)payload; + + if (hunk) + git_buf_put(buf, hunk->header, hunk->header_len); + + if (line) + git_buf_put(buf, line->content, line->content_len); + + return git_buf_oom(buf) ? -1 : 0; +} + +void test_diff_binary__print_patch_from_diff(void) +{ + git_index *index; + git_diff *diff; + git_buf actual = GIT_BUF_INIT; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + const char *expected = + "diff --git a/untimely.txt b/untimely.txt\n" \ + "index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \ + "GIT binary patch\n" \ + "delta 32\n" \ + "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ + "\n" \ + "delta 7\n" \ + "Oc%18D`@*{63ljhg(E~C7\n"; + + opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; + opts.id_abbrev = GIT_OID_HEXSZ; + + repo = cl_git_sandbox_init("renames"); + cl_git_pass(git_repository_index(&index, repo)); + + cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n"); + + cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, &opts)); + + cl_git_pass(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, print_cb, &actual)); + + cl_assert_equal_s(expected, actual.ptr); + + git_buf_free(&actual); + git_diff_free(diff); + git_index_free(index); +} From 8147b1aff56c0f36f6afee9b8810fc74776e1f58 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 25 May 2015 20:03:59 -0400 Subject: [PATCH 44/61] diff: introduce binary diff callbacks Introduce a new binary diff callback to provide the actual binary delta contents to callers. Create this data from the diff contents (instead of directly from the ODB) to support binary diffs including the workdir, not just things coming out of the ODB. --- include/git2/diff.h | 55 +++++++ src/blame.c | 4 +- src/diff_file.c | 34 +++-- src/diff_file.h | 6 +- src/diff_patch.c | 298 +++++++++++++++++++++++++++++--------- src/diff_patch.h | 34 +++++ src/diff_print.c | 280 +++++++++++++++++++---------------- src/index.c | 2 +- tests/diff/blob.c | 56 +++---- tests/diff/diff_helpers.c | 13 ++ tests/diff/diff_helpers.h | 6 + tests/diff/index.c | 10 +- tests/diff/notify.c | 4 +- tests/diff/rename.c | 110 +++++++------- tests/diff/submodules.c | 6 +- tests/diff/tree.c | 28 ++-- tests/diff/workdir.c | 112 +++++++------- tests/stress/diff.c | 8 +- 18 files changed, 691 insertions(+), 375 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index c0d42e30ee0..0ecdc1bed87 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -425,6 +425,53 @@ typedef int (*git_diff_file_cb)( float progress, void *payload); +/** + * When producing a binary diff, the binary data returned will be + * either the deflated full ("literal") contents of the file, or + * the deflated binary delta between the two sides (whichever is + * smaller). + */ +typedef enum { + /** There is no binary delta. */ + GIT_DIFF_BINARY_NONE, + + /** The binary data is the literal contents of the file. */ + GIT_DIFF_BINARY_LITERAL, + + /** The binary data is the delta from one side to the other. */ + GIT_DIFF_BINARY_DELTA, +} git_diff_binary_t; + +/** The contents of one of the files in a binary diff. */ +typedef struct { + /** The type of binary data for this file. */ + git_diff_binary_t type; + + /** The binary data, deflated. */ + const char *data; + + /** The length of the binary data. */ + size_t datalen; + + /** The length of the binary data after inflation. */ + size_t inflatedlen; +} git_diff_binary_file; + +/** Structure describing the binary contents of a diff. */ +typedef struct { + git_diff_binary_file old_file; /**< The contents of the old file. */ + git_diff_binary_file new_file; /**< The contents of the new file. */ +} git_diff_binary; + +/** +* When iterating over a diff, callback that will be made for +* binary content within the diff. +*/ +typedef int(*git_diff_binary_cb)( + const git_diff_delta *delta, + const git_diff_binary *binary, + void *payload); + /** * Structure describing a hunk of a diff. */ @@ -897,6 +944,7 @@ GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff); * * @param diff A git_diff generated by one of the above functions. * @param file_cb Callback function to make per file in the diff. + * @param binary_cb Optional callback to make for binary files. * @param hunk_cb Optional callback to make per hunk of text diff. This * callback is called to describe a range of lines in the * diff. It will not be issued for binary files. @@ -909,6 +957,7 @@ GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff); GIT_EXTERN(int) git_diff_foreach( git_diff *diff, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload); @@ -984,6 +1033,7 @@ GIT_EXTERN(int) git_diff_print( * @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 binary_cb Callback for binary files; 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 @@ -996,6 +1046,7 @@ GIT_EXTERN(int) git_diff_blobs( const char *new_as_path, const git_diff_options *options, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload); @@ -1019,6 +1070,7 @@ GIT_EXTERN(int) git_diff_blobs( * @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 binary_cb Callback for binary files; 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 @@ -1032,6 +1084,7 @@ GIT_EXTERN(int) git_diff_blob_to_buffer( const char *buffer_as_path, const git_diff_options *options, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload); @@ -1051,6 +1104,7 @@ GIT_EXTERN(int) git_diff_blob_to_buffer( * @param new_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 binary_cb Callback for binary files; 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 @@ -1065,6 +1119,7 @@ GIT_EXTERN(int) git_diff_buffers( const char *new_as_path, const git_diff_options *options, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload); diff --git a/src/blame.c b/src/blame.c index 4a12cb85b2c..a52f9402b68 100644 --- a/src/blame.c +++ b/src/blame.c @@ -496,8 +496,8 @@ int git_blame_buffer( /* Diff to the reference blob */ git_diff_blob_to_buffer(reference->final_blob, blame->path, - buffer, buffer_len, blame->path, - &diffopts, NULL, buffer_hunk_cb, buffer_line_cb, blame); + buffer, buffer_len, blame->path, &diffopts, + NULL, NULL, buffer_hunk_cb, buffer_line_cb, blame); *out = blame; return 0; diff --git a/src/diff_file.c b/src/diff_file.c index f7061ae8398..cef4bc169c4 100644 --- a/src/diff_file.c +++ b/src/diff_file.c @@ -89,10 +89,9 @@ static int diff_file_content_init_common( int git_diff_file_content__init_from_diff( git_diff_file_content *fc, git_diff *diff, - size_t delta_index, + git_diff_delta *delta, bool use_old) { - git_diff_delta *delta = git_vector_get(&diff->deltas, delta_index); bool has_data = true; memset(fc, 0, sizeof(*fc)); @@ -218,7 +217,9 @@ static int diff_file_content_commit_to_str( return 0; } -static int diff_file_content_load_blob(git_diff_file_content *fc) +static int diff_file_content_load_blob( + git_diff_file_content *fc, + git_diff_options *opts) { int error = 0; git_odb_object *odb_obj = NULL; @@ -236,7 +237,8 @@ static int diff_file_content_load_blob(git_diff_file_content *fc) return error; } - if (diff_file_content_binary_by_size(fc)) + if ((opts->flags & GIT_DIFF_SHOW_BINARY) == 0 && + diff_file_content_binary_by_size(fc)) return 0; if (odb_obj != NULL) { @@ -283,7 +285,9 @@ static int diff_file_content_load_workdir_symlink( } static int diff_file_content_load_workdir_file( - git_diff_file_content *fc, git_buf *path) + git_diff_file_content *fc, + git_buf *path, + git_diff_options *diff_opts) { int error = 0; git_filter_list *fl = NULL; @@ -297,7 +301,8 @@ static int diff_file_content_load_workdir_file( !(fc->file->size = git_futils_filesize(fd))) goto cleanup; - if (diff_file_content_binary_by_size(fc)) + if ((diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0 && + diff_file_content_binary_by_size(fc)) goto cleanup; if ((error = git_filter_list_load( @@ -339,7 +344,9 @@ static int diff_file_content_load_workdir_file( return error; } -static int diff_file_content_load_workdir(git_diff_file_content *fc) +static int diff_file_content_load_workdir( + git_diff_file_content *fc, + git_diff_options *diff_opts) { int error = 0; git_buf path = GIT_BUF_INIT; @@ -357,7 +364,7 @@ static int diff_file_content_load_workdir(git_diff_file_content *fc) 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); + error = diff_file_content_load_workdir_file(fc, &path, diff_opts); /* once data is loaded, update OID if we didn't have it previously */ if (!error && (fc->file->flags & GIT_DIFF_FLAG_VALID_ID) == 0) { @@ -370,20 +377,23 @@ static int diff_file_content_load_workdir(git_diff_file_content *fc) return error; } -int git_diff_file_content__load(git_diff_file_content *fc) +int git_diff_file_content__load( + git_diff_file_content *fc, + git_diff_options *diff_opts) { int error = 0; if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0) return 0; - if ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0) + if ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0 && + (diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0) return 0; if (fc->src == GIT_ITERATOR_TYPE_WORKDIR) - error = diff_file_content_load_workdir(fc); + error = diff_file_content_load_workdir(fc, diff_opts); else - error = diff_file_content_load_blob(fc); + error = diff_file_content_load_blob(fc, diff_opts); if (error) return error; diff --git a/src/diff_file.h b/src/diff_file.h index 4d290ad4300..0d54b6d330f 100644 --- a/src/diff_file.h +++ b/src/diff_file.h @@ -28,7 +28,7 @@ typedef struct { extern int git_diff_file_content__init_from_diff( git_diff_file_content *fc, git_diff *diff, - size_t delta_index, + git_diff_delta *delta, bool use_old); typedef struct { @@ -49,7 +49,9 @@ extern int git_diff_file_content__init_from_src( 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); +extern int git_diff_file_content__load( + git_diff_file_content *fc, + git_diff_options *diff_opts); /* this releases the blob/file-in-memory */ extern void git_diff_file_content__unload(git_diff_file_content *fc); diff --git a/src/diff_patch.c b/src/diff_patch.c index 1c4c0e8b86c..81b93e93e93 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -11,41 +11,13 @@ #include "diff_driver.h" #include "diff_patch.h" #include "diff_xdiff.h" +#include "delta.h" +#include "zstream.h" #include "fileops.h" -/* cached information about a hunk in a diff */ -typedef struct diff_patch_hunk { - git_diff_hunk hunk; - size_t line_start; - size_t line_count; -} diff_patch_hunk; - -struct git_patch { - git_refcount rc; - git_diff *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(git_diff_line) lines; - size_t content_size, context_size, header_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_line_cb, void*); + git_diff_output*, const git_diff_options*, git_diff_file_cb, + git_diff_binary_cb, git_diff_hunk_cb, git_diff_line_cb, void*); static void diff_output_to_patch(git_diff_output *, git_patch *); @@ -67,15 +39,38 @@ static void diff_patch_init_common(git_patch *patch) { diff_patch_update_binary(patch); - if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) - patch->flags |= GIT_DIFF_PATCH_LOADED; /* LOADED but not DIFFABLE */ - patch->flags |= GIT_DIFF_PATCH_INITIALIZED; if (patch->diff) git_diff_addref(patch->diff); } +static int diff_patch_normalize_options( + git_diff_options *out, + const git_diff_options *opts) +{ + if (opts) { + GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); + memcpy(out, opts, sizeof(git_diff_options)); + } else { + git_diff_options default_opts = GIT_DIFF_OPTIONS_INIT; + memcpy(out, &default_opts, sizeof(git_diff_options)); + } + + out->old_prefix = opts && opts->old_prefix ? + git__strdup(opts->old_prefix) : + git__strdup(DIFF_OLD_PREFIX_DEFAULT); + + out->new_prefix = opts && opts->new_prefix ? + git__strdup(opts->new_prefix) : + git__strdup(DIFF_NEW_PREFIX_DEFAULT); + + GITERR_CHECK_ALLOC(out->old_prefix); + GITERR_CHECK_ALLOC(out->new_prefix); + + return 0; +} + static int diff_patch_init_from_diff( git_patch *patch, git_diff *diff, size_t delta_index) { @@ -86,10 +81,12 @@ static int diff_patch_init_from_diff( patch->delta = git_vector_get(&diff->deltas, delta_index); patch->delta_index = delta_index; - if ((error = git_diff_file_content__init_from_diff( - &patch->ofile, diff, delta_index, true)) < 0 || + if ((error = diff_patch_normalize_options( + &patch->diff_opts, &diff->opts)) < 0 || (error = git_diff_file_content__init_from_diff( - &patch->nfile, diff, delta_index, false)) < 0) + &patch->ofile, diff, patch->delta, true)) < 0 || + (error = git_diff_file_content__init_from_diff( + &patch->nfile, diff, patch->delta, false)) < 0) return error; diff_patch_init_common(patch); @@ -116,6 +113,14 @@ static int diff_patch_alloc_from_diff( return error; } +GIT_INLINE(bool) should_skip_binary(git_patch *patch, git_diff_file *file) +{ + if ((patch->diff_opts.flags & GIT_DIFF_SHOW_BINARY) != 0) + return false; + + return (file->flags & GIT_DIFF_FLAG_BINARY) != 0; +} + static int diff_patch_load(git_patch *patch, git_diff_output *output) { int error = 0; @@ -128,7 +133,7 @@ static int diff_patch_load(git_patch *patch, git_diff_output *output) * binary, then there is no need to actually load the data */ if ((patch->ofile.opts_flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0 && - output && !output->hunk_cb && !output->data_cb) + output && !output->binary_cb && !output->hunk_cb && !output->data_cb) return 0; incomplete_data = @@ -141,25 +146,29 @@ static int diff_patch_load(git_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 = git_diff_file_content__load(&patch->ofile)) < 0 || - (patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0) + if ((error = git_diff_file_content__load( + &patch->ofile, &patch->diff_opts)) < 0 || + should_skip_binary(patch, patch->ofile.file)) 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) + if ((error = git_diff_file_content__load( + &patch->nfile, &patch->diff_opts)) < 0 || + should_skip_binary(patch, patch->nfile.file)) 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) + if ((error = git_diff_file_content__load( + &patch->ofile, &patch->diff_opts)) < 0 || + should_skip_binary(patch, patch->ofile.file)) 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) + if ((error = git_diff_file_content__load( + &patch->nfile, &patch->diff_opts)) < 0 || + should_skip_binary(patch, patch->nfile.file)) goto cleanup; } @@ -177,10 +186,14 @@ static int diff_patch_load(git_patch *patch, git_diff_output *output) diff_patch_update_binary(patch); if (!error) { + bool skip_binary = + (patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0 && + (patch->diff_opts.flags & GIT_DIFF_SHOW_BINARY) == 0; + /* 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 && + * at least one side has data and the data actually changed + */ + if (!skip_binary && patch->delta->status != GIT_DELTA_UNMODIFIED && (patch->ofile.map.len || patch->nfile.map.len) && (patch->ofile.map.len != patch->nfile.map.len || @@ -207,6 +220,98 @@ static int diff_patch_invoke_file_callback( "git_patch"); } +static int create_binary( + git_diff_binary_t *out_type, + char **out_data, + size_t *out_datalen, + size_t *out_inflatedlen, + const char *a_data, + size_t a_datalen, + const char *b_data, + size_t b_datalen) +{ + git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT; + unsigned long delta_data_len; + int error; + + /* The git_delta function accepts unsigned long only */ + if (!git__is_ulong(a_datalen) || !git__is_ulong(b_datalen)) + return GIT_EBUFS; + + if ((error = git_zstream_deflatebuf(&deflate, b_data, b_datalen)) < 0) + goto done; + + /* The git_delta function accepts unsigned long only */ + if (!git__is_ulong(deflate.size)) { + error = GIT_EBUFS; + goto done; + } + + if (a_datalen && b_datalen) { + void *delta_data = git_delta( + a_data, (unsigned long)a_datalen, + b_data, (unsigned long)b_datalen, + &delta_data_len, (unsigned long)deflate.size); + + if (delta_data) { + error = git_zstream_deflatebuf( + &delta, delta_data, (size_t)delta_data_len); + + git__free(delta_data); + + if (error < 0) + goto done; + } + } + + if (delta.size && delta.size < deflate.size) { + *out_type = GIT_DIFF_BINARY_DELTA; + *out_datalen = delta.size; + *out_data = git_buf_detach(&delta); + *out_inflatedlen = delta_data_len; + } else { + *out_type = GIT_DIFF_BINARY_LITERAL; + *out_datalen = deflate.size; + *out_data = git_buf_detach(&deflate); + *out_inflatedlen = b_datalen; + } + +done: + git_buf_free(&deflate); + git_buf_free(&delta); + + return error; +} + +static int diff_binary(git_diff_output *output, git_patch *patch) +{ + git_diff_binary binary = { 0 }; + const char *old_data = patch->ofile.map.data; + const char *new_data = patch->nfile.map.data; + size_t old_len = patch->ofile.map.len, + new_len = patch->nfile.map.len; + int error; + + /* Create the old->new delta (as the "new" side of the patch), + * and the new->old delta (as the "old" side) + */ + if ((error = create_binary(&binary.old_file.type, + (char **)&binary.old_file.data, + &binary.old_file.datalen, + &binary.old_file.inflatedlen, + new_data, new_len, old_data, old_len)) < 0 || + (error = create_binary(&binary.new_file.type, + (char **)&binary.new_file.data, + &binary.new_file.datalen, + &binary.new_file.inflatedlen, + old_data, old_len, new_data, new_len)) < 0) + return error; + + return giterr_set_after_callback_function( + output->binary_cb(patch->delta, &binary, output->payload), + "git_patch"); +} + static int diff_patch_generate(git_patch *patch, git_diff_output *output) { int error = 0; @@ -214,8 +319,8 @@ static int diff_patch_generate(git_patch *patch, git_diff_output *output) if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0) return 0; - /* if we are not looking at the hunks and lines, don't do the diff */ - if (!output->hunk_cb && !output->data_cb) + /* if we are not looking at the binary or text data, don't do the diff */ + if (!output->binary_cb && !output->hunk_cb && !output->data_cb) return 0; if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0 && @@ -225,10 +330,16 @@ static int diff_patch_generate(git_patch *patch, git_diff_output *output) if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0) return 0; - if (output->diff_cb != NULL && - (error = output->diff_cb(output, patch)) < 0) - patch->flags |= GIT_DIFF_PATCH_DIFFED; + if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) { + if (output->binary_cb) + error = diff_binary(output, patch); + } + else { + if (output->diff_cb) + error = output->diff_cb(output, patch); + } + patch->flags |= GIT_DIFF_PATCH_DIFFED; return error; } @@ -245,6 +356,9 @@ static void diff_patch_free(git_patch *patch) git_pool_clear(&patch->flattened); + git__free((char *)patch->diff_opts.old_prefix); + git__free((char *)patch->diff_opts.new_prefix); + if (patch->flags & GIT_DIFF_PATCH_ALLOCATED) git__free(patch); } @@ -260,6 +374,7 @@ static int diff_required(git_diff *diff, const char *action) int git_diff_foreach( git_diff *diff, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) @@ -275,7 +390,7 @@ int git_diff_foreach( memset(&xo, 0, sizeof(xo)); memset(&patch, 0, sizeof(patch)); diff_output_init( - &xo.output, &diff->opts, file_cb, hunk_cb, data_cb, payload); + &xo.output, &diff->opts, file_cb, binary_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, &diff->opts); git_vector_foreach(&diff->deltas, idx, patch.delta) { @@ -284,11 +399,15 @@ int git_diff_foreach( if (git_diff_delta__should_skip(&diff->opts, patch.delta)) continue; + if (binary_cb || hunk_cb || data_cb) { + if ((error = diff_patch_init_from_diff(&patch, diff, idx)) != 0 || + (error = diff_patch_load(&patch, &xo.output)) != 0) + return error; + } + if ((error = diff_patch_invoke_file_callback(&patch, &xo.output)) == 0) { - if (hunk_cb || data_cb) { - if ((error = diff_patch_init_from_diff(&patch, diff, idx)) == 0) + if (binary_cb || hunk_cb || data_cb) error = diff_patch_generate(&patch, &xo.output); - } } git_patch_free(&patch); @@ -419,6 +538,7 @@ static int diff_from_sources( git_diff_file_content_src *newsrc, const git_diff_options *opts, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) @@ -429,7 +549,7 @@ static int diff_from_sources( memset(&xo, 0, sizeof(xo)); diff_output_init( - &xo.output, opts, file_cb, hunk_cb, data_cb, payload); + &xo.output, opts, file_cb, binary_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, opts); memset(&pd, 0, sizeof(pd)); @@ -477,6 +597,7 @@ int git_diff_blobs( const char *new_path, const git_diff_options *opts, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) @@ -486,7 +607,7 @@ int git_diff_blobs( git_diff_file_content_src nsrc = GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path); return diff_from_sources( - &osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload); + &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload); } int git_patch_from_blobs( @@ -512,6 +633,7 @@ int git_diff_blob_to_buffer( const char *buf_path, const git_diff_options *opts, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) @@ -521,7 +643,7 @@ int git_diff_blob_to_buffer( git_diff_file_content_src nsrc = GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path); return diff_from_sources( - &osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload); + &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload); } int git_patch_from_blob_and_buffer( @@ -549,6 +671,7 @@ int git_diff_buffers( const char *new_path, const git_diff_options *opts, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) @@ -558,7 +681,7 @@ int git_diff_buffers( git_diff_file_content_src nsrc = GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path); return diff_from_sources( - &osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload); + &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload); } int git_patch_from_buffers( @@ -812,6 +935,7 @@ void git_patch__new_data( int git_patch__invoke_callbacks( git_patch *patch, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload) @@ -822,6 +946,13 @@ int git_patch__invoke_callbacks( if (file_cb) error = file_cb(patch->delta, 0, payload); + if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) { + if (binary_cb) + error = binary_cb(patch->delta, &patch->binary, payload); + + return error; + } + if (!hunk_cb && !line_cb) return error; @@ -855,6 +986,36 @@ static int diff_patch_file_cb( return 0; } +static int diff_patch_binary_cb( + const git_diff_delta *delta, + const git_diff_binary *binary, + void *payload) +{ + git_patch *patch = payload; + + GIT_UNUSED(delta); + + memcpy(&patch->binary, binary, sizeof(git_diff_binary)); + + if (binary->old_file.data) { + patch->binary.old_file.data = git__malloc(binary->old_file.datalen); + GITERR_CHECK_ALLOC(patch->binary.old_file.data); + + memcpy((char *)patch->binary.old_file.data, + binary->old_file.data, binary->old_file.datalen); + } + + if (binary->new_file.data) { + patch->binary.new_file.data = git__malloc(binary->new_file.datalen); + GITERR_CHECK_ALLOC(patch->binary.new_file.data); + + memcpy((char *)patch->binary.new_file.data, + binary->new_file.data, binary->new_file.datalen); + } + + return 0; +} + static int diff_patch_hunk_cb( const git_diff_delta *delta, const git_diff_hunk *hunk_, @@ -921,6 +1082,7 @@ static void diff_output_init( git_diff_output *out, const git_diff_options *opts, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb data_cb, void *payload) @@ -930,6 +1092,7 @@ static void diff_output_init( memset(out, 0, sizeof(*out)); out->file_cb = file_cb; + out->binary_cb = binary_cb; out->hunk_cb = hunk_cb; out->data_cb = data_cb; out->payload = payload; @@ -938,6 +1101,11 @@ static void diff_output_init( static void diff_output_to_patch(git_diff_output *out, git_patch *patch) { diff_output_init( - out, NULL, - diff_patch_file_cb, diff_patch_hunk_cb, diff_patch_line_cb, patch); + out, + NULL, + diff_patch_file_cb, + diff_patch_binary_cb, + diff_patch_hunk_cb, + diff_patch_line_cb, + patch); } diff --git a/src/diff_patch.h b/src/diff_patch.h index df2ba4c3199..f6ce57dddab 100644 --- a/src/diff_patch.h +++ b/src/diff_patch.h @@ -13,6 +13,38 @@ #include "array.h" #include "git2/patch.h" + /* cached information about a hunk in a diff */ +typedef struct diff_patch_hunk { + git_diff_hunk hunk; + size_t line_start; + size_t line_count; +} diff_patch_hunk; + +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), +}; + +struct git_patch { + git_refcount rc; + git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */ + git_diff_options diff_opts; + git_diff_delta *delta; + size_t delta_index; + git_diff_file_content ofile; + git_diff_file_content nfile; + uint32_t flags; + git_diff_binary binary; + git_array_t(diff_patch_hunk) hunks; + git_array_t(git_diff_line) lines; + size_t content_size, context_size, header_size; + git_pool flattened; +}; + extern git_diff *git_patch__diff(git_patch *); extern git_diff_driver *git_patch__driver(git_patch *); @@ -23,6 +55,7 @@ extern void git_patch__new_data(char **, size_t *, git_patch *); extern int git_patch__invoke_callbacks( git_patch *patch, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *payload); @@ -31,6 +64,7 @@ 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_binary_cb binary_cb; git_diff_hunk_cb hunk_cb; git_diff_line_cb data_cb; void *payload; diff --git a/src/diff_print.c b/src/diff_print.c index 43a90b3d8a5..ebae4ea7513 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -22,50 +22,92 @@ typedef struct { uint32_t flags; int oid_strlen; git_diff_line line; + unsigned int + content_loaded : 1, + content_allocated : 1; + git_diff_file_content *ofile; + git_diff_file_content *nfile; } diff_print_info; -static int diff_print_info_init( +static int diff_print_info_init__common( diff_print_info *pi, git_buf *out, - git_diff *diff, + git_repository *repo, git_diff_format_t format, git_diff_line_cb cb, void *payload) { - pi->diff = diff; - pi->format = format; + pi->format = format; pi->print_cb = cb; - pi->payload = payload; - pi->buf = out; - - if (diff) - pi->flags = diff->opts.flags; - else - pi->flags = 0; - - if (diff && diff->opts.id_abbrev != 0) - pi->oid_strlen = diff->opts.id_abbrev; - else 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->payload = payload; + pi->buf = out; + + if (!pi->oid_strlen) { + if (!repo) + pi->oid_strlen = GIT_ABBREV_DEFAULT; + else if (git_repository__cvar(&pi->oid_strlen, 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) + if (pi->oid_strlen > GIT_OID_HEXSZ + 1) pi->oid_strlen = GIT_OID_HEXSZ + 1; memset(&pi->line, 0, sizeof(pi->line)); pi->line.old_lineno = -1; pi->line.new_lineno = -1; - pi->line.num_lines = 1; + pi->line.num_lines = 1; return 0; } +static int diff_print_info_init_fromdiff( + diff_print_info *pi, + git_buf *out, + git_diff *diff, + git_diff_format_t format, + git_diff_line_cb cb, + void *payload) +{ + git_repository *repo = diff ? diff->repo : NULL; + + memset(pi, 0, sizeof(diff_print_info)); + + pi->diff = diff; + + if (diff) { + pi->flags = diff->opts.flags; + pi->oid_strlen = diff->opts.id_abbrev; + } + + return diff_print_info_init__common(pi, out, repo, format, cb, payload); +} + +static int diff_print_info_init_frompatch( + diff_print_info *pi, + git_buf *out, + git_patch *patch, + git_diff_format_t format, + git_diff_line_cb cb, + void *payload) +{ + git_repository *repo = patch && patch->diff ? patch->diff->repo : NULL; + + memset(pi, 0, sizeof(diff_print_info)); + + pi->diff = patch->diff; + + pi->flags = patch->diff_opts.flags; + pi->oid_strlen = patch->diff_opts.id_abbrev; + + pi->content_loaded = 1; + pi->ofile = &patch->ofile; + pi->nfile = &patch->nfile; + + return diff_print_info_init__common(pi, out, repo, format, cb, payload); +} + static char diff_pick_suffix(int mode) { if (S_ISDIR(mode)) @@ -283,66 +325,22 @@ int git_diff_delta__format_file_header( return git_buf_oom(out) ? -1 : 0; } -static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new) +static int format_binary( + diff_print_info *pi, + git_diff_binary_t type, + const char *data, + size_t datalen, + size_t inflatedlen) { - git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT, *out = NULL; - const void *old_data, *new_data; - git_off_t old_data_len, new_data_len; - unsigned long delta_data_len, inflated_len; - const char *out_type = "literal"; - char *scan, *end; - int error; - - old_data = old ? git_blob_rawcontent(old) : NULL; - new_data = new ? git_blob_rawcontent(new) : NULL; - - old_data_len = old ? git_blob_rawsize(old) : 0; - new_data_len = new ? git_blob_rawsize(new) : 0; - - /* The git_delta function accepts unsigned long only */ - if (!git__is_ulong(old_data_len) || !git__is_ulong(new_data_len)) - return GIT_EBUFS; - - out = &deflate; - inflated_len = (unsigned long)new_data_len; - - if ((error = git_zstream_deflatebuf( - out, new_data, (size_t)new_data_len)) < 0) - goto done; - - /* The git_delta function accepts unsigned long only */ - if (!git__is_ulong((git_off_t)deflate.size)) { - error = GIT_EBUFS; - goto done; - } + const char *typename = type == GIT_DIFF_BINARY_DELTA ? + "delta" : "literal"; + const char *scan, *end; + int error = 0; - if (old && new) { - void *delta_data = git_delta( - old_data, (unsigned long)old_data_len, - new_data, (unsigned long)new_data_len, - &delta_data_len, (unsigned long)deflate.size); - - if (delta_data) { - error = git_zstream_deflatebuf( - &delta, delta_data, (size_t)delta_data_len); - - git__free(delta_data); - - if (error < 0) - goto done; - - if (delta.size < deflate.size) { - out = δ - out_type = "delta"; - inflated_len = delta_data_len; - } - } - } - - git_buf_printf(pi->buf, "%s %lu\n", out_type, inflated_len); + git_buf_printf(pi->buf, "%s %lu\n", typename, inflatedlen); pi->line.num_lines++; - for (scan = out->ptr, end = out->ptr + out->size; scan < end; ) { + for (scan = data, end = data + datalen; scan < end; ) { size_t chunk_len = end - scan; if (chunk_len > 52) chunk_len = 52; @@ -355,51 +353,74 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new) git_buf_encode_base85(pi->buf, scan, chunk_len); git_buf_putc(pi->buf, '\n'); - if (git_buf_oom(pi->buf)) { - error = -1; - goto done; - } + if (git_buf_oom(pi->buf)) + return -1; scan += chunk_len; pi->line.num_lines++; } -done: - git_buf_free(&deflate); - git_buf_free(&delta); + return 0; +} - return error; +static int diff_print_load_content( + diff_print_info *pi, + git_diff_delta *delta) +{ + git_diff_file_content *ofile, *nfile; + int error; + + assert(pi->diff); + + ofile = git__calloc(1, sizeof(git_diff_file_content)); + nfile = git__calloc(1, sizeof(git_diff_file_content)); + + GITERR_CHECK_ALLOC(ofile); + GITERR_CHECK_ALLOC(nfile); + + if ((error = git_diff_file_content__init_from_diff( + ofile, pi->diff, delta, true)) < 0 || + (error = git_diff_file_content__init_from_diff( + nfile, pi->diff, delta, true)) < 0) { + + git__free(ofile); + git__free(nfile); + return error; + } + + pi->content_loaded = 1; + pi->content_allocated = 1; + pi->ofile = ofile; + pi->nfile = nfile; + + return 0; } -/* git diff --binary 8d7523f~2 8d7523f~1 */ static int diff_print_patch_file_binary( - diff_print_info *pi, const git_diff_delta *delta, - const char *oldpfx, const char *newpfx) + diff_print_info *pi, git_diff_delta *delta, + const char *old_pfx, const char *new_pfx, + const git_diff_binary *binary) { - git_blob *old = NULL, *new = NULL; - const git_oid *old_id, *new_id; - int error; size_t pre_binary_size; + int error; if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) goto noshow; + if (!pi->content_loaded && + (error = diff_print_load_content(pi, delta)) < 0) + return error; + pre_binary_size = pi->buf->size; git_buf_printf(pi->buf, "GIT binary patch\n"); pi->line.num_lines++; - old_id = (delta->status != GIT_DELTA_ADDED) ? &delta->old_file.id : NULL; - new_id = (delta->status != GIT_DELTA_DELETED) ? &delta->new_file.id : NULL; - - if (old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0) - goto done; - if (new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0) - goto done; - - if ((error = print_binary_hunk(pi, old, new)) < 0 || + if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data, + binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 || (error = git_buf_putc(pi->buf, '\n')) < 0 || - (error = print_binary_hunk(pi, new, old)) < 0) - { + (error = format_binary(pi, binary->old_file.type, binary->old_file.data, + binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) { + if (error == GIT_EBUFS) { giterr_clear(); git_buf_truncate(pi->buf, pre_binary_size); @@ -408,17 +429,12 @@ static int diff_print_patch_file_binary( } pi->line.num_lines++; - -done: - git_blob_free(old); - git_blob_free(new); - return error; noshow: pi->line.num_lines = 1; return diff_delta_format_with_paths( - pi->buf, delta, oldpfx, newpfx, + pi->buf, delta, old_pfx, new_pfx, "Binary files %s%s and %s%s differ\n"); } @@ -432,7 +448,8 @@ static int diff_print_patch_file( const char *newpfx = pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT; - bool binary = !!(delta->flags & GIT_DIFF_FLAG_BINARY); + bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) || + (pi->flags & GIT_DIFF_FORCE_BINARY); bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY); int oid_strlen = binary && show_binary ? GIT_OID_HEXSZ + 1 : pi->oid_strlen; @@ -455,19 +472,29 @@ static int diff_print_patch_file( pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); - if ((error = pi->print_cb(delta, NULL, &pi->line, pi->payload)) != 0) - return error; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); +} - if (!binary) - return 0; +static int diff_print_patch_binary( + const git_diff_delta *delta, + const git_diff_binary *binary, + void *data) +{ + diff_print_info *pi = data; + const char *old_pfx = + pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT; + const char *new_pfx = + pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT; + int error; git_buf_clear(pi->buf); - if ((error = diff_print_patch_file_binary(pi, delta, oldpfx, newpfx)) < 0) + if ((error = diff_print_patch_file_binary( + pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0) return error; - pi->line.origin = GIT_DIFF_LINE_BINARY; - pi->line.content = git_buf_cstr(pi->buf); + pi->line.origin = GIT_DIFF_LINE_BINARY; + pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); return pi->print_cb(delta, NULL, &pi->line, pi->payload); @@ -515,12 +542,14 @@ int git_diff_print( git_buf buf = GIT_BUF_INIT; diff_print_info pi; git_diff_file_cb print_file = NULL; + git_diff_binary_cb print_binary = NULL; git_diff_hunk_cb print_hunk = NULL; git_diff_line_cb print_line = NULL; switch (format) { case GIT_DIFF_FORMAT_PATCH: print_file = diff_print_patch_file; + print_binary = diff_print_patch_binary; print_hunk = diff_print_patch_hunk; print_line = diff_print_patch_line; break; @@ -541,11 +570,10 @@ int git_diff_print( return -1; } - if (!(error = diff_print_info_init( - &pi, &buf, diff, format, print_cb, payload))) - { + if (!(error = diff_print_info_init_fromdiff( + &pi, &buf, diff, format, print_cb, payload))) { error = git_diff_foreach( - diff, print_file, print_hunk, print_line, &pi); + diff, print_file, print_binary, print_hunk, print_line, &pi); if (error) /* make sure error message is set */ giterr_set_after_callback_function(error, "git_diff_print"); @@ -568,13 +596,13 @@ int git_patch_print( assert(patch && print_cb); - if (!(error = diff_print_info_init( - &pi, &temp, git_patch__diff(patch), + if (!(error = diff_print_info_init_frompatch( + &pi, &temp, patch, GIT_DIFF_FORMAT_PATCH, print_cb, payload))) { error = git_patch__invoke_callbacks( - patch, diff_print_patch_file, diff_print_patch_hunk, - diff_print_patch_line, &pi); + patch, diff_print_patch_file, diff_print_patch_binary, + diff_print_patch_hunk, diff_print_patch_line, &pi); if (error) /* make sure error message is set */ giterr_set_after_callback_function(error, "git_patch_print"); diff --git a/src/index.c b/src/index.c index 0422ff1d414..14d8d3684db 100644 --- a/src/index.c +++ b/src/index.c @@ -2727,7 +2727,7 @@ static int index_apply_to_wd_diff(git_index *index, int action, const git_strarr goto cleanup; data.pathspec = &ps; - error = git_diff_foreach(diff, apply_each_file, NULL, NULL, &data); + error = git_diff_foreach(diff, apply_each_file, NULL, NULL, NULL, &data); git_diff_free(diff); if (error) /* make sure error is set if callback stopped iteration */ diff --git a/tests/diff/blob.c b/tests/diff/blob.c index 51bdbab15e7..b0253f6dcb7 100644 --- a/tests/diff/blob.c +++ b/tests/diff/blob.c @@ -17,7 +17,7 @@ static void quick_diff_blob_to_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)); + &opts, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); } void test_diff_blob__initialize(void) @@ -88,7 +88,7 @@ void test_diff_blob__can_compare_text_blobs(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( a, NULL, b, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified(1, 6, 1, 5, 0, &expected); /* same diff but use direct buffers */ @@ -96,27 +96,27 @@ void test_diff_blob__can_compare_text_blobs(void) cl_git_pass(git_diff_buffers( git_blob_rawcontent(a), (size_t)git_blob_rawsize(a), NULL, git_blob_rawcontent(b), (size_t)git_blob_rawsize(b), NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified(1, 6, 1, 5, 0, &expected); /* diff on tests/resources/attr/root_test2 */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( b, NULL, c, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified(1, 15, 3, 9, 3, &expected); /* diff on tests/resources/attr/root_test3 */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( a, NULL, c, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified(1, 13, 0, 12, 1, &expected); memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( c, NULL, d, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified(2, 14, 4, 6, 4, &expected); git_blob_free(a); @@ -206,7 +206,7 @@ void test_diff_blob__can_compare_against_null_blobs(void) cl_git_pass(git_diff_blobs( d, NULL, e, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_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]); @@ -222,7 +222,7 @@ void test_diff_blob__can_compare_against_null_blobs(void) cl_git_pass(git_diff_blobs( d, NULL, e, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_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]); @@ -238,7 +238,7 @@ void test_diff_blob__can_compare_against_null_blobs(void) cl_git_pass(git_diff_blobs( alien, NULL, NULL, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files_binary); @@ -250,7 +250,7 @@ void test_diff_blob__can_compare_against_null_blobs(void) cl_git_pass(git_diff_blobs( NULL, NULL, alien, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files_binary); @@ -358,7 +358,7 @@ void test_diff_blob__can_compare_identical_blobs(void) cl_git_pass(git_diff_blobs( d, NULL, d, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_identical_blobs_comparison(&expected); cl_assert_equal_i(0, expected.files_binary); @@ -366,7 +366,7 @@ void test_diff_blob__can_compare_identical_blobs(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( NULL, NULL, NULL, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_identical_blobs_comparison(&expected); cl_assert_equal_i(0, expected.files_binary); @@ -374,7 +374,7 @@ void test_diff_blob__can_compare_identical_blobs(void) 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)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_identical_blobs_comparison(&expected); cl_assert(expected.files_binary > 0); @@ -441,7 +441,7 @@ void test_diff_blob__can_compare_two_binary_blobs(void) cl_git_pass(git_diff_blobs( alien, NULL, heart, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); @@ -449,7 +449,7 @@ void test_diff_blob__can_compare_two_binary_blobs(void) cl_git_pass(git_diff_blobs( heart, NULL, alien, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); @@ -460,7 +460,7 @@ void test_diff_blob__can_compare_a_binary_blob_and_a_text_blob(void) { cl_git_pass(git_diff_blobs( alien, NULL, d, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); @@ -468,7 +468,7 @@ void test_diff_blob__can_compare_a_binary_blob_and_a_text_blob(void) cl_git_pass(git_diff_blobs( d, NULL, alien, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); } @@ -510,7 +510,7 @@ 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, NULL, d, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(2, expected.hunks); @@ -519,7 +519,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( old_d, NULL, d, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(2, expected.hunks); @@ -528,7 +528,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( old_d, NULL, d, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); cl_assert_equal_i(1, expected.hunks); @@ -542,7 +542,7 @@ void test_diff_blob__checks_options_version_too_low(void) opts.version = 0; cl_git_fail(git_diff_blobs( d, NULL, alien, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); err = giterr_last(); cl_assert_equal_i(GITERR_INVALID, err->klass); } @@ -554,7 +554,7 @@ void test_diff_blob__checks_options_version_too_high(void) opts.version = 1024; cl_git_fail(git_diff_blobs( d, NULL, alien, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); err = giterr_last(); cl_assert_equal_i(GITERR_INVALID, err->klass); } @@ -752,7 +752,7 @@ void test_diff_blob__binary_data_comparisons(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( bin, NULL, nonbin, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_binary_blobs_comparison(&expected); /* @@ -773,7 +773,7 @@ void test_diff_blob__binary_data_comparisons(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( bin, NULL, nonbin, NULL, &opts, - diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified_with_lines(&expected, 4); /* cleanup */ @@ -993,8 +993,8 @@ void test_diff_blob__can_compare_buffer_to_buffer(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_buffers( - a, strlen(a), NULL, b, strlen(b), NULL, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + a, strlen(a), NULL, b, strlen(b), NULL, &opts, + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified(4, 9, 0, 4, 5, &expected); opts.flags ^= GIT_DIFF_REVERSE; @@ -1002,7 +1002,7 @@ void test_diff_blob__can_compare_buffer_to_buffer(void) memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_buffers( - a, strlen(a), NULL, b, strlen(b), NULL, - &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + a, strlen(a), NULL, b, strlen(b), NULL, &opts, + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified(4, 9, 0, 5, 4, &expected); } diff --git a/tests/diff/diff_helpers.c b/tests/diff/diff_helpers.c index 03862d6b46c..c7352571cd0 100644 --- a/tests/diff/diff_helpers.c +++ b/tests/diff/diff_helpers.c @@ -91,6 +91,18 @@ int diff_print_file_cb( return diff_file_cb(delta, progress, payload); } +int diff_binary_cb( + const git_diff_delta *delta, + const git_diff_binary *binary, + void *payload) +{ + GIT_UNUSED(delta); + GIT_UNUSED(binary); + GIT_UNUSED(payload); + + return 0; +} + int diff_hunk_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, @@ -145,6 +157,7 @@ int diff_line_cb( int diff_foreach_via_iterator( git_diff *diff, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *data) diff --git a/tests/diff/diff_helpers.h b/tests/diff/diff_helpers.h index 6bcd1e671d5..4d3cd347440 100644 --- a/tests/diff/diff_helpers.h +++ b/tests/diff/diff_helpers.h @@ -42,6 +42,11 @@ extern int diff_print_file_cb( float progress, void *cb_data); +extern int diff_binary_cb( + const git_diff_delta *delta, + const git_diff_binary *binary, + void *cb_data); + extern int diff_hunk_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, @@ -56,6 +61,7 @@ extern int diff_line_cb( extern int diff_foreach_via_iterator( git_diff *diff, git_diff_file_cb file_cb, + git_diff_binary_cb binary_cb, git_diff_hunk_cb hunk_cb, git_diff_line_cb line_cb, void *data); diff --git a/tests/diff/index.c b/tests/diff/index.c index cf883f12aa4..242fb004620 100644 --- a/tests/diff/index.c +++ b/tests/diff/index.c @@ -35,7 +35,7 @@ void test_diff_index__0(void) cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); /* to generate these values: * - cd to tests/resources/status, @@ -63,7 +63,7 @@ void test_diff_index__0(void) cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); /* to generate these values: * - cd to tests/resources/status, @@ -127,8 +127,8 @@ void test_diff_index__1(void) cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts)); - cl_assert_equal_i( - 1, git_diff_foreach(diff, diff_stop_after_2_files, NULL, NULL, &exp) ); + cl_assert_equal_i(1, git_diff_foreach( + diff, diff_stop_after_2_files, NULL, NULL, NULL, &exp) ); cl_assert_equal_i(2, exp.files); @@ -193,7 +193,7 @@ static void do_conflicted_diff(diff_expects *exp, unsigned long flags) cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, index, &opts)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, exp)); git_diff_free(diff); git_tree_free(a); diff --git a/tests/diff/notify.c b/tests/diff/notify.c index da7390d3f07..6ef4af5731c 100644 --- a/tests/diff/notify.c +++ b/tests/diff/notify.c @@ -59,7 +59,7 @@ static void test_notify( memset(&exp, 0, sizeof(exp)); 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_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(expected_diffed_files_count, exp.files); @@ -222,7 +222,7 @@ void test_diff_notify__notify_cb_can_be_used_as_filtering_function(void) memset(&exp, 0, sizeof(exp)); 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_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(0, exp.files); diff --git a/tests/diff/rename.c b/tests/diff/rename.c index 8a327f0355a..5cfd8e23566 100644 --- a/tests/diff/rename.c +++ b/tests/diff/rename.c @@ -65,7 +65,7 @@ void test_diff_rename__match_oid(void) */ memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNMODIFIED]); @@ -81,7 +81,7 @@ void test_diff_rename__match_oid(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNMODIFIED]); @@ -102,7 +102,7 @@ void test_diff_rename__match_oid(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNMODIFIED]); @@ -124,7 +124,7 @@ void test_diff_rename__match_oid(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNMODIFIED]); @@ -204,7 +204,7 @@ void test_diff_rename__not_exact_match(void) */ memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNMODIFIED]); @@ -222,7 +222,7 @@ void test_diff_rename__not_exact_match(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNMODIFIED]); @@ -243,7 +243,7 @@ void test_diff_rename__not_exact_match(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNMODIFIED]); @@ -266,7 +266,7 @@ void test_diff_rename__not_exact_match(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNMODIFIED]); @@ -297,7 +297,7 @@ void test_diff_rename__not_exact_match(void) */ memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_MODIFIED]); @@ -317,7 +317,7 @@ void test_diff_rename__not_exact_match(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(4, exp.files); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); @@ -344,7 +344,7 @@ void test_diff_rename__not_exact_match(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -369,7 +369,7 @@ void test_diff_rename__not_exact_match(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(4, exp.files); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); @@ -469,7 +469,7 @@ void test_diff_rename__working_directory_changes(void) /* 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)); + diff, diff_file_cb, diff_binary_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]); @@ -482,7 +482,7 @@ void test_diff_rename__working_directory_changes(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(5, exp.files); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]); @@ -508,7 +508,7 @@ void test_diff_rename__working_directory_changes(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(5, exp.files); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]); @@ -527,7 +527,7 @@ void test_diff_rename__working_directory_changes(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -545,7 +545,7 @@ void test_diff_rename__working_directory_changes(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -575,7 +575,7 @@ void test_diff_rename__working_directory_changes(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -673,7 +673,7 @@ void test_diff_rename__file_exchange(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -682,7 +682,7 @@ void test_diff_rename__file_exchange(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -725,7 +725,7 @@ void test_diff_rename__file_exchange_three(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -734,7 +734,7 @@ void test_diff_rename__file_exchange_three(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -776,7 +776,7 @@ void test_diff_rename__file_partial_exchange(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -785,7 +785,7 @@ void test_diff_rename__file_partial_exchange(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -833,7 +833,7 @@ void test_diff_rename__rename_and_copy_from_same_source(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -843,7 +843,7 @@ void test_diff_rename__rename_and_copy_from_same_source(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -885,7 +885,7 @@ void test_diff_rename__from_deleted_to_split(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -896,7 +896,7 @@ void test_diff_rename__from_deleted_to_split(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -998,8 +998,8 @@ void test_diff_rename__rejected_match_can_match_others(void) cl_git_pass(git_diff_find_similar(diff, &findopts)); - cl_git_pass( - git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + cl_git_pass(git_diff_foreach( + diff, test_names_expected, NULL, NULL, NULL, &expect)); git_diff_free(diff); git_tree_free(tree); @@ -1072,8 +1072,8 @@ void test_diff_rename__rejected_match_can_match_others_two(void) cl_git_pass(git_diff_find_similar(diff, &findopts)); - cl_git_pass( - git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + cl_git_pass(git_diff_foreach( + diff, test_names_expected, NULL, NULL, NULL, &expect)); cl_assert(expect.idx > 0); git_diff_free(diff); @@ -1128,8 +1128,8 @@ void test_diff_rename__rejected_match_can_match_others_three(void) cl_git_pass(git_diff_find_similar(diff, &findopts)); - cl_git_pass( - git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + cl_git_pass(git_diff_foreach( + diff, test_names_expected, NULL, NULL, NULL, &expect)); cl_assert(expect.idx == expect.len); @@ -1177,8 +1177,8 @@ void test_diff_rename__can_rename_from_rewrite(void) cl_git_pass(git_diff_find_similar(diff, &findopts)); - cl_git_pass( - git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect)); + cl_git_pass(git_diff_foreach( + diff, test_names_expected, NULL, NULL, NULL, &expect)); cl_assert(expect.idx == expect.len); @@ -1210,7 +1210,7 @@ void test_diff_rename__case_changes_are_split(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -1220,7 +1220,7 @@ void test_diff_rename__case_changes_are_split(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -1252,7 +1252,7 @@ void test_diff_rename__unmodified_can_be_renamed(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -1262,13 +1262,13 @@ void test_diff_rename__unmodified_can_be_renamed(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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)); + diff, diff_file_cb, diff_binary_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]); @@ -1317,7 +1317,7 @@ void test_diff_rename__rewrite_on_single_file(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNTRACKED]); @@ -1352,7 +1352,7 @@ void test_diff_rename__can_find_copy_to_split(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_MODIFIED]); cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNMODIFIED]); @@ -1362,7 +1362,7 @@ void test_diff_rename__can_find_copy_to_split(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_DELETED]); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]); @@ -1401,7 +1401,7 @@ void test_diff_rename__can_delete_unmodified_deltas(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_MODIFIED]); cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNMODIFIED]); @@ -1411,7 +1411,7 @@ void test_diff_rename__can_delete_unmodified_deltas(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_COPIED]); @@ -1451,7 +1451,7 @@ void test_diff_rename__matches_config_behavior(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_find_similar(diff, &opts)); cl_git_pass(git_diff_foreach(diff, - diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_file_cb, diff_binary_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_UNMODIFIED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]); @@ -1465,7 +1465,7 @@ void test_diff_rename__matches_config_behavior(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_find_similar(diff, &opts)); cl_git_pass(git_diff_foreach(diff, - diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_file_cb, diff_binary_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_UNMODIFIED]); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); @@ -1479,7 +1479,7 @@ void test_diff_rename__matches_config_behavior(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_find_similar(diff, &opts)); cl_git_pass(git_diff_foreach(diff, - diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_file_cb, diff_binary_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_UNMODIFIED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); @@ -1492,7 +1492,7 @@ void test_diff_rename__matches_config_behavior(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_find_similar(diff, NULL)); cl_git_pass(git_diff_foreach(diff, - diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_file_cb, diff_binary_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_UNMODIFIED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); @@ -1535,7 +1535,7 @@ void test_diff_rename__can_override_thresholds_when_obeying_config(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_find_similar(diff, &opts)); cl_git_pass(git_diff_foreach(diff, - diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_file_cb, diff_binary_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_UNMODIFIED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); @@ -1549,7 +1549,7 @@ void test_diff_rename__can_override_thresholds_when_obeying_config(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_find_similar(diff, &opts)); cl_git_pass(git_diff_foreach(diff, - diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_file_cb, diff_binary_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_UNMODIFIED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); @@ -1590,7 +1590,7 @@ void test_diff_rename__by_config_doesnt_mess_with_whitespace_settings(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_find_similar(diff, &opts)); cl_git_pass(git_diff_foreach(diff, - diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(5, exp.files); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); @@ -1631,7 +1631,7 @@ static void expect_files_renamed(const char *one, const char *two, uint32_t whit memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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]); @@ -1686,7 +1686,7 @@ static void expect_files_not_renamed(const char *one, const char *two, uint32_t memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNTRACKED]); diff --git a/tests/diff/submodules.c b/tests/diff/submodules.c index 02870ac8649..e2169583bf0 100644 --- a/tests/diff/submodules.c +++ b/tests/diff/submodules.c @@ -465,7 +465,7 @@ void test_diff_submodules__skips_empty_includes_used(void) cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(0, exp.files); git_diff_free(diff); @@ -478,7 +478,7 @@ void test_diff_submodules__skips_empty_includes_used(void) cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_IGNORED]); git_diff_free(diff); @@ -488,7 +488,7 @@ void test_diff_submodules__skips_empty_includes_used(void) cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_UNTRACKED]); git_diff_free(diff); diff --git a/tests/diff/tree.c b/tests/diff/tree.c index 6ab49fdb0b3..6dd17203d10 100644 --- a/tests/diff/tree.c +++ b/tests/diff/tree.c @@ -49,7 +49,7 @@ void test_diff_tree__0(void) cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect)); cl_assert_equal_i(5, expect.files); cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]); @@ -71,7 +71,7 @@ void test_diff_tree__0(void) cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, c, b, &opts)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect)); cl_assert_equal_i(2, expect.files); cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]); @@ -158,7 +158,7 @@ void test_diff_tree__options(void) cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, c, d, &opts)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &actual)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &actual)); expected = &test_expects[i]; cl_assert_equal_i(actual.files, expected->files); @@ -194,7 +194,7 @@ void test_diff_tree__bare(void) cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect)); cl_assert_equal_i(3, expect.files); cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]); @@ -235,7 +235,7 @@ void test_diff_tree__merge(void) git_diff_free(diff2); cl_git_pass(git_diff_foreach( - diff1, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect)); + diff1, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect)); cl_assert_equal_i(6, expect.files); cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]); @@ -332,7 +332,7 @@ void process_tree_to_tree_diffing( cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, NULL, NULL, &expect)); + diff, diff_file_cb, NULL, NULL, NULL, &expect)); } void test_diff_tree__symlink_blob_mode_changed_to_regular_file(void) @@ -441,8 +441,8 @@ void test_diff_tree__issue_1397(void) cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts)); - cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect)); + cl_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect)); cl_assert_equal_i(1, expect.files); cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]); @@ -472,8 +472,8 @@ void test_diff_tree__diff_configs(void) 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_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_binary_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]); @@ -492,8 +492,8 @@ void test_diff_tree__diff_configs(void) 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_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_binary_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]); @@ -513,8 +513,8 @@ void test_diff_tree__diff_configs(void) 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_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_binary_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]); diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 65005f927fe..5d6ebed9546 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -30,10 +30,10 @@ void test_diff_workdir__to_index(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); /* to generate these values: * - cd to tests/resources/status, @@ -94,7 +94,7 @@ void test_diff_workdir__to_index_with_conflicts(void) cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts)); cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(9, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -129,7 +129,7 @@ void test_diff_workdir__to_index_with_assume_unchanged(void) cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(8, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]); @@ -158,7 +158,7 @@ void test_diff_workdir__to_index_with_assume_unchanged(void) cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(6, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]); @@ -205,10 +205,10 @@ void test_diff_workdir__to_tree(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(14, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -241,10 +241,10 @@ void test_diff_workdir__to_tree(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(15, exp.files); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]); @@ -278,10 +278,10 @@ void test_diff_workdir__to_tree(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(16, exp.files); cl_assert_equal_i(5, exp.file_status[GIT_DELTA_ADDED]); @@ -312,7 +312,7 @@ void test_diff_workdir__to_tree(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(16, exp.files); cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]); @@ -359,9 +359,9 @@ void test_diff_workdir__to_index_with_pathspec(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, NULL, NULL, &exp)); + diff, diff_file_cb, NULL, NULL, NULL, &exp)); else - cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(13, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -382,9 +382,9 @@ void test_diff_workdir__to_index_with_pathspec(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, NULL, NULL, &exp)); + diff, diff_file_cb, NULL, NULL, NULL, &exp)); else - cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(1, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -405,9 +405,9 @@ void test_diff_workdir__to_index_with_pathspec(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, NULL, NULL, &exp)); + diff, diff_file_cb, NULL, NULL, NULL, &exp)); else - cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(3, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -428,9 +428,9 @@ void test_diff_workdir__to_index_with_pathspec(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, NULL, NULL, &exp)); + diff, diff_file_cb, NULL, NULL, NULL, &exp)); else - cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp)); + cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(2, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -465,10 +465,10 @@ void test_diff_workdir__filemode_changes(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(0, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]); @@ -488,10 +488,10 @@ void test_diff_workdir__filemode_changes(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_MODIFIED]); @@ -521,7 +521,7 @@ void test_diff_workdir__filemode_changes_with_filemode_false(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(0, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]); @@ -536,8 +536,8 @@ void test_diff_workdir__filemode_changes_with_filemode_false(void) cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL)); memset(&exp, 0, sizeof(exp)); - cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(0, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]); @@ -580,10 +580,10 @@ void test_diff_workdir__head_index_and_workdir_all_differ(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(1, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -601,10 +601,10 @@ void test_diff_workdir__head_index_and_workdir_all_differ(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff_w2i, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_w2i, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff_w2i, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_w2i, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(1, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -624,10 +624,10 @@ void test_diff_workdir__head_index_and_workdir_all_differ(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(1, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -666,10 +666,10 @@ void test_diff_workdir__eof_newline_changes(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(0, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -693,10 +693,10 @@ void test_diff_workdir__eof_newline_changes(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(1, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -720,10 +720,10 @@ void test_diff_workdir__eof_newline_changes(void) if (use_iterator) cl_git_pass(diff_foreach_via_iterator( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); else cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(1, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -913,7 +913,7 @@ void test_diff_workdir__submodules(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); /* so "git diff 873585" returns: * M .gitmodules @@ -991,7 +991,7 @@ void test_diff_workdir__to_null_tree(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(exp.files, exp.file_status[GIT_DELTA_UNTRACKED]); @@ -1080,7 +1080,7 @@ void test_diff_workdir__to_index_issue_1397(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(0, exp.files); cl_assert_equal_i(0, exp.hunks); @@ -1096,7 +1096,7 @@ void test_diff_workdir__to_index_issue_1397(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_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_MODIFIED]); @@ -1133,7 +1133,7 @@ void test_diff_workdir__to_tree_issue_1397(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(0, exp.files); cl_assert_equal_i(0, exp.hunks); @@ -1149,7 +1149,7 @@ void test_diff_workdir__to_tree_issue_1397(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(0, exp.files); cl_assert_equal_i(0, exp.hunks); @@ -1203,7 +1203,7 @@ void test_diff_workdir__untracked_directory_scenarios(void) 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_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(3, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -1223,7 +1223,7 @@ void test_diff_workdir__untracked_directory_scenarios(void) 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_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(4, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -1243,7 +1243,7 @@ void test_diff_workdir__untracked_directory_scenarios(void) 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_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(4, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -1267,7 +1267,7 @@ void test_diff_workdir__untracked_directory_scenarios(void) 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_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(4, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -1290,7 +1290,7 @@ void test_diff_workdir__untracked_directory_scenarios(void) 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_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(4, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -1310,7 +1310,7 @@ void test_diff_workdir__untracked_directory_scenarios(void) 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_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(4, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -1333,7 +1333,7 @@ void test_diff_workdir__untracked_directory_scenarios(void) 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_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(4, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -1354,7 +1354,7 @@ void test_diff_workdir__untracked_directory_scenarios(void) 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_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(4, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -1505,7 +1505,7 @@ void test_diff_workdir__with_stale_index(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(17, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); @@ -1527,7 +1527,7 @@ void test_diff_workdir__with_stale_index(void) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); git_diff_free(diff); @@ -1568,7 +1568,7 @@ static void basic_diff_status(git_diff **out, const git_diff_options *opts) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - *out, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + *out, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp)); cl_assert_equal_i(13, exp.files); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); diff --git a/tests/stress/diff.c b/tests/stress/diff.c index 0dda44d3017..a3ba4fab3ce 100644 --- a/tests/stress/diff.c +++ b/tests/stress/diff.c @@ -37,7 +37,7 @@ static void test_with_many(int expected_new) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, NULL, NULL, &exp)); + diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); cl_assert_equal_i(expected_new + 1, exp.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(expected_new + 2, exp.files); @@ -47,7 +47,7 @@ static void test_with_many(int expected_new) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, NULL, NULL, &exp)); + diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); cl_assert_equal_i(expected_new, exp.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(expected_new + 1, exp.files); @@ -63,7 +63,7 @@ static void test_with_many(int expected_new) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, NULL, NULL, &exp)); + diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); cl_assert_equal_i(expected_new + 1, exp.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(expected_new + 2, exp.files); @@ -73,7 +73,7 @@ static void test_with_many(int expected_new) memset(&exp, 0, sizeof(exp)); cl_git_pass(git_diff_foreach( - diff, diff_file_cb, NULL, NULL, &exp)); + diff, diff_file_cb, NULL, NULL, NULL, &exp)); cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); cl_assert_equal_i(expected_new, exp.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(expected_new + 1, exp.files); From 391281ae34ba35abfdcc7589da3a9b9d67ae1c6f Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 2 Jun 2015 18:26:22 -0400 Subject: [PATCH 45/61] binary diff: test binary blob to blob tests --- tests/diff/binary.c | 130 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/tests/diff/binary.c b/tests/diff/binary.c index 136c42c3d97..7fac1c5186e 100644 --- a/tests/diff/binary.c +++ b/tests/diff/binary.c @@ -360,3 +360,133 @@ void test_diff_binary__print_patch_from_diff(void) git_diff_free(diff); git_index_free(index); } + +struct diff_data { + char *old_path; + git_oid old_id; + git_buf old_binary_base85; + size_t old_binary_inflatedlen; + git_diff_binary_t old_binary_type; + + char *new_path; + git_oid new_id; + git_buf new_binary_base85; + size_t new_binary_inflatedlen; + git_diff_binary_t new_binary_type; +}; + +static int file_cb( + const git_diff_delta *delta, + float progress, + void *payload) +{ + struct diff_data *diff_data = payload; + + if (delta->old_file.path) + diff_data->old_path = git__strdup(delta->old_file.path); + + if (delta->new_file.path) + diff_data->new_path = git__strdup(delta->new_file.path); + + git_oid_cpy(&diff_data->old_id, &delta->old_file.id); + git_oid_cpy(&diff_data->new_id, &delta->new_file.id); + + return 0; +} + +static int binary_cb( + const git_diff_delta *delta, + const git_diff_binary *binary, + void *payload) +{ + struct diff_data *diff_data = payload; + + git_buf_encode_base85(&diff_data->old_binary_base85, + binary->old_file.data, binary->old_file.datalen); + diff_data->old_binary_inflatedlen = binary->old_file.inflatedlen; + diff_data->old_binary_type = binary->old_file.type; + + git_buf_encode_base85(&diff_data->new_binary_base85, + binary->new_file.data, binary->new_file.datalen); + diff_data->new_binary_inflatedlen = binary->new_file.inflatedlen; + diff_data->new_binary_type = binary->new_file.type; + + return 0; +} + +static int hunk_cb( + const git_diff_delta *delta, + const git_diff_hunk *hunk, + void *payload) +{ + cl_fail("did not expect hunk callback"); + return 0; +} + +static int line_cb( + const git_diff_delta *delta, + const git_diff_hunk *hunk, + const git_diff_line *line, + void *payload) +{ + cl_fail("did not expect line callback"); + return 0; +} + +void test_diff_binary__blob_to_blob(void) +{ + git_index *index; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_blob *old_blob, *new_blob; + git_oid old_id, new_id; + struct diff_data diff_data = {0}; + const char *expected = + "diff --git a/untimely.txt b/untimely.txt\n" \ + "index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \ + "GIT binary patch\n" \ + "delta 32\n" \ + "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ + "\n" \ + "delta 7\n" \ + "Oc%18D`@*{63ljhg(E~C7\n"; + + opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; + opts.id_abbrev = GIT_OID_HEXSZ; + + repo = cl_git_sandbox_init("renames"); + cl_git_pass(git_repository_index(&index, repo)); + + cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n"); + cl_git_pass(git_index_add_bypath(index, "untimely.txt")); + cl_git_pass(git_index_write(index)); + + git_oid_fromstr(&old_id, "9a69d960ae94b060f56c2a8702545e2bb1abb935"); + git_oid_fromstr(&new_id, "1111d4f11f4b35bf6759e0fb714fe09731ef0840"); + + cl_git_pass(git_blob_lookup(&old_blob, repo, &old_id)); + cl_git_pass(git_blob_lookup(&new_blob, repo, &new_id)); + + cl_git_pass(git_diff_blobs(old_blob, + "untimely.txt", new_blob, "untimely.txt", &opts, + file_cb, binary_cb, hunk_cb, line_cb, &diff_data)); + + cl_assert_equal_s("untimely.txt", diff_data.old_path); + cl_assert_equal_oid(&old_id, &diff_data.old_id); + cl_assert_equal_i(GIT_DIFF_BINARY_DELTA, diff_data.old_binary_type); + cl_assert_equal_i(7, diff_data.old_binary_inflatedlen); + cl_assert_equal_s("c%18D`@*{63ljhg(E~C7", + diff_data.old_binary_base85.ptr); + + cl_assert_equal_s("untimely.txt", diff_data.new_path); + cl_assert_equal_oid(&new_id, &diff_data.new_id); + cl_assert_equal_i(GIT_DIFF_BINARY_DELTA, diff_data.new_binary_type); + cl_assert_equal_i(32, diff_data.new_binary_inflatedlen); + cl_assert_equal_s("c%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW", + diff_data.new_binary_base85.ptr); + + git_blob_free(old_blob); + git_blob_free(new_blob); + + git__free(diff_data.old_path); + git__free(diff_data.new_path); +} From 3208df37fbddeb3ec467fd5b3b98be77b29ad196 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 2 Jun 2015 18:28:06 -0400 Subject: [PATCH 46/61] patch: include diff options on blob->blob diffs --- src/diff_patch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/diff_patch.c b/src/diff_patch.c index 81b93e93e93..63253c2c5ea 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -469,7 +469,8 @@ static int diff_patch_from_sources( git_diff_file *lfile = &pd->delta.old_file, *rfile = &pd->delta.new_file; git_diff_file_content *ldata = &pd->patch.ofile, *rdata = &pd->patch.nfile; - GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); + if ((error = diff_patch_normalize_options(&pd->patch.diff_opts, opts)) < 0) + return error; if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { void *tmp = lfile; lfile = rfile; rfile = tmp; From 0e522f6e6a338da722c7197da9af9435a03049d0 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 12 Jun 2015 09:28:19 -0400 Subject: [PATCH 47/61] binary diff: document changes in CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fff2d499c9..a8e3e18ac07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,10 @@ support for HTTPS connections insead of OpenSSL. configuration. This allows modifying URL prefixes to a custom value via gitconfig. +* `git_diff_foreach`, `git_diff_blobs`, `git_diff_blob_to_buffer`, + and `git_diff_buffers` now accept a new binary callback of type + `git_diff_binary_cb` that includes the binary diff information. + ### API additions * The `git_merge_options` gained a `file_flags` member. From 95639dbb9bc912b05652468346f206355345e41b Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Fri, 12 Jun 2015 08:50:48 -0700 Subject: [PATCH 48/61] Fix memory leak on windows in diriter. --- src/path.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/path.c b/src/path.c index 02917607a51..0ee3efad79a 100644 --- a/src/path.c +++ b/src/path.c @@ -1231,6 +1231,8 @@ void git_path_diriter_free(git_path_diriter *diriter) if (diriter == NULL) return; + git_buf_free(&diriter->path_utf8); + if (diriter->handle != INVALID_HANDLE_VALUE) { FindClose(diriter->handle); diriter->handle = INVALID_HANDLE_VALUE; From 5c757327faa52a5133d3d950175ca901b4b6d22c Mon Sep 17 00:00:00 2001 From: Damien PROFETA Date: Fri, 12 Jun 2015 18:14:32 +0200 Subject: [PATCH 49/61] Fix visibility of transaction symbol Transaction.c did not include the visibility definition of its symbol (that are in git2/transaction.h) and so was by default hidden. --- src/transaction.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/transaction.c b/src/transaction.c index 7b45b8bf293..e8331891cb7 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -13,6 +13,7 @@ #include "reflog.h" #include "signature.h" +#include "git2/transaction.h" #include "git2/signature.h" #include "git2/sys/refs.h" #include "git2/sys/refdb_backend.h" From 26d5c0b823bfce9106cbfa51c9c020874470f19e Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Fri, 12 Jun 2015 09:28:47 -0700 Subject: [PATCH 50/61] Fix leaks in tests/checkout/icase --- tests/checkout/icase.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/checkout/icase.c b/tests/checkout/icase.c index 510f5424d85..55ab3ab24ba 100644 --- a/tests/checkout/icase.c +++ b/tests/checkout/icase.c @@ -269,7 +269,9 @@ void test_checkout_icase__ignores_unstaged_casechange(void) cl_git_pass(git_checkout_tree(repo, (const git_object *)br2, &checkout_opts)); git_commit_free(orig); + git_commit_free(br2); git_reference_free(orig_ref); + git_reference_free(br2_ref); } void test_checkout_icase__conflicts_with_casechanged_subtrees(void) From b224c3886924d181e2699d799826d3e631d5f90f Mon Sep 17 00:00:00 2001 From: Logan Collins Date: Thu, 7 May 2015 14:21:25 -0700 Subject: [PATCH 51/61] Fix in stransport_stream.c for usage of SecCopyErrorMessageString(), which is unavailable to iOS targets. --- src/stransport_stream.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/stransport_stream.c b/src/stransport_stream.c index d316c376971..34c38b22d41 100644 --- a/src/stransport_stream.c +++ b/src/stransport_stream.c @@ -24,11 +24,16 @@ int stransport_error(OSStatus ret) return 0; } +#if !TARGET_OS_IPHONE message = SecCopyErrorMessageString(ret, NULL); GITERR_CHECK_ALLOC(message); giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8)); CFRelease(message); +#else + giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret); +#endif + return -1; } From 25bd0aaf20463fd9d00d882d39cb1cfec4aa6917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 15 Jun 2015 13:28:08 +0200 Subject: [PATCH 52/61] path: remove unnecessary readdir_r usage Arguably all uses of readdir_r are unnecessary, but in this case especially so, as the directory handle only exists within this function, so we don't race with anybody. --- src/path.c | 7 +++---- src/path.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/path.c b/src/path.c index 0ee3efad79a..aca85f99275 100644 --- a/src/path.c +++ b/src/path.c @@ -889,7 +889,7 @@ void git_path_iconv_clear(git_path_iconv_t *ic) } } -int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen) +int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen) { char *nfd = *in, *nfc; size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv; @@ -1018,8 +1018,7 @@ int git_path_direach( int error = 0; ssize_t wd_len; DIR *dir; - path_dirent_data de_data; - struct dirent *de, *de_buf = (struct dirent *)&de_data; + struct dirent *de; #ifdef GIT_USE_ICONV git_path_iconv_t ic = GIT_PATH_ICONV_INIT; @@ -1045,7 +1044,7 @@ int git_path_direach( (void)git_path_iconv_init_precompose(&ic); #endif - while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) { + while ((de = readdir(dir)) != NULL) { char *de_path = de->d_name; size_t de_len = strlen(de_path); diff --git a/src/path.h b/src/path.h index 14237cb4673..5927a53812c 100644 --- a/src/path.h +++ b/src/path.h @@ -407,7 +407,7 @@ extern void git_path_iconv_clear(git_path_iconv_t *ic); * pointer internal iconv buffer if rewrite happened. The `in` pointer * will be left unchanged if no rewrite was needed. */ -extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen); +extern int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen); #endif /* GIT_USE_ICONV */ From 0f4d9c03676f4226f6af705ae3ad06fcc5a82eb5 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Latour Date: Mon, 15 Jun 2015 09:52:40 -0700 Subject: [PATCH 53/61] Fixed Xcode 6.1 build warnings --- src/diff_patch.c | 2 +- src/diff_print.c | 1 - src/path.c | 4 ++-- src/refs.c | 2 +- tests/core/iconv.c | 6 +++--- tests/diff/binary.c | 24 +++++++++++++++--------- tests/diff/diff_helpers.c | 2 ++ 7 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/diff_patch.c b/src/diff_patch.c index 63253c2c5ea..60648f8d7f2 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -285,7 +285,7 @@ static int create_binary( static int diff_binary(git_diff_output *output, git_patch *patch) { - git_diff_binary binary = { 0 }; + git_diff_binary binary = {{0}}; const char *old_data = patch->ofile.map.data; const char *new_data = patch->nfile.map.data; size_t old_len = patch->ofile.map.len, diff --git a/src/diff_print.c b/src/diff_print.c index ebae4ea7513..c58698368df 100644 --- a/src/diff_print.c +++ b/src/diff_print.c @@ -335,7 +335,6 @@ static int format_binary( const char *typename = type == GIT_DIFF_BINARY_DELTA ? "delta" : "literal"; const char *scan, *end; - int error = 0; git_buf_printf(pi->buf, "%s %lu\n", typename, inflatedlen); pi->line.num_lines++; diff --git a/src/path.c b/src/path.c index aca85f99275..c04b5062241 100644 --- a/src/path.c +++ b/src/path.c @@ -1045,7 +1045,7 @@ int git_path_direach( #endif while ((de = readdir(dir)) != NULL) { - char *de_path = de->d_name; + const char *de_path = de->d_name; size_t de_len = strlen(de_path); if (git_path_is_dot_or_dotdot(de_path)) @@ -1305,7 +1305,7 @@ int git_path_diriter_next(git_path_diriter *diriter) #ifdef GIT_USE_ICONV if ((diriter->flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0 && - (error = git_path_iconv(&diriter->ic, (char **)&filename, &filename_len)) < 0) + (error = git_path_iconv(&diriter->ic, &filename, &filename_len)) < 0) return error; #endif diff --git a/src/refs.c b/src/refs.c index ff5e24067f0..7b538659d9b 100644 --- a/src/refs.c +++ b/src/refs.c @@ -863,7 +863,7 @@ int git_reference__normalize_name( const char *name, unsigned int flags) { - char *current; + const char *current; int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC; unsigned int process_flags; bool normalize = (buf != NULL); diff --git a/tests/core/iconv.c b/tests/core/iconv.c index cb85f458a98..498094bdbb8 100644 --- a/tests/core/iconv.c +++ b/tests/core/iconv.c @@ -24,7 +24,7 @@ void test_core_iconv__cleanup(void) void test_core_iconv__unchanged(void) { #ifdef GIT_USE_ICONV - char *data = "Ascii data", *original = data; + const char *data = "Ascii data", *original = data; size_t datalen = strlen(data); cl_git_pass(git_path_iconv(&ic, &data, &datalen)); @@ -38,7 +38,7 @@ void test_core_iconv__unchanged(void) void test_core_iconv__decomposed_to_precomposed(void) { #ifdef GIT_USE_ICONV - char *data = nfd; + const char *data = nfd; size_t datalen, nfdlen = strlen(nfd); datalen = nfdlen; @@ -64,7 +64,7 @@ void test_core_iconv__decomposed_to_precomposed(void) void test_core_iconv__precomposed_is_unmodified(void) { #ifdef GIT_USE_ICONV - char *data = nfc; + const char *data = nfc; size_t datalen = strlen(nfc); cl_git_pass(git_path_iconv(&ic, &data, &datalen)); diff --git a/tests/diff/binary.c b/tests/diff/binary.c index 7fac1c5186e..424a53e5f09 100644 --- a/tests/diff/binary.c +++ b/tests/diff/binary.c @@ -317,6 +317,8 @@ static int print_cb( { git_buf *buf = (git_buf *)payload; + GIT_UNUSED(delta); + if (hunk) git_buf_put(buf, hunk->header, hunk->header_len); @@ -382,6 +384,8 @@ static int file_cb( { struct diff_data *diff_data = payload; + GIT_UNUSED(progress); + if (delta->old_file.path) diff_data->old_path = git__strdup(delta->old_file.path); @@ -401,6 +405,8 @@ static int binary_cb( { struct diff_data *diff_data = payload; + GIT_UNUSED(delta); + git_buf_encode_base85(&diff_data->old_binary_base85, binary->old_file.data, binary->old_file.datalen); diff_data->old_binary_inflatedlen = binary->old_file.inflatedlen; @@ -419,6 +425,10 @@ static int hunk_cb( const git_diff_hunk *hunk, void *payload) { + GIT_UNUSED(delta); + GIT_UNUSED(hunk); + GIT_UNUSED(payload); + cl_fail("did not expect hunk callback"); return 0; } @@ -429,6 +439,11 @@ static int line_cb( const git_diff_line *line, void *payload) { + GIT_UNUSED(delta); + GIT_UNUSED(hunk); + GIT_UNUSED(line); + GIT_UNUSED(payload); + cl_fail("did not expect line callback"); return 0; } @@ -440,15 +455,6 @@ void test_diff_binary__blob_to_blob(void) git_blob *old_blob, *new_blob; git_oid old_id, new_id; struct diff_data diff_data = {0}; - const char *expected = - "diff --git a/untimely.txt b/untimely.txt\n" \ - "index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \ - "GIT binary patch\n" \ - "delta 32\n" \ - "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ - "\n" \ - "delta 7\n" \ - "Oc%18D`@*{63ljhg(E~C7\n"; opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; opts.id_abbrev = GIT_OID_HEXSZ; diff --git a/tests/diff/diff_helpers.c b/tests/diff/diff_helpers.c index c7352571cd0..c6cdf803fc4 100644 --- a/tests/diff/diff_helpers.c +++ b/tests/diff/diff_helpers.c @@ -164,6 +164,8 @@ int diff_foreach_via_iterator( { size_t d, num_d = git_diff_num_deltas(diff); + GIT_UNUSED(binary_cb); + for (d = 0; d < num_d; ++d) { git_patch *patch; const git_diff_delta *delta; From 77596fcfd7cd14d0998b333af5f2d11ffea84843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 15 Jun 2015 09:51:34 +0200 Subject: [PATCH 54/61] diff: add failing test for racy-git in the index We update the index and then immediately change the contents of the file. This makes the diff think there are no changes, as the timestamp of the file agrees with the cached data. This is however a bug, as the file has obviously changed contents. The test is a bit fragile, as it assumes that the index writing and the following modification of the file happen in the same second, but it's enough to show the issue. --- tests/diff/racy.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/diff/racy.c diff --git a/tests/diff/racy.c b/tests/diff/racy.c new file mode 100644 index 00000000000..a109f8c3b7f --- /dev/null +++ b/tests/diff/racy.c @@ -0,0 +1,39 @@ +#include "clar_libgit2.h" + +#include "buffer.h" + +static git_repository *g_repo; + +void test_diff_racy__initialize(void) +{ + cl_git_pass(git_repository_init(&g_repo, "diff_racy", false)); +} + +void test_diff_racy__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_diff_racy__diff(void) +{ + git_index *index; + git_diff *diff; + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A")); + cl_git_mkfile(path.ptr, "A"); + + /* Put 'A' into the index */ + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_add_bypath(index, "A")); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + + /* Change its contents quickly, so we get the same timestamp */ + cl_git_mkfile(path.ptr, "B"); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL)); + cl_assert_equal_i(1, git_diff_num_deltas(diff)); +} From 316b820b6fed712d4b6fae886ba1e6c44afc6c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 15 Jun 2015 09:55:40 +0200 Subject: [PATCH 55/61] index: zero the size of racily-clean entries If a file entry has the same timestamp as the index itself, it is considered racily-clean, as it may have been modified after the index was written, but during the same second. We take extra steps to check the contents, but this is just one part of avoiding races. For files which do have changes but have not been updated in the index, updating the on-disk index means updating its timestamp, which means we would no longer recognise these entries as racy and we would trust the timestamp to tell us whether they have changed. In order to work around this, git zeroes out the file-size field in entries with the same timestamp as the index in order to force the next diff to check the contents. Do so in libgit2 as well. --- src/index.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/index.c b/src/index.c index 14d8d3684db..a931f048a5c 100644 --- a/src/index.c +++ b/src/index.c @@ -658,11 +658,29 @@ int git_index__changed_relative_to( index->stamp.ino != fs->ino); } +/* + * Force the next diff to take a look at those entries which have the + * same timestamp as the current index. + */ +static void truncate_racily_clean(git_index *index) +{ + size_t i; + git_index_entry *entry; + git_time_t ts = index->stamp.mtime; + + git_vector_foreach(&index->entries, i, entry) { + if (entry->mtime.seconds == ts || ts == 0) + entry->file_size = 0; + } +} + int git_index_write(git_index *index) { git_indexwriter writer = GIT_INDEXWRITER_INIT; int error; + truncate_racily_clean(index); + if ((error = git_indexwriter_init(&writer, index)) == 0) error = git_indexwriter_commit(&writer); From c4e6ab5f23819c35dc0c1a0fd7f50e5a194f0d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 15 Jun 2015 14:32:08 +0200 Subject: [PATCH 56/61] crlf: tick the index forward to work around racy-git behaviour In order to avoid racy-git, we zero out the file size for entries with the same timestamp as the index (or during the initial checkout). This is the case in a couple of crlf tests, as the code is fast enough to do everything in the same second. As we know that we do not perform the modification just after writing out the index, which is what this is designed to work around, tick the mtime of the index file such that it doesn't agree with the files anymore, and we do not zero out these entries. --- src/unix/posix.h | 1 + tests/checkout/crlf.c | 30 ++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/unix/posix.h b/src/unix/posix.h index 8b4f427f774..35bd431b844 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -26,6 +26,7 @@ typedef int GIT_SOCKET; #define p_mkdir(p,m) mkdir(p, m) #define p_fsync(fd) fsync(fd) extern char *p_realpath(const char *, char *); +#define p_utimensat(fd, path, times, flags) utimensat(fd, path, times, flags) #define p_recv(s,b,l,f) recv(s,b,l,f) #define p_send(s,b,l,f) send(s,b,l,f) diff --git a/tests/checkout/crlf.c b/tests/checkout/crlf.c index 381b0401374..e0d94e79fef 100644 --- a/tests/checkout/crlf.c +++ b/tests/checkout/crlf.c @@ -4,6 +4,7 @@ #include "git2/checkout.h" #include "repository.h" +#include "index.h" #include "posix.h" static git_repository *g_repo; @@ -31,6 +32,25 @@ void test_checkout_crlf__detect_crlf_autocrlf_false(void) check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW); } +static void tick_index(git_index *index) +{ + git_time_t ts; + struct timespec times[2]; + + cl_assert(index->on_disk); + cl_assert(git_index_path(index)); + + cl_git_pass(git_index_read(index, true)); + ts = index->stamp.mtime; + + times[0].tv_sec = UTIME_OMIT; /* dont' change the atime */ + times[0].tv_nsec = UTIME_OMIT; /* dont' change the atime */ + times[1].tv_sec = ts + 1; + times[1].tv_nsec = 0; + cl_git_pass(p_utimensat(AT_FDCWD, git_index_path(index), times, 0)); + cl_git_pass(git_index_read(index, true)); +} + void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void) { git_index *index; @@ -40,9 +60,10 @@ void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void) cl_repo_set_bool(g_repo, "core.autocrlf", false); - git_checkout_head(g_repo, &opts); - git_repository_index(&index, g_repo); + tick_index(index); + + git_checkout_head(g_repo, &opts); cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL); cl_assert(entry->file_size == strlen(ALL_LF_TEXT_RAW)); @@ -140,9 +161,10 @@ void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void) cl_repo_set_bool(g_repo, "core.autocrlf", true); - git_checkout_head(g_repo, &opts); - git_repository_index(&index, g_repo); + tick_index(index); + + git_checkout_head(g_repo, &opts); cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL); From e44abe16bd20512c76331e6889f390e35993153a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 16 Jun 2015 08:51:45 +0200 Subject: [PATCH 57/61] tests: tick the index when we count OID calculations These tests want to test that we don't recalculate entries which match the index already. This is however something we force when truncating racily-clean entries. Tick the index forward as we know that we don't perform the modifications which the racily-clean code is trying to avoid. --- tests/checkout/checkout_helpers.c | 20 ++++++++++++++++++++ tests/checkout/checkout_helpers.h | 2 ++ tests/checkout/crlf.c | 19 ------------------- tests/diff/workdir.c | 6 ++++++ tests/status/worktree.c | 6 ++++++ 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/tests/checkout/checkout_helpers.c b/tests/checkout/checkout_helpers.c index 06b4e0682e5..c2e65b88521 100644 --- a/tests/checkout/checkout_helpers.c +++ b/tests/checkout/checkout_helpers.c @@ -2,6 +2,7 @@ #include "checkout_helpers.h" #include "refs.h" #include "fileops.h" +#include "index.h" void assert_on_branch(git_repository *repo, const char *branch) { @@ -128,3 +129,22 @@ int checkout_count_callback( return 0; } + +void tick_index(git_index *index) +{ + git_time_t ts; + struct timespec times[2]; + + cl_assert(index->on_disk); + cl_assert(git_index_path(index)); + + cl_git_pass(git_index_read(index, true)); + ts = index->stamp.mtime; + + times[0].tv_sec = UTIME_OMIT; /* dont' change the atime */ + times[0].tv_nsec = UTIME_OMIT; /* dont' change the atime */ + times[1].tv_sec = ts + 1; + times[1].tv_nsec = 0; + cl_git_pass(p_utimensat(AT_FDCWD, git_index_path(index), times, 0)); + cl_git_pass(git_index_read(index, true)); +} diff --git a/tests/checkout/checkout_helpers.h b/tests/checkout/checkout_helpers.h index 705ee903d15..6058a196c70 100644 --- a/tests/checkout/checkout_helpers.h +++ b/tests/checkout/checkout_helpers.h @@ -27,3 +27,5 @@ extern int checkout_count_callback( const git_diff_file *target, const git_diff_file *workdir, void *payload); + +extern void tick_index(git_index *index); diff --git a/tests/checkout/crlf.c b/tests/checkout/crlf.c index e0d94e79fef..61459b3a44e 100644 --- a/tests/checkout/crlf.c +++ b/tests/checkout/crlf.c @@ -32,25 +32,6 @@ void test_checkout_crlf__detect_crlf_autocrlf_false(void) check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW); } -static void tick_index(git_index *index) -{ - git_time_t ts; - struct timespec times[2]; - - cl_assert(index->on_disk); - cl_assert(git_index_path(index)); - - cl_git_pass(git_index_read(index, true)); - ts = index->stamp.mtime; - - times[0].tv_sec = UTIME_OMIT; /* dont' change the atime */ - times[0].tv_nsec = UTIME_OMIT; /* dont' change the atime */ - times[1].tv_sec = ts + 1; - times[1].tv_nsec = 0; - cl_git_pass(p_utimensat(AT_FDCWD, git_index_path(index), times, 0)); - cl_git_pass(git_index_read(index, true)); -} - void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void) { git_index *index; diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c index 5d6ebed9546..6b72f3286b4 100644 --- a/tests/diff/workdir.c +++ b/tests/diff/workdir.c @@ -2,6 +2,7 @@ #include "diff_helpers.h" #include "repository.h" #include "git2/sys/diff.h" +#include "../checkout/checkout_helpers.h" static git_repository *g_repo = NULL; @@ -1583,6 +1584,7 @@ void test_diff_workdir__can_update_index(void) git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff = NULL; git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT; + git_index *index; g_repo = cl_git_sandbox_init("status"); @@ -1607,6 +1609,10 @@ void test_diff_workdir__can_update_index(void) /* now allow diff to update stat cache */ opts.flags |= GIT_DIFF_UPDATE_INDEX; + /* advance a tick for the index so we don't re-calculate racily-clean entries */ + cl_git_pass(git_repository_index__weakptr(&index, g_repo)); + tick_index(index); + basic_diff_status(&diff, &opts); cl_git_pass(git_diff_get_perfdata(&perf, diff)); diff --git a/tests/status/worktree.c b/tests/status/worktree.c index 3b18ae6c03f..f8d1f7f54bb 100644 --- a/tests/status/worktree.c +++ b/tests/status/worktree.c @@ -6,6 +6,7 @@ #include "util.h" #include "path.h" #include "../diff/diff_helpers.h" +#include "../checkout/checkout_helpers.h" #include "git2/sys/diff.h" /** @@ -956,6 +957,7 @@ void test_status_worktree__update_stat_cache_0(void) git_status_options opts = GIT_STATUS_OPTIONS_INIT; git_status_list *status; git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT; + git_index *index; opts.flags = GIT_STATUS_OPT_DEFAULTS; @@ -967,6 +969,10 @@ void test_status_worktree__update_stat_cache_0(void) git_status_list_free(status); + /* tick the index so we avoid recalculating racily-clean entries */ + cl_git_pass(git_repository_index__weakptr(&index, repo)); + tick_index(index); + opts.flags |= GIT_STATUS_OPT_UPDATE_INDEX; cl_git_pass(git_status_list_new(&status, repo, &opts)); From 47a40d1d4421b0081622c1bf4af4f58ebb497ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 16 Jun 2015 12:39:11 +0200 Subject: [PATCH 58/61] remote: return EINVALIDSPEC when given an empty URL This is what we used to return in the settter and there's tests in bindings which ask for this. There's no particular reason to stop doing so. --- src/remote.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/remote.c b/src/remote.c index 63f6d346255..7c2d9993765 100644 --- a/src/remote.c +++ b/src/remote.c @@ -167,14 +167,18 @@ static int get_check_cert(int *out, git_repository *repo) static int canonicalize_url(git_buf *out, const char *in) { -#ifdef GIT_WIN32 - const char *c; + if (in == NULL || strlen(in) == 0) { + giterr_set(GITERR_INVALID, "cannot set empty URL"); + return GIT_EINVALIDSPEC; + } +#ifdef GIT_WIN32 /* Given a UNC path like \\server\path, we need to convert this * to //server/path for compatibility with core git. */ if (in[0] == '\\' && in[1] == '\\' && (git__isalpha(in[2]) || git__isdigit(in[2]))) { + const char *c; for (c = in; *c; c++) git_buf_putc(out, *c == '\\' ? '/' : *c); From 121c3171e5b4918337910250487691efa8dbbd49 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 16 Jun 2015 15:18:04 -0400 Subject: [PATCH 59/61] Introduce p_utimes and p_futimes Provide functionality to set the time on a filesystem entry, using utimes or futimes on POSIX type systems or SetFileTime on Win32. --- src/unix/posix.h | 4 ++- src/win32/posix.h | 3 ++ src/win32/posix_w32.c | 38 ++++++++++++++++++++++++ src/win32/w32_util.h | 10 +++++++ tests/checkout/checkout_helpers.c | 12 ++++---- tests/core/posix.c | 49 +++++++++++++++++++++++++++++++ 6 files changed, 110 insertions(+), 6 deletions(-) diff --git a/src/unix/posix.h b/src/unix/posix.h index 35bd431b844..425e6bb1b27 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -19,6 +19,9 @@ typedef int GIT_SOCKET; #define p_lstat(p,b) lstat(p,b) #define p_stat(p,b) stat(p, b) +#define p_utimes(f, t) utimes(f, t) +#define p_futimes(f, t) futimes(f, t) + #define p_readlink(a, b, c) readlink(a, b, c) #define p_symlink(o,n) symlink(o, n) #define p_link(o,n) link(o, n) @@ -26,7 +29,6 @@ typedef int GIT_SOCKET; #define p_mkdir(p,m) mkdir(p, m) #define p_fsync(fd) fsync(fd) extern char *p_realpath(const char *, char *); -#define p_utimensat(fd, path, times, flags) utimensat(fd, path, times, flags) #define p_recv(s,b,l,f) recv(s,b,l,f) #define p_send(s,b,l,f) send(s,b,l,f) diff --git a/src/win32/posix.h b/src/win32/posix.h index bf35c812536..ac98fd864ed 100644 --- a/src/win32/posix.h +++ b/src/win32/posix.h @@ -20,6 +20,9 @@ typedef SOCKET GIT_SOCKET; extern int p_lstat(const char *file_name, struct stat *buf); extern int p_stat(const char* path, struct stat* buf); +extern int p_utimes(const char *filename, const struct timeval times[2]); +extern int p_futimes(int fd, const struct timeval times[2]); + extern int p_readlink(const char *path, char *buf, size_t bufsiz); extern int p_symlink(const char *old, const char *new); extern int p_link(const char *old, const char *new); diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 332ea233c6b..504562b0e3e 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -201,6 +201,44 @@ int p_lstat_posixly(const char *filename, struct stat *buf) return do_lstat(filename, buf, true); } +int p_utimes(const char *filename, const struct timeval times[2]) +{ + int fd, error; + + if ((fd = p_open(filename, O_RDWR)) < 0) + return fd; + + error = p_futimes(fd, times); + + close(fd); + return error; +} + +int p_futimes(int fd, const struct timeval times[2]) +{ + HANDLE handle; + FILETIME atime = {0}, mtime = {0}; + + if (times == NULL) { + SYSTEMTIME st; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &atime); + SystemTimeToFileTime(&st, &mtime); + } else { + git_win32__timeval_to_filetime(&atime, times[0]); + git_win32__timeval_to_filetime(&mtime, times[1]); + } + + if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE) + return -1; + + if (SetFileTime(handle, NULL, &atime, &mtime) == 0) + return -1; + + return 0; +} + int p_readlink(const char *path, char *buf, size_t bufsiz) { git_win32_path path_w, target_w; diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h index 8cb0f5b9436..377d651a8d4 100644 --- a/src/win32/w32_util.h +++ b/src/win32/w32_util.h @@ -79,6 +79,16 @@ GIT_INLINE(time_t) git_win32__filetime_to_time_t(const FILETIME *ft) return (time_t)winTime; } +GIT_INLINE(void) git_win32__timeval_to_filetime( + FILETIME *ft, const struct timeval tv) +{ + long long ticks = (tv.tv_sec * 10000000LL) + + (tv.tv_usec * 10LL) + 116444736000000000LL; + + ft->dwHighDateTime = ((ticks >> 32) & 0xffffffffLL); + ft->dwLowDateTime = (ticks & 0xffffffffLL); +} + GIT_INLINE(int) git_win32__file_attribute_to_stat( struct stat *st, const WIN32_FILE_ATTRIBUTE_DATA *attrdata, diff --git a/tests/checkout/checkout_helpers.c b/tests/checkout/checkout_helpers.c index c2e65b88521..f6e36d39b45 100644 --- a/tests/checkout/checkout_helpers.c +++ b/tests/checkout/checkout_helpers.c @@ -132,8 +132,9 @@ int checkout_count_callback( void tick_index(git_index *index) { + int index_fd; git_time_t ts; - struct timespec times[2]; + struct timeval times[2]; cl_assert(index->on_disk); cl_assert(git_index_path(index)); @@ -141,10 +142,11 @@ void tick_index(git_index *index) cl_git_pass(git_index_read(index, true)); ts = index->stamp.mtime; - times[0].tv_sec = UTIME_OMIT; /* dont' change the atime */ - times[0].tv_nsec = UTIME_OMIT; /* dont' change the atime */ + times[0].tv_sec = ts; + times[0].tv_usec = 0; times[1].tv_sec = ts + 1; - times[1].tv_nsec = 0; - cl_git_pass(p_utimensat(AT_FDCWD, git_index_path(index), times, 0)); + times[1].tv_usec = 0; + + cl_git_pass(p_utimes(git_index_path(index), times)); cl_git_pass(git_index_read(index, true)); } diff --git a/tests/core/posix.c b/tests/core/posix.c index 1cef937cdb0..5a9e24899f9 100644 --- a/tests/core/posix.c +++ b/tests/core/posix.c @@ -97,3 +97,52 @@ void test_core_posix__inet_pton(void) cl_git_fail(p_inet_pton(5, "315.124", NULL)); /* AF_CHAOS */ cl_assert_equal_i(EAFNOSUPPORT, errno); } + +void test_core_posix__utimes(void) +{ + struct timeval times[2]; + struct stat st; + time_t curtime; + int fd; + + /* test p_utimes */ + times[0].tv_sec = 1234567890; + times[0].tv_usec = 0; + times[1].tv_sec = 1234567890; + times[1].tv_usec = 0; + + cl_git_mkfile("foo", "Dummy file."); + cl_must_pass(p_utimes("foo", times)); + + p_stat("foo", &st); + cl_assert_equal_i(1234567890, st.st_atime); + cl_assert_equal_i(1234567890, st.st_mtime); + + + /* test p_futimes */ + times[0].tv_sec = 1414141414; + times[0].tv_usec = 0; + times[1].tv_sec = 1414141414; + times[1].tv_usec = 0; + + cl_must_pass(fd = p_open("foo", O_RDWR)); + cl_must_pass(p_futimes(fd, times)); + p_close(fd); + + p_stat("foo", &st); + cl_assert_equal_i(1414141414, st.st_atime); + cl_assert_equal_i(1414141414, st.st_mtime); + + + /* test p_utimes with current time, assume that + * it takes < 5 seconds to get the time...! + */ + cl_must_pass(p_utimes("foo", NULL)); + + curtime = time(NULL); + p_stat("foo", &st); + cl_assert((st.st_atime - curtime) < 5); + cl_assert((st.st_mtime - curtime) < 5); + + p_unlink("foo"); +} From fef5344c50e33d01c08bec1687cb8a14a2a5efa2 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 16 Jun 2015 16:34:25 -0400 Subject: [PATCH 60/61] merge::workdir::dirty: tick idx to defeat racy-git --- tests/merge/workdir/dirty.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/merge/workdir/dirty.c b/tests/merge/workdir/dirty.c index b66225d1f33..30c404b701e 100644 --- a/tests/merge/workdir/dirty.c +++ b/tests/merge/workdir/dirty.c @@ -2,6 +2,7 @@ #include "git2/merge.h" #include "buffer.h" #include "merge.h" +#include "index.h" #include "../merge_helpers.h" #include "posix.h" @@ -231,9 +232,20 @@ static int merge_differently_filtered_files(char *files[]) cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJ_COMMIT)); cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL)); + /* Emulate checkout with a broken or misconfigured filter: modify some + * files on-disk and then update the index with the updated file size + * and time, as if some filter applied them. These files should not be + * treated as dirty since we created them. + * + * (Make sure to update the index stamp to defeat racy-git protections + * trying to sanity check the files in the index; those would rehash the + * files, showing them as dirty, the exact mechanism we're trying to avoid.) + */ + write_files(files); hack_index(files); + repo_index->stamp.mtime = time(NULL) + 1; cl_git_pass(git_index_write(repo_index)); error = merge_branch(); From 892abf93157ea576fc3f2ccac118045a6a47247c Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Tue, 16 Jun 2015 17:23:12 -0400 Subject: [PATCH 61/61] checkout: allow workdir to contain checkout target When checking out some file 'foo' that has been modified in the working directory, allow the checkout to proceed (do not conflict) if 'foo' is identical to the target of the checkout. --- src/checkout.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index cc73e483fc2..fd2b19a951b 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -150,6 +150,15 @@ static int checkout_notify( } } +GIT_INLINE(bool) is_workdir_base_or_new( + const git_oid *workdir_id, + const git_diff_file *baseitem, + const git_diff_file *newitem) +{ + return (git_oid__cmp(&baseitem->id, workdir_id) == 0 || + git_oid__cmp(&newitem->id, workdir_id) == 0); +} + static bool checkout_is_workdir_modified( checkout_data *data, const git_diff_file *baseitem, @@ -193,8 +202,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->id, &ie->id) != 0 && - git_oid_cmp(&newitem->id, &ie->id) != 0); + return !is_workdir_base_or_new(&ie->id, baseitem, newitem); } /* depending on where base is coming from, we may or may not know @@ -206,7 +214,10 @@ static bool checkout_is_workdir_modified( if (git_diff__oid_for_entry(&oid, data->diff, wditem, NULL) < 0) return false; - return (git_oid__cmp(&baseitem->id, &oid) != 0); + /* Allow the checkout if the workdir is not modified *or* if the checkout + * target's contents are already in the working directory. + */ + return !is_workdir_base_or_new(&oid, baseitem, newitem); } #define CHECKOUT_ACTION_IF(FLAG,YES,NO) \