summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2016-08-04 13:45:28 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2016-10-01 17:40:41 +0200
commit85addddf4c903c83d3f6851c4dfebbe5b9d10376 (patch)
treea6659f8d55a35f9f3bd0e423cb795b043ee37019
parentd711165d03d785c1d8a8a5cf390a4dd0a6a0a107 (diff)
downloadlibgit2-85addddf4c903c83d3f6851c4dfebbe5b9d10376.tar.gz
refspec: do not set empty rhs for fetch refspecs
According to git-fetch(1), "[t]he colon can be omitted when <dst> is empty." So according to git, the refspec "refs/heads/master" is the same as the refspec "refs/heads/master:" when fetching changes. When trying to fetch from a remote with a trailing colon with libgit2, though, the fetch actually fails while it works when the trailing colon is left out. So obviously, libgit2 does _not_ treat these two refspec formats the same for fetches. The problem results from parsing refspecs, where the resulting refspec has its destination set to an empty string in the case of a trailing colon and to a `NULL` pointer in the case of no trailing colon. When passing this to our DWIM machinery, the empty string gets translated to "refs/heads/", which is simply wrong. Fix the problem by having the parsing machinery treat both cases the same for fetch refspecs.
-rw-r--r--src/refspec.c6
-rw-r--r--tests/online/fetchhead.c51
2 files changed, 55 insertions, 2 deletions
diff --git a/src/refspec.c b/src/refspec.c
index debde8692..d200e5609 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -53,8 +53,10 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
if (rhs) {
size_t rlen = strlen(++rhs);
- is_glob = (1 <= rlen && strchr(rhs, '*'));
- refspec->dst = git__strndup(rhs, rlen);
+ if (rlen || !is_fetch) {
+ is_glob = (1 <= rlen && strchr(rhs, '*'));
+ refspec->dst = git__strndup(rhs, rlen);
+ }
}
llen = (rhs ? (size_t)(rhs - lhs - 1) : strlen(lhs));
diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c
index 200edacfd..9aaad253c 100644
--- a/tests/online/fetchhead.c
+++ b/tests/online/fetchhead.c
@@ -35,6 +35,19 @@ static void fetchhead_test_clone(void)
cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
}
+static int count_references(void)
+{
+ git_strarray array;
+ int refs;
+
+ cl_git_pass(git_reference_list(&array, g_repo));
+ refs = array.count;
+
+ git_strarray_free(&array);
+
+ return refs;
+}
+
static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fetchhead)
{
git_remote *remote;
@@ -101,3 +114,41 @@ void test_online_fetchhead__no_merges(void)
cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA3);
}
+
+void test_online_fetchhead__explicit_dst_refspec_creates_branch(void)
+{
+ git_reference *ref;
+ int refs;
+
+ fetchhead_test_clone();
+ refs = count_references();
+ fetchhead_test_fetch("refs/heads/first-merge:refs/heads/explicit-refspec", FETCH_HEAD_EXPLICIT_DATA);
+
+ cl_git_pass(git_branch_lookup(&ref, g_repo, "explicit-refspec", GIT_BRANCH_ALL));
+ cl_assert_equal_i(refs + 1, count_references());
+}
+
+void test_online_fetchhead__empty_dst_refspec_creates_no_branch(void)
+{
+ git_reference *ref;
+ int refs;
+
+ fetchhead_test_clone();
+ refs = count_references();
+
+ fetchhead_test_fetch("refs/heads/first-merge", FETCH_HEAD_EXPLICIT_DATA);
+ cl_git_fail(git_branch_lookup(&ref, g_repo, "first-merge", GIT_BRANCH_ALL));
+
+ cl_assert_equal_i(refs, count_references());
+}
+
+void test_online_fetchhead__colon_only_dst_refspec_creates_no_branch(void)
+{
+ int refs;
+
+ fetchhead_test_clone();
+ refs = count_references();
+ fetchhead_test_fetch("refs/heads/first-merge:", FETCH_HEAD_EXPLICIT_DATA);
+
+ cl_assert_equal_i(refs, count_references());
+}