refs: introduce git_reference_remote_tracking_from_branch()
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
diff --git a/include/git2/refs.h b/include/git2/refs.h
index 7f6eb0e..b119e90 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -363,6 +363,27 @@ GIT_EXTERN(int) git_reference_foreach_glob(
*/
GIT_EXTERN(int) git_reference_has_log(git_reference *ref);
+
+/**
+ * Return the reference supporting the remote tracking branch,
+ * given a reference branch.
+ *
+ * The input reference has to be located in the `refs/heads`
+ * namespace.
+ *
+ * @param tracking_ref Pointer where to store the retrieved
+ * reference.
+ *
+ * @param branch_ref A git local branch reference.
+ *
+ * @return 0 on success; GIT_ENOTFOUND when no remote tracking
+ * reference exists, otherwise an error code.
+ */
+GIT_EXTERN(int) git_reference_remote_tracking_from_branch(
+ git_reference **tracking_ref,
+ git_reference *branch_ref
+);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/src/refs.c b/src/refs.c
index e8f9fc8..13022c7 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -11,6 +11,7 @@
#include "fileops.h"
#include "pack.h"
#include "reflog.h"
+#include "config.h"
#include <git2/tag.h>
#include <git2/object.h>
@@ -1811,3 +1812,76 @@ int git_reference_has_log(
return result;
}
+
+//TODO: How about also taking care of local tracking branches?
+//cf. http://alblue.bandlem.com/2011/07/git-tip-of-week-tracking-branches.html
+int git_reference_remote_tracking_from_branch(
+ git_reference **tracking_ref,
+ git_reference *branch_ref)
+{
+ git_config *config = NULL;
+ const char *name, *remote, *merge;
+ git_buf buf = GIT_BUF_INIT;
+ int error = -1;
+
+ assert(tracking_ref && branch_ref);
+
+ name = git_reference_name(branch_ref);
+
+ if (git__prefixcmp(name, GIT_REFS_HEADS_DIR)) {
+ giterr_set(
+ GITERR_INVALID,
+ "Failed to retrieve tracking reference - '%s' is not a branch.",
+ name);
+ return -1;
+ }
+
+ if (git_repository_config(&config, branch_ref->owner) < 0)
+ return -1;
+
+ if (git_buf_printf(
+ &buf,
+ "branch.%s.remote",
+ name + strlen(GIT_REFS_HEADS_DIR)) < 0)
+ goto cleanup;
+
+ if ((error = git_config_get_string(&remote, config, git_buf_cstr(&buf))) < 0)
+ goto cleanup;
+
+ error = -1;
+
+ git_buf_clear(&buf);
+
+ //TODO: Is it ok to fail when no merge target is found?
+ if (git_buf_printf(
+ &buf,
+ "branch.%s.merge",
+ name + strlen(GIT_REFS_HEADS_DIR)) < 0)
+ goto cleanup;
+
+ if (git_config_get_string(&merge, config, git_buf_cstr(&buf)) < 0)
+ goto cleanup;
+
+ //TODO: Should we test this?
+ if (git__prefixcmp(merge, GIT_REFS_HEADS_DIR))
+ goto cleanup;
+
+ git_buf_clear(&buf);
+
+ if (git_buf_printf(
+ &buf,
+ "refs/remotes/%s/%s",
+ remote,
+ merge + strlen(GIT_REFS_HEADS_DIR)) < 0)
+ goto cleanup;
+
+ error = git_reference_lookup(
+ tracking_ref,
+ branch_ref->owner,
+ git_buf_cstr(&buf));
+
+cleanup:
+ git_config_free(config);
+ git_buf_free(&buf);
+ return error;
+}
diff --git a/tests-clar/refs/remotetracking.c b/tests-clar/refs/remotetracking.c
new file mode 100644
index 0000000..c4ec588
--- /dev/null
+++ b/tests-clar/refs/remotetracking.c
@@ -0,0 +1,49 @@
+#include "clar_libgit2.h"
+
+static git_repository *g_repo;
+
+void test_refs_remotetracking__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_remotetracking__cleanup(void)
+{
+ git_repository_free(g_repo);
+}
+
+void test_refs_remotetracking__unfound_returns_GIT_ENOTFOUND(void)
+{
+ git_reference *branch, *tracking;
+
+ cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/subtrees"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_remote_tracking_from_branch(&tracking, branch));
+
+ git_reference_free(branch);
+}
+
+void test_refs_remotetracking__retrieving_from_a_non_head_fails(void)
+{
+ git_reference *branch, *tracking;
+
+ cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/tags/e90810b"));
+
+ cl_git_fail(git_reference_remote_tracking_from_branch(&tracking, branch));
+
+ git_reference_free(branch);
+}
+
+void test_refs_remotetracking__can_retrieve_a_remote_tracking_branch_reference(void)
+{
+ git_reference *branch, *tracking;
+
+ cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_reference_remote_tracking_from_branch(&tracking, branch));
+
+ cl_assert_equal_s("refs/remotes/test/master", git_reference_name(tracking));
+
+ git_reference_free(branch);
+ git_reference_free(tracking);
+}