Push code for transport library
This moves the code to call push backends into a library that can be extended to make matching fetch and push decisions based on the URL it gets, and which could be changed to have built-in implementations instead of calling external programs. Signed-off-by: Daniel Barkalow <barkalow@iabervon.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									2d4177c01c
								
							
						
					
					
						commit
						9b288516ee
					
				
							
								
								
									
										3
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										3
									
								
								Makefile
								
								
								
								
							|  | @ -310,7 +310,8 @@ LIB_OBJS = \ | |||
| 	write_or_die.o trace.o list-objects.o grep.o match-trees.o \ | ||||
| 	alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \ | ||||
| 	color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \ | ||||
| 	convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o | ||||
| 	convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \ | ||||
| 	transport.o | ||||
|  | ||||
| BUILTIN_OBJS = \ | ||||
| 	builtin-add.o \ | ||||
|  |  | |||
|  | @ -6,10 +6,11 @@ | |||
| #include "run-command.h" | ||||
| #include "builtin.h" | ||||
| #include "remote.h" | ||||
| #include "transport.h" | ||||
|  | ||||
| static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]"; | ||||
|  | ||||
| static int all, force, thin, verbose; | ||||
| static int all, thin, verbose; | ||||
| static const char *receivepack; | ||||
|  | ||||
| static const char **refspec; | ||||
|  | @ -43,80 +44,38 @@ static void set_refspecs(const char **refs, int nr) | |||
| 	} | ||||
| } | ||||
|  | ||||
| static int do_push(const char *repo) | ||||
| static int do_push(const char *repo, int flags) | ||||
| { | ||||
| 	int i, errs; | ||||
| 	int common_argc; | ||||
| 	const char **argv; | ||||
| 	int argc; | ||||
| 	struct remote *remote = remote_get(repo); | ||||
|  | ||||
| 	if (!remote) | ||||
| 		die("bad repository '%s'", repo); | ||||
|  | ||||
| 	if (remote->receivepack) { | ||||
| 		char *rp = xmalloc(strlen(remote->receivepack) + 16); | ||||
| 		sprintf(rp, "--receive-pack=%s", remote->receivepack); | ||||
| 		receivepack = rp; | ||||
| 	} | ||||
| 	if (!refspec && !all && remote->push_refspec_nr) { | ||||
| 		refspec = remote->push_refspec; | ||||
| 		refspec_nr = remote->push_refspec_nr; | ||||
| 	} | ||||
|  | ||||
| 	argv = xmalloc((refspec_nr + 10) * sizeof(char *)); | ||||
| 	argv[0] = "dummy-send-pack"; | ||||
| 	argc = 1; | ||||
| 	if (all) | ||||
| 		argv[argc++] = "--all"; | ||||
| 	if (force) | ||||
| 		argv[argc++] = "--force"; | ||||
| 	if (receivepack) | ||||
| 		argv[argc++] = receivepack; | ||||
| 	common_argc = argc; | ||||
|  | ||||
| 	errs = 0; | ||||
| 	for (i = 0; i < remote->uri_nr; i++) { | ||||
| 		struct transport *transport = | ||||
| 			transport_get(remote, remote->uri[i], 0); | ||||
| 		int err; | ||||
| 		int dest_argc = common_argc; | ||||
| 		int dest_refspec_nr = refspec_nr; | ||||
| 		const char **dest_refspec = refspec; | ||||
| 		const char *dest = remote->uri[i]; | ||||
| 		const char *sender = "send-pack"; | ||||
| 		if (!prefixcmp(dest, "http://") || | ||||
| 		    !prefixcmp(dest, "https://")) | ||||
| 			sender = "http-push"; | ||||
| 		else { | ||||
| 			char *rem = xmalloc(strlen(remote->name) + 10); | ||||
| 			sprintf(rem, "--remote=%s", remote->name); | ||||
| 			argv[dest_argc++] = rem; | ||||
| 			if (thin) | ||||
| 				argv[dest_argc++] = "--thin"; | ||||
| 		} | ||||
| 		argv[0] = sender; | ||||
| 		argv[dest_argc++] = dest; | ||||
| 		while (dest_refspec_nr--) | ||||
| 			argv[dest_argc++] = *dest_refspec++; | ||||
| 		argv[dest_argc] = NULL; | ||||
| 		if (receivepack) | ||||
| 			transport_set_option(transport, | ||||
| 					     TRANS_OPT_RECEIVEPACK, receivepack); | ||||
| 		if (thin) | ||||
| 			transport_set_option(transport, TRANS_OPT_THIN, "yes"); | ||||
|  | ||||
| 		if (verbose) | ||||
| 			fprintf(stderr, "Pushing to %s\n", dest); | ||||
| 		err = run_command_v_opt(argv, RUN_GIT_CMD); | ||||
| 			fprintf(stderr, "Pushing to %s\n", remote->uri[i]); | ||||
| 		err = transport_push(transport, refspec_nr, refspec, flags); | ||||
| 		err |= transport_disconnect(transport); | ||||
|  | ||||
| 		if (!err) | ||||
| 			continue; | ||||
|  | ||||
| 		error("failed to push to '%s'", remote->uri[i]); | ||||
| 		switch (err) { | ||||
| 		case -ERR_RUN_COMMAND_FORK: | ||||
| 			error("unable to fork for %s", sender); | ||||
| 		case -ERR_RUN_COMMAND_EXEC: | ||||
| 			error("unable to exec %s", sender); | ||||
| 			break; | ||||
| 		case -ERR_RUN_COMMAND_WAITPID: | ||||
| 		case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: | ||||
| 		case -ERR_RUN_COMMAND_WAITPID_SIGNAL: | ||||
| 		case -ERR_RUN_COMMAND_WAITPID_NOEXIT: | ||||
| 			error("%s died with strange error", sender); | ||||
| 		} | ||||
| 		errs++; | ||||
| 	} | ||||
| 	return !!errs; | ||||
|  | @ -125,6 +84,7 @@ static int do_push(const char *repo) | |||
| int cmd_push(int argc, const char **argv, const char *prefix) | ||||
| { | ||||
| 	int i; | ||||
| 	int flags = 0; | ||||
| 	const char *repo = NULL;	/* default repository */ | ||||
|  | ||||
| 	for (i = 1; i < argc; i++) { | ||||
|  | @ -144,7 +104,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) | |||
| 			continue; | ||||
| 		} | ||||
| 		if (!strcmp(arg, "--all")) { | ||||
| 			all = 1; | ||||
| 			flags |= TRANSPORT_PUSH_ALL; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (!strcmp(arg, "--tags")) { | ||||
|  | @ -152,7 +112,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) | |||
| 			continue; | ||||
| 		} | ||||
| 		if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) { | ||||
| 			force = 1; | ||||
| 			flags |= TRANSPORT_PUSH_FORCE; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (!strcmp(arg, "--thin")) { | ||||
|  | @ -164,11 +124,11 @@ int cmd_push(int argc, const char **argv, const char *prefix) | |||
| 			continue; | ||||
| 		} | ||||
| 		if (!prefixcmp(arg, "--receive-pack=")) { | ||||
| 			receivepack = arg; | ||||
| 			receivepack = arg + 15; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (!prefixcmp(arg, "--exec=")) { | ||||
| 			receivepack = arg; | ||||
| 			receivepack = arg + 7; | ||||
| 			continue; | ||||
| 		} | ||||
| 		usage(push_usage); | ||||
|  | @ -177,5 +137,5 @@ int cmd_push(int argc, const char **argv, const char *prefix) | |||
| 	if (all && refspec) | ||||
| 		usage(push_usage); | ||||
|  | ||||
| 	return do_push(repo); | ||||
| 	return do_push(repo, flags); | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,196 @@ | |||
| #include "cache.h" | ||||
| #include "transport.h" | ||||
| #include "run-command.h" | ||||
|  | ||||
| static const struct transport_ops rsync_transport; | ||||
|  | ||||
| static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { | ||||
| 	const char **argv; | ||||
| 	int argc; | ||||
| 	int err; | ||||
|  | ||||
| 	argv = xmalloc((refspec_nr + 11) * sizeof(char *)); | ||||
| 	argv[0] = "http-push"; | ||||
| 	argc = 1; | ||||
| 	if (flags & TRANSPORT_PUSH_ALL) | ||||
| 		argv[argc++] = "--all"; | ||||
| 	if (flags & TRANSPORT_PUSH_FORCE) | ||||
| 		argv[argc++] = "--force"; | ||||
| 	argv[argc++] = transport->url; | ||||
| 	while (refspec_nr--) | ||||
| 		argv[argc++] = *refspec++; | ||||
| 	argv[argc] = NULL; | ||||
| 	err = run_command_v_opt(argv, RUN_GIT_CMD); | ||||
| 	switch (err) { | ||||
| 	case -ERR_RUN_COMMAND_FORK: | ||||
| 		error("unable to fork for %s", argv[0]); | ||||
| 	case -ERR_RUN_COMMAND_EXEC: | ||||
| 		error("unable to exec %s", argv[0]); | ||||
| 		break; | ||||
| 	case -ERR_RUN_COMMAND_WAITPID: | ||||
| 	case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: | ||||
| 	case -ERR_RUN_COMMAND_WAITPID_SIGNAL: | ||||
| 	case -ERR_RUN_COMMAND_WAITPID_NOEXIT: | ||||
| 		error("%s died with strange error", argv[0]); | ||||
| 	} | ||||
| 	return !!err; | ||||
| } | ||||
|  | ||||
| static const struct transport_ops curl_transport = { | ||||
| 	/* set_option */	NULL, | ||||
| 	/* push */		curl_transport_push | ||||
| }; | ||||
|  | ||||
| static const struct transport_ops bundle_transport = { | ||||
| }; | ||||
|  | ||||
| struct git_transport_data { | ||||
| 	unsigned thin : 1; | ||||
|  | ||||
| 	const char *receivepack; | ||||
| }; | ||||
|  | ||||
| static int set_git_option(struct transport *connection, | ||||
| 			  const char *name, const char *value) | ||||
| { | ||||
| 	struct git_transport_data *data = connection->data; | ||||
| 	if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) { | ||||
| 		data->receivepack = value; | ||||
| 		return 0; | ||||
| 	} else if (!strcmp(name, TRANS_OPT_THIN)) { | ||||
| 		data->thin = !!value; | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { | ||||
| 	struct git_transport_data *data = transport->data; | ||||
| 	const char **argv; | ||||
| 	char *rem; | ||||
| 	int argc; | ||||
| 	int err; | ||||
|  | ||||
| 	argv = xmalloc((refspec_nr + 11) * sizeof(char *)); | ||||
| 	argv[0] = "send-pack"; | ||||
| 	argc = 1; | ||||
| 	if (flags & TRANSPORT_PUSH_ALL) | ||||
| 		argv[argc++] = "--all"; | ||||
| 	if (flags & TRANSPORT_PUSH_FORCE) | ||||
| 		argv[argc++] = "--force"; | ||||
| 	if (data->receivepack) { | ||||
| 		char *rp = xmalloc(strlen(data->receivepack) + 16); | ||||
| 		sprintf(rp, "--receive-pack=%s", data->receivepack); | ||||
| 		argv[argc++] = rp; | ||||
| 	} | ||||
| 	if (data->thin) | ||||
| 		argv[argc++] = "--thin"; | ||||
| 	rem = xmalloc(strlen(transport->remote->name) + 10); | ||||
| 	sprintf(rem, "--remote=%s", transport->remote->name); | ||||
| 	argv[argc++] = rem; | ||||
| 	argv[argc++] = transport->url; | ||||
| 	while (refspec_nr--) | ||||
| 		argv[argc++] = *refspec++; | ||||
| 	argv[argc] = NULL; | ||||
| 	err = run_command_v_opt(argv, RUN_GIT_CMD); | ||||
| 	switch (err) { | ||||
| 	case -ERR_RUN_COMMAND_FORK: | ||||
| 		error("unable to fork for %s", argv[0]); | ||||
| 	case -ERR_RUN_COMMAND_EXEC: | ||||
| 		error("unable to exec %s", argv[0]); | ||||
| 		break; | ||||
| 	case -ERR_RUN_COMMAND_WAITPID: | ||||
| 	case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: | ||||
| 	case -ERR_RUN_COMMAND_WAITPID_SIGNAL: | ||||
| 	case -ERR_RUN_COMMAND_WAITPID_NOEXIT: | ||||
| 		error("%s died with strange error", argv[0]); | ||||
| 	} | ||||
| 	return !!err; | ||||
| } | ||||
|  | ||||
| static const struct transport_ops git_transport = { | ||||
| 	/* set_option */	set_git_option, | ||||
| 	/* push */		git_transport_push | ||||
| }; | ||||
|  | ||||
| static int is_local(const char *url) | ||||
| { | ||||
| 	const char *colon = strchr(url, ':'); | ||||
| 	const char *slash = strchr(url, '/'); | ||||
| 	return !colon || (slash && slash < colon); | ||||
| } | ||||
|  | ||||
| static int is_file(const char *url) | ||||
| { | ||||
| 	struct stat buf; | ||||
| 	if (stat(url, &buf)) | ||||
| 		return 0; | ||||
| 	return S_ISREG(buf.st_mode); | ||||
| } | ||||
|  | ||||
| struct transport *transport_get(struct remote *remote, const char *url, | ||||
| 				int fetch) | ||||
| { | ||||
| 	struct transport *ret = NULL; | ||||
| 	if (!prefixcmp(url, "rsync://")) { | ||||
| 		ret = xmalloc(sizeof(*ret)); | ||||
| 		ret->data = NULL; | ||||
| 		ret->ops = &rsync_transport; | ||||
| 	} else if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://") || | ||||
| 		   !prefixcmp(url, "ftp://")) { | ||||
| 		ret = xmalloc(sizeof(*ret)); | ||||
| 		ret->ops = &curl_transport; | ||||
| 		ret->data = NULL; | ||||
| 	} else if (is_local(url) && is_file(url)) { | ||||
| 		ret = xmalloc(sizeof(*ret)); | ||||
| 		ret->data = NULL; | ||||
| 		ret->ops = &bundle_transport; | ||||
| 	} else { | ||||
| 		struct git_transport_data *data = xcalloc(1, sizeof(*data)); | ||||
| 		ret = xcalloc(1, sizeof(*ret)); | ||||
| 		ret->data = data; | ||||
| 		data->thin = 1; | ||||
| 		data->receivepack = "git-receive-pack"; | ||||
| 		if (remote && remote->receivepack) | ||||
| 			data->receivepack = remote->receivepack; | ||||
| 		ret->ops = &git_transport; | ||||
| 	} | ||||
| 	if (ret) { | ||||
| 		ret->remote = remote; | ||||
| 		ret->url = url; | ||||
| 		ret->fetch = !!fetch; | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int transport_set_option(struct transport *transport, | ||||
| 			 const char *name, const char *value) | ||||
| { | ||||
| 	int ret = 1; | ||||
| 	if (transport->ops->set_option) | ||||
| 		ret = transport->ops->set_option(transport, name, value); | ||||
| 	if (ret < 0) | ||||
| 		fprintf(stderr, "For '%s' option %s cannot be set to '%s'\n", | ||||
| 			transport->url, name, value); | ||||
| 	if (ret > 0) | ||||
| 		fprintf(stderr, "For '%s' option %s is ignored\n", | ||||
| 			transport->url, name); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int transport_push(struct transport *transport, | ||||
| 		   int refspec_nr, const char **refspec, int flags) | ||||
| { | ||||
| 	if (!transport->ops->push) | ||||
| 		return 1; | ||||
| 	return transport->ops->push(transport, refspec_nr, refspec, flags); | ||||
| } | ||||
|  | ||||
| int transport_disconnect(struct transport *transport) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	if (transport->ops->disconnect) | ||||
| 		ret = transport->ops->disconnect(transport); | ||||
| 	free(transport); | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -0,0 +1,61 @@ | |||
| #ifndef TRANSPORT_H | ||||
| #define TRANSPORT_H | ||||
|  | ||||
| #include "cache.h" | ||||
| #include "remote.h" | ||||
|  | ||||
| struct transport { | ||||
| 	unsigned verbose : 1; | ||||
| 	unsigned fetch : 1; | ||||
| 	struct remote *remote; | ||||
| 	const char *url; | ||||
|  | ||||
| 	void *data; | ||||
|  | ||||
| 	struct ref *remote_refs; | ||||
|  | ||||
| 	const struct transport_ops *ops; | ||||
| }; | ||||
|  | ||||
| #define TRANSPORT_PUSH_ALL 1 | ||||
| #define TRANSPORT_PUSH_FORCE 2 | ||||
|  | ||||
| struct transport_ops { | ||||
| 	/** | ||||
| 	 * Returns 0 if successful, positive if the option is not | ||||
| 	 * recognized or is inapplicable, and negative if the option | ||||
| 	 * is applicable but the value is invalid. | ||||
| 	 **/ | ||||
| 	int (*set_option)(struct transport *connection, const char *name, | ||||
| 			  const char *value); | ||||
|  | ||||
| 	int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags); | ||||
|  | ||||
| 	int (*disconnect)(struct transport *connection); | ||||
| }; | ||||
|  | ||||
| /* Returns a transport suitable for the url */ | ||||
| struct transport *transport_get(struct remote *remote, const char *url, | ||||
| 				int fetch); | ||||
|  | ||||
| /* Transport options which apply to git:// and scp-style URLs */ | ||||
|  | ||||
| /* The program to use on the remote side to receive a pack */ | ||||
| #define TRANS_OPT_RECEIVEPACK "receivepack" | ||||
|  | ||||
| /* Transfer the data as a thin pack if not null */ | ||||
| #define TRANS_OPT_THIN "thin" | ||||
|  | ||||
| /** | ||||
|  * Returns 0 if the option was used, non-zero otherwise. Prints a | ||||
|  * message to stderr if the option is not used. | ||||
|  **/ | ||||
| int transport_set_option(struct transport *transport, const char *name, | ||||
| 			 const char *value); | ||||
|  | ||||
| int transport_push(struct transport *connection, | ||||
| 		   int refspec_nr, const char **refspec, int flags); | ||||
|  | ||||
| int transport_disconnect(struct transport *transport); | ||||
|  | ||||
| #endif | ||||
		Loading…
	
		Reference in New Issue
	
	 Daniel Barkalow
						Daniel Barkalow