Commit 581b6a8e90bbbc704f85906b8a32374fe1c0128c

Carlos Martín Nieto 2012-07-26T17:29:58

Merge pull request #838 from scunz/remote_push_url Add support for push-urls

diff --git a/include/git2/remote.h b/include/git2/remote.h
index 5c01949..6d4b6cc 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -80,6 +80,36 @@ GIT_EXTERN(const char *) git_remote_name(git_remote *remote);
 GIT_EXTERN(const char *) git_remote_url(git_remote *remote);
 
 /**
+ * Get the remote's url for pushing
+ *
+ * @param remote the remote
+ * @return a pointer to the url or NULL if no special url for pushing is set
+ */
+GIT_EXTERN(const char *) git_remote_pushurl(git_remote *remote);
+
+/**
+ * Set the remote's url
+ *
+ * Existing connections will not be updated.
+ *
+ * @param remote the remote
+ * @param url the url to set
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_url(git_remote *remote, const char* url);
+
+/**
+ * Set the remote's url for pushing
+ *
+ * Existing connections will not be updated.
+ *
+ * @param remote the remote
+ * @param url the url to set or NULL to clear the pushurl
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url);
+
+/**
  * Set the remote's fetch refspec
  *
  * @param remote the remote
diff --git a/src/remote.c b/src/remote.c
index e46249e..b4a21a6 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -131,6 +131,26 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
 	GITERR_CHECK_ALLOC(remote->url);
 
 	git_buf_clear(&buf);
+	if (git_buf_printf(&buf, "remote.%s.pushurl", name) < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	error = git_config_get_string(&val, config, git_buf_cstr(&buf));
+	if (error == GIT_ENOTFOUND)
+		error = 0;
+
+	if (error < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	if (val) {
+		remote->pushurl = git__strdup(val);
+		GITERR_CHECK_ALLOC(remote->pushurl);
+	}
+
+	git_buf_clear(&buf);
 	if (git_buf_printf(&buf, "remote.%s.fetch", name) < 0) {
 		error = -1;
 		goto cleanup;
@@ -179,7 +199,7 @@ int git_remote_save(const git_remote *remote)
 	if (git_repository_config__weakptr(&config, remote->repo) < 0)
 		return -1;
 
-	if (git_buf_printf(&buf, "remote.%s.%s", remote->name, "url") < 0)
+	if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0)
 		return -1;
 
 	if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) {
@@ -187,6 +207,26 @@ int git_remote_save(const git_remote *remote)
 		return -1;
 	}
 
+	git_buf_clear(&buf);
+	if (git_buf_printf(&buf, "remote.%s.pushurl", remote->name) < 0)
+		return -1;
+
+	if (remote->pushurl) {
+		if (git_config_set_string(config, git_buf_cstr(&buf), remote->pushurl) < 0) {
+			git_buf_free(&buf);
+			return -1;
+		}
+	} else {
+		int error = git_config_delete(config, git_buf_cstr(&buf));
+		if (error == GIT_ENOTFOUND) {
+			error = 0;
+		}
+		if (error < 0) {
+			git_buf_free(&buf);
+			return -1;
+		}
+	}
+
 	if (remote->fetch.src != NULL && remote->fetch.dst != NULL) {
 		git_buf_clear(&buf);
 		git_buf_clear(&value);
@@ -238,6 +278,38 @@ const char *git_remote_url(git_remote *remote)
 	return remote->url;
 }
 
+int git_remote_set_url(git_remote *remote, const char* url)
+{
+	assert(remote);
+	assert(url);
+
+	git__free(remote->url);
+	remote->url = git__strdup(url);
+	GITERR_CHECK_ALLOC(remote->url);
+
+	return 0;
+}
+
+const char *git_remote_pushurl(git_remote *remote)
+{
+	assert(remote);
+	return remote->pushurl;
+}
+
+int git_remote_set_pushurl(git_remote *remote, const char* url)
+{
+	assert(remote);
+
+	git__free(remote->pushurl);
+	if (url) {
+		remote->pushurl = git__strdup(url);
+		GITERR_CHECK_ALLOC(remote->pushurl);
+	} else {
+		remote->pushurl = NULL;
+	}
+	return 0;
+}
+
 int git_remote_set_fetchspec(git_remote *remote, const char *spec)
 {
 	git_refspec refspec;
@@ -284,13 +356,32 @@ const git_refspec *git_remote_pushspec(git_remote *remote)
 	return &remote->push;
 }
 
+const char* git_remote__urlfordirection(git_remote *remote, int direction)
+{
+	assert(remote);
+
+	if (direction == GIT_DIR_FETCH) {
+		return remote->url;
+	}
+
+	if (direction == GIT_DIR_PUSH) {
+		return remote->pushurl ? remote->pushurl : remote->url;
+	}
+
+	return NULL;
+}
+
 int git_remote_connect(git_remote *remote, int direction)
 {
 	git_transport *t;
 
 	assert(remote);
 
-	if (git_transport_new(&t, remote->url) < 0)
+	const char* url = git_remote__urlfordirection(remote, direction);
+	if (url == NULL )
+		return -1;
+
+	if (git_transport_new(&t, url) < 0)
 		return -1;
 
 	t->check_cert = remote->check_cert;
@@ -429,6 +520,7 @@ void git_remote_free(git_remote *remote)
 	git__free(remote->push.src);
 	git__free(remote->push.dst);
 	git__free(remote->url);
+	git__free(remote->pushurl);
 	git__free(remote->name);
 	git__free(remote);
 }
diff --git a/src/remote.h b/src/remote.h
index 0949ad4..623d40c 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -14,6 +14,7 @@
 struct git_remote {
 	char *name;
 	char *url;
+	char *pushurl;
 	git_vector refs;
 	struct git_refspec fetch;
 	struct git_refspec push;
@@ -23,4 +24,6 @@ struct git_remote {
 		check_cert;
 };
 
+const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
+
 #endif
diff --git a/tests-clar/network/remotes.c b/tests-clar/network/remotes.c
index eb7947d..f1d6f47 100644
--- a/tests-clar/network/remotes.c
+++ b/tests-clar/network/remotes.c
@@ -2,6 +2,7 @@
 #include "buffer.h"
 #include "refspec.h"
 #include "transport.h"
+#include "remote.h"
 
 static git_remote *_remote;
 static git_repository *_repo;
@@ -27,8 +28,37 @@ void test_network_remotes__cleanup(void)
 
 void test_network_remotes__parsing(void)
 {
+	git_remote *_remote2 = NULL;
+
 	cl_assert_equal_s(git_remote_name(_remote), "test");
 	cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
+	cl_assert(git_remote_pushurl(_remote) == NULL);
+
+	cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIR_FETCH),
+					  "git://github.com/libgit2/libgit2");
+	cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIR_PUSH),
+					  "git://github.com/libgit2/libgit2");
+
+	cl_git_pass(git_remote_load(&_remote2, _repo, "test_with_pushurl"));
+	cl_assert_equal_s(git_remote_name(_remote2), "test_with_pushurl");
+	cl_assert_equal_s(git_remote_url(_remote2), "git://github.com/libgit2/fetchlibgit2");
+	cl_assert_equal_s(git_remote_pushurl(_remote2), "git://github.com/libgit2/pushlibgit2");
+
+	cl_assert_equal_s(git_remote__urlfordirection(_remote2, GIT_DIR_FETCH),
+					  "git://github.com/libgit2/fetchlibgit2");
+	cl_assert_equal_s(git_remote__urlfordirection(_remote2, GIT_DIR_PUSH),
+					  "git://github.com/libgit2/pushlibgit2");
+
+	git_remote_free(_remote2);
+}
+
+void test_network_remotes__pushurl(void)
+{
+	cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/notlibgit2"));
+	cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/notlibgit2");
+
+	cl_git_pass(git_remote_set_pushurl(_remote, NULL));
+	cl_assert(git_remote_pushurl(_remote) == NULL);
 }
 
 void test_network_remotes__parsing_ssh_remote(void)
@@ -81,6 +111,7 @@ void test_network_remotes__save(void)
 	cl_git_pass(git_remote_new(&_remote, _repo, "upstream", "git://github.com/libgit2/libgit2", NULL));
 	cl_git_pass(git_remote_set_fetchspec(_remote, "refs/heads/*:refs/remotes/upstream/*"));
 	cl_git_pass(git_remote_set_pushspec(_remote, "refs/heads/*:refs/heads/*"));
+	cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/libgit2_push"));
 	cl_git_pass(git_remote_save(_remote));
 	git_remote_free(_remote);
 	_remote = NULL;
@@ -98,6 +129,18 @@ void test_network_remotes__save(void)
 	cl_assert(_refspec != NULL);
 	cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*");
 	cl_assert_equal_s(git_refspec_dst(_refspec), "refs/heads/*");
+
+	cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
+	cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/libgit2_push");
+
+	/* remove the pushurl again and see if we can save that too */
+	cl_git_pass(git_remote_set_pushurl(_remote, NULL));
+	cl_git_pass(git_remote_save(_remote));
+	git_remote_free(_remote);
+	_remote = NULL;
+
+	cl_git_pass(git_remote_load(&_remote, _repo, "upstream"));
+	cl_assert(git_remote_pushurl(_remote) == NULL);
 }
 
 void test_network_remotes__fnmatch(void)
@@ -143,13 +186,13 @@ void test_network_remotes__list(void)
 	git_config *cfg;
 
 	cl_git_pass(git_remote_list(&list, _repo));
-	cl_assert(list.count == 1);
+	cl_assert(list.count == 2);
 	git_strarray_free(&list);
 
 	cl_git_pass(git_repository_config(&cfg, _repo));
 	cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com"));
 	cl_git_pass(git_remote_list(&list, _repo));
-	cl_assert(list.count == 2);
+	cl_assert(list.count == 3);
 	git_strarray_free(&list);
 
 	git_config_free(cfg);
@@ -180,4 +223,5 @@ void test_network_remotes__add(void)
 	cl_assert(!strcmp(git_refspec_src(_refspec), "refs/heads/*"));
 	cl_assert(git_refspec_force(_refspec) == 1);
 	cl_assert(!strcmp(git_refspec_dst(_refspec), "refs/remotes/addtest/*"));
+	cl_assert_equal_s(git_remote_url(_remote), "http://github.com/libgit2/libgit2");
 }
diff --git a/tests-clar/resources/testrepo.git/config b/tests-clar/resources/testrepo.git/config
index b4fdac6..c99d971 100644
--- a/tests-clar/resources/testrepo.git/config
+++ b/tests-clar/resources/testrepo.git/config
@@ -7,6 +7,11 @@
 	url = git://github.com/libgit2/libgit2
 	fetch = +refs/heads/*:refs/remotes/test/*
 
+[remote "test_with_pushurl"]
+	url = git://github.com/libgit2/fetchlibgit2
+	pushurl = git://github.com/libgit2/pushlibgit2
+	fetch = +refs/heads/*:refs/remotes/test_with_pushurl/*
+
 [branch "master"]
    remote = test
    merge = refs/heads/master