summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/git2/refs.h21
-rw-r--r--src/refs.c74
-rw-r--r--tests-clar/refs/remotetracking.c49
3 files changed, 144 insertions, 0 deletions
diff --git a/include/git2/refs.h b/include/git2/refs.h
index 7f6eb0e9..b119e90b 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 e8f9fc8d..13022c7a 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 00000000..c4ec588e
--- /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);
+}