Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b8e1b76

Browse files
Erik Aignertiennou
authored andcommitted
remote: add callback to resolve URLs before connecting
Since libssh2 doesn't read host configuration from the config file, this callback can be used to hand over URL resolving to the client without touching the SSH implementation itself.
1 parent 65ad4fa commit b8e1b76

File tree

5 files changed

+258
-60
lines changed

5 files changed

+258
-60
lines changed

include/git2/remote.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,24 @@ typedef int GIT_CALLBACK(git_push_negotiation)(const git_push_update **updates,
474474
*/
475475
typedef int GIT_CALLBACK(git_push_update_reference_cb)(const char *refname, const char *status, void *data);
476476

477+
/**
478+
* Callback to resolve URLs before connecting to remote
479+
*
480+
* You need to be careful not to overflow url_resolved. If it's too small,
481+
* returning a positive value equal to the size required will cause the
482+
* resolution to be tried again with a buffer of the requested size.
483+
*
484+
* If you return GIT_PASSTHROUGH, you don't need to write anything to
485+
* url_resolved.
486+
*
487+
* @param url_resolved The buffer to write the resolved URL to
488+
* @param url The URL to resolve
489+
* @param direction GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
490+
* @param payload Payload provided by the caller
491+
* @return 0 on success, GIT_PASSTHROUGH or an error
492+
*/
493+
typedef int GIT_CALLBACK(git_resolve_url_cb)(git_buf *url_resolved, const char *url, int direction, void *payload);
494+
477495
/**
478496
* The callback settings structure
479497
*
@@ -562,9 +580,15 @@ struct git_remote_callbacks {
562580
* as the last parameter.
563581
*/
564582
void *payload;
583+
584+
/**
585+
* Resolve URL before connecting to remote.
586+
* The returned URL will be used to connect to the remote instead.
587+
*/
588+
git_resolve_url_cb resolve_url;
565589
};
566590

567-
#define GIT_REMOTE_CALLBACKS_VERSION 1
591+
#define GIT_REMOTE_CALLBACKS_VERSION 2
568592
#define GIT_REMOTE_CALLBACKS_INIT {GIT_REMOTE_CALLBACKS_VERSION}
569593

570594
/**

src/remote.c

Lines changed: 105 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -670,21 +670,59 @@ int git_remote_set_pushurl(git_repository *repo, const char *remote, const char*
670670
return set_url(repo, remote, CONFIG_PUSHURL_FMT, url);
671671
}
672672

673-
const char* git_remote__urlfordirection(git_remote *remote, int direction)
673+
static int resolve_url(git_buf *resolved_url, const char *url, int direction, const git_remote_callbacks *callbacks)
674674
{
675-
assert(remote);
675+
int status;
676676

677-
assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
677+
if (!callbacks || !callbacks->resolve_url) {
678+
git_buf_sets(resolved_url, url);
679+
return 0;
680+
}
681+
682+
/* Allocate a generous buffer to cater for most */
683+
git_buf_clear(resolved_url);
684+
git_buf_grow(resolved_url, 256);
685+
GIT_ERROR_CHECK_ALLOC_BUF(resolved_url);
686+
687+
do {
688+
status = callbacks->resolve_url(resolved_url, url, direction, callbacks->payload);
689+
if (status == GIT_PASSTHROUGH) {
690+
git_buf_sets(resolved_url, url);
691+
return 0;
692+
}
693+
if (status <= 0)
694+
return status;
695+
696+
/* A positive value tells us what's actually needed */
697+
git_buf_grow(resolved_url, status);
698+
GIT_ERROR_CHECK_ALLOC_BUF(resolved_url);
699+
} while (status > 0);
700+
701+
return 0;
702+
}
703+
704+
int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks)
705+
{
706+
const char *url = NULL;
707+
708+
assert(remote);
678709

679710
if (direction == GIT_DIRECTION_FETCH) {
680-
return remote->url;
711+
url = remote->url;
712+
} else if (direction == GIT_DIRECTION_PUSH) {
713+
url = remote->pushurl ? remote->pushurl : remote->url;
714+
} else {
715+
assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
681716
}
682717

683-
if (direction == GIT_DIRECTION_PUSH) {
684-
return remote->pushurl ? remote->pushurl : remote->url;
718+
if (!url) {
719+
git_error_set(GIT_ERROR_INVALID,
720+
"Malformed remote '%s' - missing %s URL",
721+
remote->name ? remote->name : "(anonymous)",
722+
direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
723+
return GIT_ERROR_INVALID;
685724
}
686-
687-
return NULL;
725+
return resolve_url(url_out, url, direction, callbacks);
688726
}
689727

690728
int set_transport_callbacks(git_transport *t, const git_remote_callbacks *cbs)
@@ -704,10 +742,34 @@ static int set_transport_custom_headers(git_transport *t, const git_strarray *cu
704742
return t->set_custom_headers(t, custom_headers);
705743
}
706744

707-
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn)
745+
int git_struct__upgrade_remote_callbacks(
746+
git_remote_callbacks *new,
747+
const git_struct__version *old,
748+
const char *name)
749+
{
750+
unsigned int latest = old->version;
751+
size_t size;
752+
753+
GIT_UNUSED(name);
754+
755+
switch (old->version) {
756+
case 1:
757+
size = sizeof(new) - sizeof(git_resolve_url_cb);
758+
memcpy(new, old, size);
759+
new->version = latest;
760+
break;
761+
762+
default:
763+
break;
764+
}
765+
return 0;
766+
}
767+
768+
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *given_callbacks, const git_remote_connection_opts *conn)
708769
{
709-
git_transport *t;
710-
const char *url;
770+
git_transport *t = NULL;
771+
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
772+
git_buf url = GIT_BUF_INIT;
711773
int flags = GIT_TRANSPORTFLAGS_NONE;
712774
int error;
713775
void *payload = NULL;
@@ -716,51 +778,48 @@ int git_remote__connect(git_remote *remote, git_direction direction, const git_r
716778

717779
assert(remote);
718780

719-
if (callbacks) {
720-
GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
721-
credentials = callbacks->credentials;
722-
transport = callbacks->transport;
723-
payload = callbacks->payload;
724-
}
781+
GIT_STRUCT_UPGRADE(&callbacks, sizeof(callbacks), given_callbacks, "git_remote_callbacks", git_struct__upgrade_remote_callbacks);
782+
credentials = callbacks.credentials;
783+
transport = callbacks.transport;
784+
payload = callbacks.payload;
725785

726786
if (conn->proxy)
727787
GIT_ERROR_CHECK_VERSION(conn->proxy, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
728788

729789
t = remote->transport;
730790

731-
url = git_remote__urlfordirection(remote, direction);
732-
if (url == NULL) {
733-
git_error_set(GIT_ERROR_INVALID,
734-
"Malformed remote '%s' - missing %s URL",
735-
remote->name ? remote->name : "(anonymous)",
736-
direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
737-
return -1;
738-
}
791+
if ((error = git_remote__urlfordirection(&url, remote, direction, &callbacks)))
792+
goto on_error;
739793

740794
/* If we don't have a transport object yet, and the caller specified a
741795
* custom transport factory, use that */
742796
if (!t && transport &&
743797
(error = transport(&t, remote, payload)) < 0)
744-
return error;
798+
goto on_error;
745799

746800
/* If we still don't have a transport, then use the global
747801
* transport registrations which map URI schemes to transport factories */
748-
if (!t && (error = git_transport_new(&t, remote, url)) < 0)
749-
return error;
802+
if (!t && (error = git_transport_new(&t, remote, url.ptr)) < 0)
803+
goto on_error;
750804

751805
if ((error = set_transport_custom_headers(t, conn->custom_headers)) != 0)
752806
goto on_error;
753807

754-
if ((error = set_transport_callbacks(t, callbacks)) < 0 ||
755-
(error = t->connect(t, url, credentials, payload, conn->proxy, direction, flags)) != 0)
808+
if ((error = set_transport_callbacks(t, &callbacks)) < 0 ||
809+
(error = t->connect(t, url.ptr, credentials, payload, conn->proxy, direction, flags)) != 0)
756810
goto on_error;
757811

758812
remote->transport = t;
759813

814+
git_buf_dispose(&url);
815+
760816
return 0;
761817

762818
on_error:
763-
t->free(t);
819+
if (t)
820+
t->free(t);
821+
822+
git_buf_dispose(&url);
764823

765824
if (t == remote->transport)
766825
remote->transport = NULL;
@@ -922,7 +981,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
922981
int error = -1;
923982
size_t i;
924983
git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
925-
const git_remote_callbacks *cbs = NULL;
984+
git_remote_callbacks cbs = GIT_REMOTE_CALLBACKS_INIT;
926985
const git_strarray *custom_headers = NULL;
927986
const git_proxy_options *proxy = NULL;
928987

@@ -934,15 +993,14 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
934993
}
935994

936995
if (opts) {
937-
GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
938-
cbs = &opts->callbacks;
996+
GIT_STRUCT_UPGRADE(&cbs, sizeof(cbs), &opts->callbacks, "git_remote_callbacks", git_struct__upgrade_remote_callbacks);
939997
custom_headers = &opts->custom_headers;
940998
GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
941999
proxy = &opts->proxy_opts;
9421000
}
9431001

9441002
if (!git_remote_connected(remote) &&
945-
(error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) < 0)
1003+
(error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &cbs, proxy, custom_headers)) < 0)
9461004
goto on_error;
9471005

9481006
if (ls_to_vector(&refs, remote) < 0)
@@ -986,7 +1044,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
9861044
if ((error = git_fetch_negotiate(remote, opts)) < 0)
9871045
return error;
9881046

989-
return git_fetch_download_pack(remote, cbs);
1047+
return git_fetch_download_pack(remote, &cbs);
9901048

9911049
on_error:
9921050
git_vector_free(&refs);
@@ -1005,12 +1063,11 @@ int git_remote_fetch(
10051063
git_remote_autotag_option_t tagopt = remote->download_tags;
10061064
bool prune = false;
10071065
git_buf reflog_msg_buf = GIT_BUF_INIT;
1008-
const git_remote_callbacks *cbs = NULL;
1066+
git_remote_callbacks cbs = GIT_REMOTE_CALLBACKS_INIT;
10091067
git_remote_connection_opts conn = GIT_REMOTE_CONNECTION_OPTIONS_INIT;
10101068

10111069
if (opts) {
1012-
GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
1013-
cbs = &opts->callbacks;
1070+
GIT_STRUCT_UPGRADE(&cbs, sizeof(cbs), &opts->callbacks, "git_remote_callbacks", git_struct__upgrade_remote_callbacks);
10141071
conn.custom_headers = &opts->custom_headers;
10151072
update_fetchhead = opts->update_fetchhead;
10161073
tagopt = opts->download_tags;
@@ -1019,7 +1076,7 @@ int git_remote_fetch(
10191076
}
10201077

10211078
/* Connect and download everything */
1022-
if ((error = git_remote__connect(remote, GIT_DIRECTION_FETCH, cbs, &conn)) != 0)
1079+
if ((error = git_remote__connect(remote, GIT_DIRECTION_FETCH, &cbs, &conn)) != 0)
10231080
return error;
10241081

10251082
error = git_remote_download(remote, refspecs, opts);
@@ -1040,7 +1097,7 @@ int git_remote_fetch(
10401097
}
10411098

10421099
/* Create "remote/foo" branches for all remote branches */
1043-
error = git_remote_update_tips(remote, cbs, update_fetchhead, tagopt, git_buf_cstr(&reflog_msg_buf));
1100+
error = git_remote_update_tips(remote, &cbs, update_fetchhead, tagopt, git_buf_cstr(&reflog_msg_buf));
10441101
git_buf_dispose(&reflog_msg_buf);
10451102
if (error < 0)
10461103
return error;
@@ -1055,7 +1112,7 @@ int git_remote_fetch(
10551112
prune = remote->prune_refs;
10561113

10571114
if (prune)
1058-
error = git_remote_prune(remote, cbs);
1115+
error = git_remote_prune(remote, &cbs);
10591116

10601117
return error;
10611118
}
@@ -1258,11 +1315,11 @@ int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks)
12581315
git_vector candidates = GIT_VECTOR_INIT;
12591316
const git_refspec *spec;
12601317
const char *refname;
1318+
git_remote_callbacks cbs = GIT_REMOTE_CALLBACKS_INIT;
12611319
int error;
12621320
git_oid zero_id = {{ 0 }};
12631321

1264-
if (callbacks)
1265-
GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
1322+
GIT_STRUCT_UPGRADE(&cbs, sizeof(cbs), callbacks, "git_remote_callbacks", git_struct__upgrade_remote_callbacks);
12661323

12671324
if ((error = ls_to_vector(&remote_refs, remote)) < 0)
12681325
goto cleanup;
@@ -2491,7 +2548,7 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi
24912548
int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
24922549
{
24932550
int error;
2494-
const git_remote_callbacks *cbs = NULL;
2551+
git_remote_callbacks cbs = GIT_REMOTE_CALLBACKS_INIT;
24952552
const git_strarray *custom_headers = NULL;
24962553
const git_proxy_options *proxy = NULL;
24972554

@@ -2503,22 +2560,21 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_
25032560
}
25042561

25052562
if (opts) {
2506-
GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
2507-
cbs = &opts->callbacks;
2563+
GIT_STRUCT_UPGRADE(&cbs, sizeof(cbs), &opts->callbacks, "git_remote_callbacks", git_struct__upgrade_remote_callbacks);
25082564
custom_headers = &opts->custom_headers;
25092565
GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
25102566
proxy = &opts->proxy_opts;
25112567
}
25122568

25132569
assert(remote);
25142570

2515-
if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
2571+
if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, &cbs, proxy, custom_headers)) < 0)
25162572
return error;
25172573

25182574
if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
25192575
return error;
25202576

2521-
error = git_remote_update_tips(remote, cbs, 0, 0, NULL);
2577+
error = git_remote_update_tips(remote, &cbs, 0, 0, NULL);
25222578

25232579
git_remote_disconnect(remote);
25242580
return error;

src/remote.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ typedef struct git_remote_connection_opts {
4545

4646
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn);
4747

48-
const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
48+
int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks);
4949
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url);
5050

5151
git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname);

0 commit comments

Comments
 (0)