From 82374d9825040914a3cfd8ceeaf60f76b93fe638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 8 Nov 2014 20:00:17 +0100 Subject: [PATCH 1/4] branch: add getter for the upstream remote name This gets the value from branch..remote. --- include/git2/branch.h | 11 +++++++++++ src/branch.c | 23 +++++++++++++++++++++++ tests/refs/branches/upstream.c | 9 +++++++++ 3 files changed, 43 insertions(+) diff --git a/include/git2/branch.h b/include/git2/branch.h index f28410000..888781037 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -260,6 +260,17 @@ GIT_EXTERN(int) git_branch_remote_name( git_repository *repo, const char *canonical_branch_name); + +/** + * Retrieve the name fo the upstream remote of a local branch + * + * @param buf the buffer into which to write the name + * @param repo the repository in which to look + * @param refname the full name of the branch + * @return 0 or an error code + */ + GIT_EXTERN(int) git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname); + /** @} */ GIT_END_DECL #endif diff --git a/src/branch.c b/src/branch.c index 52760853b..c24904a6c 100644 --- a/src/branch.c +++ b/src/branch.c @@ -384,6 +384,29 @@ cleanup: return error; } +int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname) +{ + int error; + const char *str; + git_config *cfg; + + if (!git_reference__is_branch(refname)) + return not_a_local_branch(refname); + + git_buf_sanitize(buf); + if ((error = git_repository_config_snapshot(&cfg, repo)) < 0) + return error; + + if ((error = retrieve_upstream_configuration(&str, cfg, refname, "branch.%s.remote")) < 0) + goto cleanup; + + error = git_buf_puts(buf, str); + +cleanup: + git_config_free(cfg); + return error; +} + int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname) { git_strarray remote_list = {0}; diff --git a/tests/refs/branches/upstream.c b/tests/refs/branches/upstream.c index ce3569813..abf7933d3 100644 --- a/tests/refs/branches/upstream.c +++ b/tests/refs/branches/upstream.c @@ -61,6 +61,15 @@ void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch)); } +void test_refs_branches_upstream__upstream_remote(void) +{ + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_branch_upstream_remote(&buf, repo, "refs/heads/master")); + cl_assert_equal_s("test", buf.ptr); + git_buf_free(&buf); +} + static void assert_merge_and_or_remote_key_missing(git_repository *repository, const git_commit *target, const char *entry_name) { git_reference *branch; From e235db021267841ac39e2f539339d63d7e757672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 8 Nov 2014 20:09:11 +0100 Subject: [PATCH 2/4] remote: use git_branch_upstream_remote() This reduces the clutter somewhat and lets us see what we're asking about the reference. --- src/remote.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/remote.c b/src/remote.c index 0007c41db..c2c71592e 100644 --- a/src/remote.c +++ b/src/remote.c @@ -936,9 +936,10 @@ static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_re git_buf remote_name = GIT_BUF_INIT; git_buf upstream_name = GIT_BUF_INIT; git_buf config_key = GIT_BUF_INIT; + git_buf upstream_remote = GIT_BUF_INIT; git_repository *repo; git_config *config = NULL; - const char *ref_name, *branch_remote; + const char *ref_name; int error = 0; assert(out && spec && ref); @@ -958,17 +959,16 @@ static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_re } if ((!git_reference__is_branch(ref_name)) || - (error = git_repository_config_snapshot(&config, repo)) < 0 || - (error = git_buf_printf(&config_key, "branch.%s.remote", - ref_name + strlen(GIT_REFS_HEADS_DIR))) < 0 || - (error = git_config_get_string(&branch_remote, config, git_buf_cstr(&config_key))) < 0 || - git__strcmp(git_remote_name(remote), branch_remote) || + (error = git_branch_upstream_remote(&upstream_remote, repo, ref_name)) || + git__strcmp(git_remote_name(remote), git_buf_cstr(&upstream_remote)) || (error = git_branch_upstream_name(&upstream_name, repo, ref_name)) < 0 || !git_refspec_dst_matches(spec, git_buf_cstr(&upstream_name)) || (error = git_refspec_rtransform(&remote_name, spec, upstream_name.ptr)) < 0) { /* Not an error if there is no upstream */ - if (error == GIT_ENOTFOUND) + if (error == GIT_ENOTFOUND) { + giterr_clear(); error = 0; + } goto cleanup; } @@ -977,6 +977,7 @@ static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_re cleanup: git_reference_free(resolved_ref); + git_buf_free(&upstream_remote); git_buf_free(&remote_name); git_buf_free(&upstream_name); git_buf_free(&config_key); From 64fdd86d0e820479b414628051cf8e16c643dc8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 8 Nov 2014 20:21:14 +0100 Subject: [PATCH 3/4] remote: don't check for upstream on an anonymous remote If the remote is anonymous, then we cannot check for any configuration, as there is no name. Check for this before we try to use the name, which may be a NULL pointer. This fixes #2697. --- src/remote.c | 1 + tests/network/remote/remotes.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/remote.c b/src/remote.c index c2c71592e..ce3530156 100644 --- a/src/remote.c +++ b/src/remote.c @@ -959,6 +959,7 @@ static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_re } if ((!git_reference__is_branch(ref_name)) || + !git_remote_name(remote) || (error = git_branch_upstream_remote(&upstream_remote, repo, ref_name)) || git__strcmp(git_remote_name(remote), git_buf_cstr(&upstream_remote)) || (error = git_branch_upstream_name(&upstream_name, repo, ref_name)) < 0 || diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c index 2cdf9226e..c00929a23 100644 --- a/tests/network/remote/remotes.c +++ b/tests/network/remote/remotes.c @@ -535,6 +535,16 @@ static int remote_single_branch(git_remote **out, git_repository *repo, const ch return 0; } +void test_network_remote_remotes__fetch_from_anonymous(void) +{ + git_remote *remote; + + cl_git_pass(git_remote_create_anonymous(&remote, _repo, cl_fixture("testrepo.git"), + "refs/heads/*:refs/other/*")); + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); + git_remote_free(remote); +} + void test_network_remote_remotes__single_branch(void) { git_clone_options opts = GIT_CLONE_OPTIONS_INIT; From 2c9b9c8bb4ffe083ddea7044cdfd07d6985756d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 8 Nov 2014 20:40:02 +0100 Subject: [PATCH 4/4] remote: refactor the reference-update decision This is an ugly chunk of code, so let's put it into its own function. --- src/remote.c | 62 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/src/remote.c b/src/remote.c index ce3530156..83e65f9ef 100644 --- a/src/remote.c +++ b/src/remote.c @@ -930,24 +930,50 @@ static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *upda return 0; } +static int ref_to_update(int *update, git_buf *remote_name, git_remote *remote, git_refspec *spec, const char *ref_name) +{ + int error = 0; + git_repository *repo; + git_buf upstream_remote = GIT_BUF_INIT; + git_buf upstream_name = GIT_BUF_INIT; + + repo = git_remote_owner(remote); + + if ((!git_reference__is_branch(ref_name)) || + !git_remote_name(remote) || + (error = git_branch_upstream_remote(&upstream_remote, repo, ref_name) < 0) || + git__strcmp(git_remote_name(remote), git_buf_cstr(&upstream_remote)) || + (error = git_branch_upstream_name(&upstream_name, repo, ref_name)) < 0 || + !git_refspec_dst_matches(spec, git_buf_cstr(&upstream_name)) || + (error = git_refspec_rtransform(remote_name, spec, upstream_name.ptr)) < 0) { + /* Not an error if there is no upstream */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + + *update = 0; + } else { + *update = 1; + } + + git_buf_free(&upstream_remote); + git_buf_free(&upstream_name); + return error; +} + static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_refspec *spec, git_vector *update_heads, git_reference *ref) { git_reference *resolved_ref = NULL; git_buf remote_name = GIT_BUF_INIT; - git_buf upstream_name = GIT_BUF_INIT; - git_buf config_key = GIT_BUF_INIT; - git_buf upstream_remote = GIT_BUF_INIT; - git_repository *repo; git_config *config = NULL; const char *ref_name; - int error = 0; + int error = 0, update; assert(out && spec && ref); *out = NULL; - repo = git_reference_owner(ref); - error = git_reference_resolve(&resolved_ref, ref); /* If we're in an unborn branch, let's pretend nothing happened */ @@ -958,30 +984,14 @@ static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_re ref_name = git_reference_name(resolved_ref); } - if ((!git_reference__is_branch(ref_name)) || - !git_remote_name(remote) || - (error = git_branch_upstream_remote(&upstream_remote, repo, ref_name)) || - git__strcmp(git_remote_name(remote), git_buf_cstr(&upstream_remote)) || - (error = git_branch_upstream_name(&upstream_name, repo, ref_name)) < 0 || - !git_refspec_dst_matches(spec, git_buf_cstr(&upstream_name)) || - (error = git_refspec_rtransform(&remote_name, spec, upstream_name.ptr)) < 0) { - /* Not an error if there is no upstream */ - if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; - } - + if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0) goto cleanup; - } - error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name)); + if (update) + error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name)); cleanup: git_reference_free(resolved_ref); - git_buf_free(&upstream_remote); - git_buf_free(&remote_name); - git_buf_free(&upstream_name); - git_buf_free(&config_key); git_config_free(config); return error; }