Commit 72df17c659619707e4e5f27b2a51db60848a1d04

Edward Thomson 2021-08-27T10:59:51

remote: introduce git_remote_ready_cb Introduce a new callback that fires when the remote is ready to connect.

diff --git a/include/git2/remote.h b/include/git2/remote.h
index 5b67717..b75a991 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -514,6 +514,18 @@ typedef int GIT_CALLBACK(git_push_update_reference_cb)(const char *refname, cons
 typedef int GIT_CALLBACK(git_url_resolve_cb)(git_buf *url_resolved, const char *url, int direction, void *payload);
 
 /**
+ * Callback invoked immediately before we attempt to connect to the
+ * given url.  Callers may change the URL before the connection by
+ * calling `git_remote_set_instance_url` in the callback.
+ *
+ * @param remote The remote to be connected
+ * @param direction GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
+ * @param payload Payload provided by the caller
+ * @return 0 on success, or an error
+ */
+typedef int GIT_CALLBACK(git_remote_ready_cb)(git_remote *remote, int direction, void *payload);
+
+/**
  * The callback settings structure
  *
  * Set the callbacks to be called by the remote when informing the user
@@ -598,6 +610,11 @@ struct git_remote_callbacks {
 	git_transport_cb transport;
 
 	/**
+	 * Callback when the remote is ready to connect.
+	 */
+	git_remote_ready_cb remote_ready;
+
+	/**
 	 * This will be passed to each of the callbacks in this struct
 	 * as the last parameter.
 	 */
diff --git a/src/remote.c b/src/remote.c
index 236f39a..8050e65 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -709,11 +709,19 @@ int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int
 	GIT_ASSERT_ARG(remote);
 	GIT_ASSERT_ARG(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
 
-	if (direction == GIT_DIRECTION_FETCH) {
+	if (callbacks && callbacks->remote_ready) {
+		int status = callbacks->remote_ready(remote, direction, callbacks->payload);
+
+		if (status != 0 && status != GIT_PASSTHROUGH) {
+			git_error_set_after_callback_function(status, "git_remote_ready_cb");
+			return status;
+		}
+	}
+
+	if (direction == GIT_DIRECTION_FETCH)
 		url = remote->url;
-	} else if (direction == GIT_DIRECTION_PUSH) {
+	else if (direction == GIT_DIRECTION_PUSH)
 		url = remote->pushurl ? remote->pushurl : remote->url;
-	}
 
 	if (!url) {
 		git_error_set(GIT_ERROR_INVALID,
@@ -722,6 +730,7 @@ int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int
 			direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
 		return GIT_EINVALID;
 	}
+
 	return resolve_url(url_out, url, direction, callbacks);
 }
 
diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c
index a962d92..4a9f5ae 100644
--- a/tests/network/remote/remotes.c
+++ b/tests/network/remote/remotes.c
@@ -56,6 +56,48 @@ void test_network_remote_remotes__parsing(void)
 	git_buf_dispose(&url);
 }
 
+static int remote_ready_callback(git_remote *remote, int direction, void *payload)
+{
+	if (direction == GIT_DIRECTION_PUSH) {
+		const char *url = git_remote_pushurl(remote);
+
+		cl_assert_equal_p(url, NULL);;
+		cl_assert_equal_s(payload, "payload");
+		return git_remote_set_instance_pushurl(remote, "push_url");
+	}
+
+	if (direction == GIT_DIRECTION_FETCH) {
+		const char *url = git_remote_url(remote);
+
+		cl_assert_equal_s(url, "git://github.com/libgit2/libgit2");
+		cl_assert_equal_s(payload, "payload");
+		return git_remote_set_instance_url(remote, "fetch_url");
+	}
+
+	return -1;
+}
+
+void test_network_remote_remotes__remote_ready(void)
+{
+	git_buf url = GIT_BUF_INIT;
+
+	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+	callbacks.remote_ready = remote_ready_callback;
+	callbacks.payload = "payload";
+
+	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_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, &callbacks));
+	cl_assert_equal_s(url.ptr, "fetch_url");
+
+	cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, &callbacks));
+	cl_assert_equal_s(url.ptr, "push_url");
+
+	git_buf_dispose(&url);
+}
+
 static int urlresolve_callback(git_buf *url_resolved, const char *url, int direction, void *payload)
 {
 	cl_assert(strcmp(url, "git://github.com/libgit2/libgit2") == 0);
diff --git a/tests/online/push_util.h b/tests/online/push_util.h
index d829bbc..5f669fe 100644
--- a/tests/online/push_util.h
+++ b/tests/online/push_util.h
@@ -12,7 +12,7 @@ extern const git_oid OID_ZERO;
  * @param data pointer to a record_callbacks_data instance
  */
 #define RECORD_CALLBACKS_INIT(data) \
-	{ GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, NULL, NULL, NULL, NULL, NULL, data, NULL }
+	{ GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, NULL, NULL, NULL, NULL, NULL, NULL, data, NULL }
 
 typedef struct {
 	char *name;