"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
Re: separate send and fetch config blocks
To:
gameoftrees@openbsd.org
Date:
Mon, 30 Aug 2021 15:36:34 +0200

Download raw body.

Thread
  • Stefan Sperling:

    separate send and fetch config blocks

  • On Mon, Aug 30, 2021 at 01:33:51PM +0200, Stefan Sperling wrote:
    > This allows for setting fetch-specific and send-specific options in got.conf.
    > I have added a minimal test which verifies that different repositories can
    > be fetched from and sent to.
    > 
    > I have not yet updated got.conf documentation.
    
    New patch which updates the documentation as well.
    
    diff refs/heads/main refs/heads/sendfetchcfg
    blob - f48598b57aa7474d4174c928c96d948834824f21
    blob + 7341a1e00a6e590674fc8184217fe51ee5893014
    --- got/got.c
    +++ got/got.c
    @@ -2370,20 +2370,20 @@ cmd_fetch(int argc, char *argv[])
     	if (TAILQ_EMPTY(&wanted_branches)) {
     		if (!fetch_all_branches)
     			fetch_all_branches = remote->fetch_all_branches;
    -		for (i = 0; i < remote->nbranches; i++) {
    +		for (i = 0; i < remote->nfetch_branches; i++) {
     			got_pathlist_append(&wanted_branches,
    -			    remote->branches[i], NULL);
    +			    remote->fetch_branches[i], NULL);
     		}
     	}
     	if (TAILQ_EMPTY(&wanted_refs)) {
    -		for (i = 0; i < remote->nrefs; i++) {
    +		for (i = 0; i < remote->nfetch_refs; i++) {
     			got_pathlist_append(&wanted_refs,
    -			    remote->refs[i], NULL);
    +			    remote->fetch_refs[i], NULL);
     		}
     	}
     
     	error = got_fetch_parse_uri(&proto, &host, &port, &server_path,
    -	    &repo_name, remote->url);
    +	    &repo_name, remote->fetch_url);
     	if (error)
     		goto done;
     
    @@ -7701,7 +7701,7 @@ cmd_send(int argc, char *argv[])
     	}
     
     	error = got_fetch_parse_uri(&proto, &host, &port, &server_path,
    -	    &repo_name, remote->url);
    +	    &repo_name, remote->send_url);
     	if (error)
     		goto done;
     
    blob - 58e0d602e43b9e45856efd11c355b7c86cccedbc
    blob + 7b400d18d82d02aba5e0b4d7b7dad40a43b51be5
    --- got/got.conf.5
    +++ got/got.conf.5
    @@ -60,7 +60,9 @@ Define a remote repository.
     The specified
     .Ar name
     can be used to refer to the remote repository on the command line of
    -.Cm got fetch .
    +.Cm got fetch
    +and
    +.Cm got send .
     .Pp
     Information about this repository is declared in a block of options
     enclosed in curly brackets:
    @@ -108,10 +110,14 @@ will be used.
     .It Ic branch Brq Ar branch ...
     Specify one or more branches which
     .Cm got fetch
    -should fetch from the remote repository by default.
    +and
    +.Cm got send
    +should fetch and send from the remote repository by default.
     The list of branches specified here can be overridden at the
     .Cm got fetch
    -command line with the
    +and
    +.Cm got send
    +command lines with the
     .Fl b
     option.
     .It Ic fetch-all-branches Ar yes | no
    @@ -165,8 +171,50 @@ all branches in the
     .Dq refs/heads/
     namespace will be updated directly to match the corresponding branches in
     the remote repository.
    +.It Ic fetch Brq ...
    +An optional
    +.Ic fetch
    +block may contain any of the following configuration settings
    +for use by
    +.Cm got fetch ,
    +overriding corresponding settings in the containing
    +.Ic remote Ar name Brq ...
    +block.
    +.Bl -bullet
    +.It
    +.Ic server Ar hostname
    +.It
    +.Ic repository Ar path
    +.It
    +.Ic protocol Ar path
    +.It
    +.Ic port Ar port
    +.It
    +.Ic branch Brq Ar branch ...
     .El
    +.It Ic send Brq ...
    +An optional
    +.Ic send
    +block may contain any of the following configuration settings
    +for use by
    +.Cm got send ,
    +overriding corresponding settings in the containing
    +.Ic remote Ar name Brq ...
    +block.
    +.Bl -bullet
    +.It
    +.Ic server Ar hostname
    +.It
    +.Ic repository Ar path
    +.It
    +.Ic protocol Ar path
    +.It
    +.Ic port Ar port
    +.It
    +.Ic branch Brq Ar branch ...
     .El
    +.El
    +.El
     .Sh EXAMPLES
     Configure author information:
     .Bd -literal -offset indent
    @@ -194,6 +242,19 @@ remote "origin" {
     	mirror-references yes
     }
     .Ed
    +.Pp
    +Fetch changes via the Git protocol and send changes via the SSH protocol:
    +.Bd -literal -offset indent
    +remote "origin" {
    +	repository my_repo
    +	server git.example.com
    +	protocol git
    +	send {
    +		server git@git.example.com
    +		protocol ssh
    +	}
    +}
    +.Ed
     .Sh FILES
     .Bl -tag -width Ds -compact
     .It Pa got.conf
    blob - 9c2f8389ca9fc09f3499080dd949d464f4e85c67
    blob + cad8fd52bf0d74bccf0f30ddd4d2173398530bcc
    --- gotadmin/gotadmin.c
    +++ gotadmin/gotadmin.c
    @@ -292,7 +292,7 @@ cmd_info(int argc, char *argv[])
     		got_gotconfig_get_remotes(&nremotes, &remotes, gotconfig);
     		for (i = 0; i < nremotes; i++) {
     			printf("remote \"%s\": %s\n", remotes[i].name,
    -			    remotes[i].url);
    +			    remotes[i].fetch_url);
     		}
     	}
     
    blob - daa3c3755d6b05b2eb1cfa3014d128ff50539685
    blob + 16dcde35f5cc7689f88ae7e3486c4f1fe72aa8f2
    --- include/got_repository.h
    +++ include/got_repository.h
    @@ -57,10 +57,11 @@ void got_repo_get_gitconfig_extensions(char ***, int *
     /* Information about one remote repository. */
     struct got_remote_repo {
     	char *name;
    -	char *url;
    +	char *fetch_url;
    +	char *send_url;
     
     	/*
    -	 * If set, references are mirrored 1:1 into the local repository.
    +	 * If set, fetched references are mirrored 1:1 into our repository.
     	 * If not set, references are mapped into "refs/remotes/$name/".
     	 */
     	int mirror_references;
    @@ -72,12 +73,16 @@ struct got_remote_repo {
     	int fetch_all_branches;
     
     	/* Branches to fetch by default. */
    -	int nbranches;
    -	char **branches;
    +	int nfetch_branches;
    +	char **fetch_branches;
     
    +	/* Branches to send by default. */
    +	int nsend_branches;
    +	char **send_branches;
    +
     	/* Other arbitrary references to fetch by default. */
    -	int nrefs;
    -	char **refs;
    +	int nfetch_refs;
    +	char **fetch_refs;
     };
     
     /*
    blob - daaf7f3639f1a1257da430a23bbc5529db74d685
    blob + e60e8eb1808bf28909abf7034ebbce19c0dc5100
    --- lib/got_lib_privsep.h
    +++ lib/got_lib_privsep.h
    @@ -443,19 +443,24 @@ struct got_imsg_traversed_commits {
     } __attribute__((__packed__));
     
     /*
    - * Structure for GOT_IMSG_GITCONFIG_REMOTE data.
    + * Structure for GOT_IMSG_GOTCONFIG_REMOTE and
    + * GOT_IMSG_GOTCONFIG_REMOTE data.
      */
     struct got_imsg_remote {
     	size_t name_len;
    -	size_t url_len;
    +	size_t fetch_url_len;
    +	size_t send_url_len;
     	int mirror_references;
     	int fetch_all_branches;
    -	int nbranches;
    -	int nrefs;
    +	int nfetch_branches;
    +	int nsend_branches;
    +	int nfetch_refs;
     
    -	/* Followed by name_len + url_len data bytes. */
    -	/* Followed by nbranches GOT_IMSG_GITCONFIG_STR_VAL messages. */
    -	/* Followed by nrefs GOT_IMSG_GITCONFIG_STR_VAL messages. */
    +	/* Followed by name_len data bytes. */
    +	/* Followed by fetch_url_len + send_url_len data bytes. */
    +	/* Followed by nfetch_branches GOT_IMSG_GITCONFIG_STR_VAL messages. */
    +	/* Followed by nsend_branches GOT_IMSG_GITCONFIG_STR_VAL messages. */
    +	/* Followed by nfetch_refs GOT_IMSG_GITCONFIG_STR_VAL messages. */
     } __attribute__((__packed__));
     
     /*
    blob - ecad96e05628c923f414e7613a59a1e54b82bba0
    blob + 4846f5880ced5034d986881b1a612ba1062686af
    --- lib/privsep.c
    +++ lib/privsep.c
    @@ -2206,13 +2206,17 @@ free_remote_data(struct got_remote_repo *remote)
     	int i;
     
     	free(remote->name);
    -	free(remote->url);
    -	for (i = 0; i < remote->nbranches; i++)
    -		free(remote->branches[i]);
    -	free(remote->branches);
    -	for (i = 0; i < remote->nrefs; i++)
    -		free(remote->refs[i]);
    -	free(remote->refs);
    +	free(remote->fetch_url);
    +	free(remote->send_url);
    +	for (i = 0; i < remote->nfetch_branches; i++)
    +		free(remote->fetch_branches[i]);
    +	free(remote->fetch_branches);
    +	for (i = 0; i < remote->nsend_branches; i++)
    +		free(remote->send_branches[i]);
    +	free(remote->send_branches);
    +	for (i = 0; i < remote->nfetch_refs; i++)
    +		free(remote->fetch_refs[i]);
    +	free(remote->fetch_refs);
     }
     
     const struct got_error *
    @@ -2274,9 +2278,11 @@ got_privsep_recv_gitconfig_remotes(struct got_remote_r
     				break;
     			}
     			memcpy(&iremote, imsg.data, sizeof(iremote));
    -			if (iremote.name_len == 0 || iremote.url_len == 0 ||
    +			if (iremote.name_len == 0 ||
    +			    iremote.fetch_url_len == 0 ||
    +			    iremote.send_url_len == 0 ||
     			    (sizeof(iremote) + iremote.name_len +
    -			    iremote.url_len) > datalen) {
    +			    iremote.fetch_url_len + iremote.send_url_len) > datalen) {
     				err = got_error(GOT_ERR_PRIVSEP_LEN);
     				break;
     			}
    @@ -2286,19 +2292,29 @@ got_privsep_recv_gitconfig_remotes(struct got_remote_r
     				err = got_error_from_errno("strndup");
     				break;
     			}
    -			remote->url = strndup(imsg.data + sizeof(iremote) +
    -			    iremote.name_len, iremote.url_len);
    -			if (remote->url == NULL) {
    +			remote->fetch_url = strndup(imsg.data + sizeof(iremote) +
    +			    iremote.name_len, iremote.fetch_url_len);
    +			if (remote->fetch_url == NULL) {
     				err = got_error_from_errno("strndup");
     				free_remote_data(remote);
     				break;
     			}
    +			remote->send_url = strndup(imsg.data + sizeof(iremote) +
    +			    iremote.name_len + iremote.fetch_url_len,
    +			    iremote.send_url_len);
    +			if (remote->send_url == NULL) {
    +				err = got_error_from_errno("strndup");
    +				free_remote_data(remote);
    +				break;
    +			}
     			remote->mirror_references = iremote.mirror_references;
     			remote->fetch_all_branches = iremote.fetch_all_branches;
    -			remote->nbranches = 0;
    -			remote->branches = NULL;
    -			remote->nrefs = 0;
    -			remote->refs = NULL;
    +			remote->nfetch_branches = 0;
    +			remote->fetch_branches = NULL;
    +			remote->nsend_branches = 0;
    +			remote->send_branches = NULL;
    +			remote->nfetch_refs = 0;
    +			remote->fetch_refs = NULL;
     			(*nremotes)++;
     			break;
     		default:
    @@ -2404,7 +2420,6 @@ got_privsep_recv_gotconfig_str(char **str, struct imsg
     	return err;
     }
     
    -
     const struct got_error *
     got_privsep_recv_gotconfig_remotes(struct got_remote_repo **remotes,
         int *nremotes, struct imsgbuf *ibuf)
    @@ -2483,9 +2498,12 @@ got_privsep_recv_gotconfig_remotes(struct got_remote_r
     				break;
     			}
     			memcpy(&iremote, imsg.data, sizeof(iremote));
    -			if (iremote.name_len == 0 || iremote.url_len == 0 ||
    +			if (iremote.name_len == 0 ||
    +			    (iremote.fetch_url_len == 0 &&
    +			    iremote.send_url_len == 0) ||
     			    (sizeof(iremote) + iremote.name_len +
    -			    iremote.url_len) > datalen) {
    +			    iremote.fetch_url_len + iremote.send_url_len) >
    +			    datalen) {
     				err = got_error(GOT_ERR_PRIVSEP_LEN);
     				break;
     			}
    @@ -2495,26 +2513,35 @@ got_privsep_recv_gotconfig_remotes(struct got_remote_r
     				err = got_error_from_errno("strndup");
     				break;
     			}
    -			remote->url = strndup(imsg.data + sizeof(iremote) +
    -			    iremote.name_len, iremote.url_len);
    -			if (remote->url == NULL) {
    +			remote->fetch_url = strndup(imsg.data +
    +			    sizeof(iremote) + iremote.name_len,
    +			    iremote.fetch_url_len);
    +			if (remote->fetch_url == NULL) {
     				err = got_error_from_errno("strndup");
     				free_remote_data(remote);
     				break;
     			}
    +			remote->send_url = strndup(imsg.data +
    +			    sizeof(iremote) + iremote.name_len +
    +			    iremote.fetch_url_len, iremote.send_url_len);
    +			if (remote->send_url == NULL) {
    +				err = got_error_from_errno("strndup");
    +				free_remote_data(remote);
    +				break;
    +			}
     			remote->mirror_references = iremote.mirror_references;
     			remote->fetch_all_branches = iremote.fetch_all_branches;
    -			if (iremote.nbranches > 0) {
    -				remote->branches = recallocarray(NULL, 0,
    -				    iremote.nbranches, sizeof(char *));
    -				if (remote->branches == NULL) {
    +			if (iremote.nfetch_branches > 0) {
    +				remote->fetch_branches = recallocarray(NULL, 0,
    +				    iremote.nfetch_branches, sizeof(char *));
    +				if (remote->fetch_branches == NULL) {
     					err = got_error_from_errno("calloc");
     					free_remote_data(remote);
     					break;
     				}
     			}
    -			remote->nbranches = 0;
    -			for (i = 0; i < iremote.nbranches; i++) {
    +			remote->nfetch_branches = 0;
    +			for (i = 0; i < iremote.nfetch_branches; i++) {
     				char *branch;
     				err = got_privsep_recv_gotconfig_str(&branch,
     				    ibuf);
    @@ -2522,20 +2549,41 @@ got_privsep_recv_gotconfig_remotes(struct got_remote_r
     					free_remote_data(remote);
     					goto done;
     				}
    -				remote->branches[i] = branch;
    -				remote->nbranches++;
    +				remote->fetch_branches[i] = branch;
    +				remote->nfetch_branches++;
     			}
    -			if (iremote.nrefs > 0) {
    -				remote->refs = recallocarray(NULL, 0,
    -				    iremote.nrefs, sizeof(char *));
    -				if (remote->refs == NULL) {
    +			if (iremote.nsend_branches > 0) {
    +				remote->send_branches = recallocarray(NULL, 0,
    +				    iremote.nsend_branches, sizeof(char *));
    +				if (remote->send_branches == NULL) {
     					err = got_error_from_errno("calloc");
     					free_remote_data(remote);
     					break;
     				}
     			}
    -			remote->nrefs = 0;
    -			for (i = 0; i < iremote.nrefs; i++) {
    +			remote->nsend_branches = 0;
    +			for (i = 0; i < iremote.nsend_branches; i++) {
    +				char *branch;
    +				err = got_privsep_recv_gotconfig_str(&branch,
    +				    ibuf);
    +				if (err) {
    +					free_remote_data(remote);
    +					goto done;
    +				}
    +				remote->send_branches[i] = branch;
    +				remote->nsend_branches++;
    +			}
    +			if (iremote.nfetch_refs > 0) {
    +				remote->fetch_refs = recallocarray(NULL, 0,
    +				    iremote.nfetch_refs, sizeof(char *));
    +				if (remote->fetch_refs == NULL) {
    +					err = got_error_from_errno("calloc");
    +					free_remote_data(remote);
    +					break;
    +				}
    +			}
    +			remote->nfetch_refs = 0;
    +			for (i = 0; i < iremote.nfetch_refs; i++) {
     				char *ref;
     				err = got_privsep_recv_gotconfig_str(&ref,
     				    ibuf);
    @@ -2543,8 +2591,8 @@ got_privsep_recv_gotconfig_remotes(struct got_remote_r
     					free_remote_data(remote);
     					goto done;
     				}
    -				remote->refs[i] = ref;
    -				remote->nrefs++;
    +				remote->fetch_refs[i] = ref;
    +				remote->nfetch_refs++;
     			}
     			(*nremotes)++;
     			break;
    blob - 3d857e3a7784f358729bd0b93b349fa77eaa1c10
    blob + deb44a1116a2fcd589ff0e0db2fcc4a1c0e2c63d
    --- lib/repository.c
    +++ lib/repository.c
    @@ -786,13 +786,20 @@ got_repo_free_remote_repo_data(struct got_remote_repo 
     
     	free(repo->name);
     	repo->name = NULL;
    -	free(repo->url);
    -	repo->url = NULL;
    -	for (i = 0; i < repo->nbranches; i++)
    -		free(repo->branches[i]);
    -	free(repo->branches);
    -	repo->branches = NULL;
    -	repo->nbranches = 0;
    +	free(repo->fetch_url);
    +	repo->fetch_url = NULL;
    +	free(repo->send_url);
    +	repo->send_url = NULL;
    +	for (i = 0; i < repo->nfetch_branches; i++)
    +		free(repo->fetch_branches[i]);
    +	free(repo->fetch_branches);
    +	repo->fetch_branches = NULL;
    +	repo->nfetch_branches = 0;
    +	for (i = 0; i < repo->nsend_branches; i++)
    +		free(repo->send_branches[i]);
    +	free(repo->send_branches);
    +	repo->send_branches = NULL;
    +	repo->nsend_branches = 0;
     }
     
     const struct got_error *
    blob - eadef712009b2da9a414361c8395ccbd43f2b1aa
    blob + 3ce93f09be615d41528eb6af7e5f5a1bd77ce92f
    --- libexec/got-read-gitconfig/got-read-gitconfig.c
    +++ libexec/got-read-gitconfig/got-read-gitconfig.c
    @@ -121,8 +121,10 @@ send_gitconfig_remotes(struct imsgbuf *ibuf, struct go
     		iremote.mirror_references = remotes[i].mirror_references;
     		iremote.name_len = strlen(remotes[i].name);
     		len += iremote.name_len;
    -		iremote.url_len = strlen(remotes[i].url);
    -		len += iremote.url_len;
    +		iremote.fetch_url_len = strlen(remotes[i].fetch_url);
    +		len += iremote.fetch_url_len;
    +		iremote.send_url_len = strlen(remotes[i].send_url);
    +		len += iremote.send_url_len;
     
     		wbuf = imsg_create(ibuf, GOT_IMSG_GITCONFIG_REMOTE, 0, 0, len);
     		if (wbuf == NULL)
    @@ -142,12 +144,18 @@ send_gitconfig_remotes(struct imsgbuf *ibuf, struct go
     			ibuf_free(wbuf);
     			return err;
     		}
    -		if (imsg_add(wbuf, remotes[i].url, iremote.url_len) == -1) {
    +		if (imsg_add(wbuf, remotes[i].fetch_url, iremote.fetch_url_len) == -1) {
     			err = got_error_from_errno(
     			    "imsg_add GITCONFIG_REMOTE");
     			ibuf_free(wbuf);
     			return err;
     		}
    +		if (imsg_add(wbuf, remotes[i].send_url, iremote.send_url_len) == -1) {
    +			err = got_error_from_errno(
    +			    "imsg_add GITCONFIG_REMOTE");
    +			ibuf_free(wbuf);
    +			return err;
    +		}
     
     		wbuf->fd = -1;
     		imsg_close(ibuf, wbuf);
    @@ -218,13 +226,23 @@ gitconfig_remotes_request(struct imsgbuf *ibuf, struct
     			*end = '\0';
     		remotes[i].name = name;
     
    -		remotes[i].url = got_gitconfig_get_str(gitconfig,
    +		remotes[i].fetch_url = got_gitconfig_get_str(gitconfig,
     		    node->field, "url");
    -		if (remotes[i].url == NULL) {
    +		if (remotes[i].fetch_url == NULL) {
     			err = got_error(GOT_ERR_GITCONFIG_SYNTAX);
     			goto done;
     		}
     
    +		remotes[i].send_url = got_gitconfig_get_str(gitconfig,
    +		    node->field, "pushurl");
    +		if (remotes[i].send_url == NULL)
    +			remotes[i].send_url = got_gitconfig_get_str(gitconfig,
    +			    node->field, "url");
    +		if (remotes[i].send_url == NULL) {
    +			err = got_error(GOT_ERR_GITCONFIG_SYNTAX);
    +			goto done;
    +		}
    +
     		remotes[i].mirror_references = 0;
     		mirror = got_gitconfig_get_str(gitconfig, node->field,
     		    "mirror");
    blob - d2b3b15299aad1d02917abf96a7496072e5c4af3
    blob + 87b6e325e90f1a5df5293fea97f21d0e0ec8ecdd
    --- libexec/got-read-gotconfig/got-read-gotconfig.c
    +++ libexec/got-read-gotconfig/got-read-gotconfig.c
    @@ -51,28 +51,48 @@ catch_sigint(int signo)
     }
     
     static const struct got_error *
    -make_repo_url(char **url, struct gotconfig_remote_repo *repo)
    +make_fetch_url(char **url, struct gotconfig_remote_repo *repo)
     {
     	const struct got_error *err = NULL;
     	char *s = NULL, *p = NULL;
    +	const char *protocol, *server, *repo_path;
    +	int port;
     
     	*url = NULL;
     
    -	if (asprintf(&s, "%s://", repo->protocol) == -1)
    +	if (repo->fetch_config && repo->fetch_config->protocol)
    +		protocol = repo->fetch_config->protocol;
    +	else
    +		protocol = repo->protocol;
    +	if (protocol == NULL)
    +		return got_error_fmt(GOT_ERR_PARSE_CONFIG,
    +		    "fetch protocol required for remote repository \"%s\"",
    +		    repo->name);
    +	if (asprintf(&s, "%s://", protocol) == -1)
     		return got_error_from_errno("asprintf");
     
    -	if (repo->server) {
    -		p = s;
    -		s = NULL;
    -		if (asprintf(&s, "%s%s", p, repo->server) == -1) {
    -			err = got_error_from_errno("asprintf");
    -			goto done;
    -		}
    -		free(p);
    -		p = NULL;
    +	if (repo->fetch_config && repo->fetch_config->server)
    +		server = repo->fetch_config->server;
    +	else
    +		server = repo->server;
    +	if (server == NULL)
    +		return got_error_fmt(GOT_ERR_PARSE_CONFIG,
    +		    "fetch server required for remote repository \"%s\"",
    +		    repo->name);
    +	p = s;
    +	s = NULL;
    +	if (asprintf(&s, "%s%s", p, server) == -1) {
    +		err = got_error_from_errno("asprintf");
    +		goto done;
     	}
    +	free(p);
    +	p = NULL;
     
    -	if (repo->port) {
    +	if (repo->fetch_config && repo->fetch_config->server)
    +		port = repo->fetch_config->port;
    +	else
    +		port = repo->port;
    +	if (port) {
     		p = s;
     		s = NULL;
     		if (asprintf(&s, "%s:%d", p, repo->port) == -1) {
    @@ -83,13 +103,82 @@ make_repo_url(char **url, struct gotconfig_remote_repo
     		p = NULL;
     	}
     
    -	if (repo->repository) {
    -		char *repo_path = repo->repository;
    -		while (repo_path[0] == '/')
    -			repo_path++;
    +	if (repo->fetch_config && repo->fetch_config->repository)
    +		repo_path = repo->fetch_config->repository;
    +	else
    +		repo_path = repo->repository;
    +	if (repo_path == NULL)
    +		return got_error_fmt(GOT_ERR_PARSE_CONFIG,
    +		    "fetch repository path required for remote "
    +		    "repository \"%s\"", repo->name);
    +
    +	while (repo_path[0] == '/')
    +		repo_path++;
    +	p = s;
    +	s = NULL;
    +	if (asprintf(&s, "%s/%s", p, repo_path) == -1) {
    +		err = got_error_from_errno("asprintf");
    +		goto done;
    +	}
    +	free(p);
    +	p = NULL;
    +
    +	got_path_strip_trailing_slashes(s);
    +done:
    +	if (err) {
    +		free(s);
    +		free(p);
    +	} else
    +		*url = s;
    +	return err;
    +}
    +
    +static const struct got_error *
    +make_send_url(char **url, struct gotconfig_remote_repo *repo)
    +{
    +	const struct got_error *err = NULL;
    +	char *s = NULL, *p = NULL;
    +	const char *protocol, *server, *repo_path;
    +	int port;
    +
    +	*url = NULL;
    +
    +	if (repo->send_config && repo->send_config->protocol)
    +		protocol = repo->send_config->protocol;
    +	else
    +		protocol = repo->protocol;
    +	if (protocol == NULL)
    +		return got_error_fmt(GOT_ERR_PARSE_CONFIG,
    +		    "send protocol required for remote repository \"%s\"",
    +		    repo->name);
    +	if (asprintf(&s, "%s://", protocol) == -1)
    +		return got_error_from_errno("asprintf");
    +
    +	if (repo->send_config && repo->send_config->server)
    +		server = repo->send_config->server;
    +	else
    +		server = repo->server;
    +	if (server == NULL)
    +		return got_error_fmt(GOT_ERR_PARSE_CONFIG,
    +		    "send server required for remote repository \"%s\"",
    +		    repo->name);
    +	p = s;
    +	s = NULL;
    +	if (asprintf(&s, "%s%s", p, server) == -1) {
    +		err = got_error_from_errno("asprintf");
    +		goto done;
    +	}
    +	free(p);
    +	p = NULL;
    +
    +	if (repo->send_config && repo->send_config->server)
    +		port = repo->send_config->port;
    +	else
    +		port = repo->port;
    +	if (port) {
     		p = s;
     		s = NULL;
    -		if (asprintf(&s, "%s/%s", p, repo_path) == -1) {
    +		if (asprintf(&s, "%s:%d", p, repo->port) == -1) {
     			err = got_error_from_errno("asprintf");
     			goto done;
     		}
    @@ -97,6 +186,26 @@ make_repo_url(char **url, struct gotconfig_remote_repo
     		p = NULL;
     	}
     
    +	if (repo->send_config && repo->send_config->repository)
    +		repo_path = repo->send_config->repository;
    +	else
    +		repo_path = repo->repository;
    +	if (repo_path == NULL)
    +		return got_error_fmt(GOT_ERR_PARSE_CONFIG,
    +		    "send repository path required for remote "
    +		    "repository \"%s\"", repo->name);
    +
    +	while (repo_path[0] == '/')
    +		repo_path++;
    +	p = s;
    +	s = NULL;
    +	if (asprintf(&s, "%s/%s", p, repo_path) == -1) {
    +		err = got_error_from_errno("asprintf");
    +		goto done;
    +	}
    +	free(p);
    +	p = NULL;
    +
     	got_path_strip_trailing_slashes(s);
     done:
     	if (err) {
    @@ -126,7 +235,7 @@ send_gotconfig_remotes(struct imsgbuf *ibuf,
     	const struct got_error *err = NULL;
     	struct got_imsg_remotes iremotes;
     	struct gotconfig_remote_repo *repo;
    -	char *url = NULL;
    +	char *fetch_url = NULL, *send_url = NULL;
     
     	iremotes.nremotes = nremotes;
     	if (imsg_compose(ibuf, GOT_IMSG_GOTCONFIG_REMOTES, 0, 0, -1,
    @@ -144,34 +253,63 @@ send_gotconfig_remotes(struct imsgbuf *ibuf,
     		struct ibuf *wbuf;
     		struct node_branch *branch;
     		struct node_ref *ref;
    -		int nbranches = 0, nrefs = 0;
    +		int nfetch_branches = 0, nsend_branches = 0, nfetch_refs = 0;
     
    -		branch = repo->branch;
    -		while (branch) {
    -			branch = branch->next;
    -			nbranches++;
    +		if (repo->fetch_config && repo->fetch_config->branch) {
    +			branch = repo->fetch_config->branch;
    +			while (branch) {
    +				branch = branch->next;
    +				nfetch_branches++;
    +			}
    +		} else {
    +			branch = repo->branch;
    +			while (branch) {
    +				branch = branch->next;
    +				nfetch_branches++;
    +			}
     		}
     
    -		ref = repo->ref;
    +		if (repo->send_config && repo->send_config->branch) {
    +			branch = repo->send_config->branch;
    +			while (branch) {
    +				branch = branch->next;
    +				nsend_branches++;
    +			}
    +		} else {
    +			branch = repo->branch;
    +			while (branch) {
    +				branch = branch->next;
    +				nsend_branches++;
    +			}
    +		}
    +
    +		ref = repo->fetch_ref;
     		while (ref) {
     			ref = ref->next;
    -			nrefs++;
    +			nfetch_refs++;
     		}
     
    -		iremote.nbranches = nbranches;
    -		iremote.nrefs = nrefs;
    +		iremote.nfetch_branches = nfetch_branches;
    +		iremote.nsend_branches = nsend_branches;
    +		iremote.nfetch_refs = nfetch_refs;
     		iremote.mirror_references = repo->mirror_references;
     		iremote.fetch_all_branches = repo->fetch_all_branches;
     
     		iremote.name_len = strlen(repo->name);
     		len += iremote.name_len;
     
    -		err = make_repo_url(&url, repo);
    +		err = make_fetch_url(&fetch_url, repo);
     		if (err)
     			break;
    -		iremote.url_len = strlen(url);
    -		len += iremote.url_len;
    +		iremote.fetch_url_len = strlen(fetch_url);
    +		len += iremote.fetch_url_len;
     
    +		err = make_send_url(&send_url, repo);
    +		if (err)
    +			break;
    +		iremote.send_url_len = strlen(send_url);
    +		len += iremote.send_url_len;
    +
     		wbuf = imsg_create(ibuf, GOT_IMSG_GOTCONFIG_REMOTE, 0, 0, len);
     		if (wbuf == NULL) {
     			err = got_error_from_errno(
    @@ -192,12 +330,18 @@ send_gotconfig_remotes(struct imsgbuf *ibuf,
     			ibuf_free(wbuf);
     			break;
     		}
    -		if (imsg_add(wbuf, url, iremote.url_len) == -1) {
    +		if (imsg_add(wbuf, fetch_url, iremote.fetch_url_len) == -1) {
     			err = got_error_from_errno(
     			    "imsg_add GOTCONFIG_REMOTE");
     			ibuf_free(wbuf);
     			break;
     		}
    +		if (imsg_add(wbuf, send_url, iremote.send_url_len) == -1) {
    +			err = got_error_from_errno(
    +			    "imsg_add GOTCONFIG_REMOTE");
    +			ibuf_free(wbuf);
    +			break;
    +		}
     
     		wbuf->fd = -1;
     		imsg_close(ibuf, wbuf);
    @@ -205,18 +349,52 @@ send_gotconfig_remotes(struct imsgbuf *ibuf,
     		if (err)
     			break;
     
    -		free(url);
    -		url = NULL;
    +		free(fetch_url);
    +		fetch_url = NULL;
    +		free(send_url);
    +		send_url = NULL;
     
    -		branch = repo->branch;
    -		while (branch) {
    -			err = send_gotconfig_str(ibuf, branch->branch_name);
    -			if (err)
    -				break;
    -			branch = branch->next;
    +		if (repo->fetch_config && repo->fetch_config->branch) {
    +			branch = repo->fetch_config->branch;
    +			while (branch) {
    +				err = send_gotconfig_str(ibuf,
    +				    branch->branch_name);
    +				if (err)
    +					break;
    +				branch = branch->next;
    +			}
    +		} else {
    +			branch = repo->branch;
    +			while (branch) {
    +				err = send_gotconfig_str(ibuf,
    +				    branch->branch_name);
    +				if (err)
    +					break;
    +				branch = branch->next;
    +			}
     		}
     
    -		ref = repo->ref;
    +		if (repo->send_config && repo->send_config->branch) {
    +			branch = repo->send_config->branch;
    +			while (branch) {
    +				err = send_gotconfig_str(ibuf,
    +				    branch->branch_name);
    +				if (err)
    +					break;
    +				branch = branch->next;
    +			}
    +		} else {
    +			branch = repo->branch;
    +			while (branch) {
    +				err = send_gotconfig_str(ibuf,
    +				    branch->branch_name);
    +				if (err)
    +					break;
    +				branch = branch->next;
    +			}
    +		}
    +
    +		ref = repo->fetch_ref;
     		while (ref) {
     			err = send_gotconfig_str(ibuf, ref->ref_name);
     			if (err)
    @@ -225,7 +403,8 @@ send_gotconfig_remotes(struct imsgbuf *ibuf,
     		}
     	}
     
    -	free(url);
    +	free(fetch_url);
    +	free(send_url);
     	return err;
     }
     
    blob - 1ce83bbcfa6c33daba6da2f078860f3a41659d38
    blob + 1ce499222101a45de399bd433825c767df869d91
    --- libexec/got-read-gotconfig/gotconfig.h
    +++ libexec/got-read-gotconfig/gotconfig.h
    @@ -56,7 +56,7 @@ struct gotconfig_remote_repo {
     	int	mirror_references;
     	int	fetch_all_branches;
     	struct	node_branch *branch;
    -	struct	node_ref *ref;
    +	struct	node_ref *fetch_ref;
     	struct	fetch_config *fetch_config;
     	struct	send_config *send_config;
     };
    blob - 842552aef596457405a11618bb92b1586d2d094c
    blob + db321a515c3fb30904d91781a67ae7dd6d1b462c
    --- libexec/got-read-gotconfig/parse.y
    +++ libexec/got-read-gotconfig/parse.y
    @@ -231,7 +231,7 @@ remoteopts1	: REPOSITORY STRING {
     			remote->branch = $2;
     		}
     		| REFERENCE ref {
    -			remote->ref = $2;
    +			remote->fetch_ref = $2;
     		}
     		| FETCH {
     			static const struct got_error* error;
    blob - b7603c982431e0c17ae7aeb27a58df3481304608
    blob + 2c2112b0a45b7a3317cd276452aa270633f87995
    --- regress/cmdline/send.sh
    +++ regress/cmdline/send.sh
    @@ -1041,7 +1041,123 @@ EOF
     	test_done "$testroot" "$ret"
     }
     
    +test_send_and_fetch_config() {
    +	local testroot=`test_init send_fetch_conf`
    +	local testurl=ssh://127.0.0.1/$testroot
    +	local commit_id=`git_show_head $testroot/repo`
     
    +	got clone -q $testurl/repo $testroot/repo-clone
    +	ret="$?"
    +	if [ "$ret" != "0" ]; then
    +		echo "got clone command failed unexpectedly" >&2
    +		test_done "$testroot" "$ret"
    +		return 1
    +	fi
    +
    +	got tag -r $testroot/repo -m '1.0' 1.0 >/dev/null
    +	tag_id=`got ref -r $testroot/repo -l | grep "^refs/tags/1.0" \
    +		| tr -d ' ' | cut -d: -f2`
    +
    +	cp -R $testroot/repo-clone $testroot/repo-clone2
    +	got tag -r $testroot/repo-clone2 -m '2.0' 2.0 >/dev/null
    +	tag_id2=`got ref -r $testroot/repo-clone2 -l | grep "^refs/tags/2.0" \
    +		| tr -d ' ' | cut -d: -f2`
    +
    +	cat > $testroot/repo/.git/got.conf <<EOF
    +remote "origin" {
    +	protocol ssh
    +	server 127.0.0.1
    +	send {
    +		repository "$testroot/repo-clone"
    +	}
    +	fetch {
    +		repository "$testroot/repo-clone2"
    +	}
    +}
    +EOF
    +	got ref -l -r $testroot/repo > $testroot/stdout
    +	if [ "$ret" != "0" ]; then
    +		echo "got ref command failed unexpectedly" >&2
    +		test_done "$testroot" "$ret"
    +		return 1
    +	fi
    +
    +	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
    +	echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected
    +	echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected
    +	cmp -s $testroot/stdout $testroot/stdout.expected
    +	ret="$?"
    +	if [ "$ret" != "0" ]; then
    +		diff -u $testroot/stdout.expected $testroot/stdout
    +		test_done "$testroot" "$ret"
    +		return 1
    +	fi
    +
    +	# fetch tag 2.0 from repo-clone2
    +	got fetch -q -r $testroot/repo > $testroot/stdout
    +	ret="$?"
    +	if [ "$ret" != "0" ]; then
    +		echo "got fetch command failed unexpectedly" >&2
    +		test_done "$testroot" "$ret"
    +		return 1
    +	fi
    +
    +	got ref -l -r $testroot/repo > $testroot/stdout
    +	if [ "$ret" != "0" ]; then
    +		echo "got ref command failed unexpectedly" >&2
    +		test_done "$testroot" "$ret"
    +		return 1
    +	fi
    +
    +	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
    +	echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected
    +	echo "refs/remotes/origin/HEAD: refs/remotes/origin/master" \
    +		>> $testroot/stdout.expected
    +	echo "refs/remotes/origin/master: $commit_id" \
    +		>> $testroot/stdout.expected
    +	echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected
    +	echo "refs/tags/2.0: $tag_id2" >> $testroot/stdout.expected
    +	cmp -s $testroot/stdout $testroot/stdout.expected
    +	ret="$?"
    +	if [ "$ret" != "0" ]; then
    +		diff -u $testroot/stdout.expected $testroot/stdout
    +		test_done "$testroot" "$ret"
    +		return 1
    +	fi
    +
    +	# send tag 1.0 to repo-clone
    +	got send -q -r $testroot/repo -t 1.0 > $testroot/stdout
    +	ret="$?"
    +	if [ "$ret" != "0" ]; then
    +		echo "got send command failed unexpectedly" >&2
    +		test_done "$testroot" "$ret"
    +		return 1
    +	fi
    +	
    +	got ref -l -r $testroot/repo-clone > $testroot/stdout
    +	if [ "$ret" != "0" ]; then
    +		echo "got ref command failed unexpectedly" >&2
    +		test_done "$testroot" "$ret"
    +		return 1
    +	fi
    +
    +	echo "HEAD: refs/heads/master" > $testroot/stdout.expected
    +	echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected
    +	echo "refs/remotes/origin/HEAD: refs/remotes/origin/master" \
    +		>> $testroot/stdout.expected
    +	echo "refs/remotes/origin/master: $commit_id" \
    +		>> $testroot/stdout.expected
    +	echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected
    +
    +	cmp -s $testroot/stdout $testroot/stdout.expected
    +	ret="$?"
    +	if [ "$ret" != "0" ]; then
    +		diff -u $testroot/stdout.expected $testroot/stdout
    +	fi
    +
    +	test_done "$testroot" "$ret"
    +}
    +
     test_parseargs "$@"
     run_test test_send_basic
     run_test test_send_rebase_required
    @@ -1052,3 +1168,4 @@ run_test test_send_tags
     run_test test_send_new_branch
     run_test test_send_all_branches
     run_test test_send_to_empty_repo
    +run_test test_send_and_fetch_config
    
    
    
  • Stefan Sperling:

    separate send and fetch config blocks