Merge pull request #2698 from libgit2/cmn/fetchhead-refactor Refactor fetchhead
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
diff --git a/include/git2/branch.h b/include/git2/branch.h
index f284100..8887810 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 9a3cb07..01402a2 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/src/remote.c b/src/remote.c
index 4b9993d..2206954 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -930,23 +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_repository *repo;
git_config *config = NULL;
- const char *ref_name, *branch_remote;
- int error = 0;
+ const char *ref_name;
+ 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 */
@@ -957,29 +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)) ||
- (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_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)
- 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(&remote_name);
- git_buf_free(&upstream_name);
- git_buf_free(&config_key);
git_config_free(config);
return error;
}
diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c
index a5e1ba2..07ad934 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;
diff --git a/tests/refs/branches/upstream.c b/tests/refs/branches/upstream.c
index ce35698..abf7933 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;