{"thread":{"id":"8941","subject":"[PATCH v2] Push code for transport library","startedAt":"2007-07-09T05:10:21Z","lastAt":"2007-07-09T05:10:21Z","messageCount":1,"participants":["Daniel Barkalow"],"isPatch":true,"patchVersion":2,"patchTotal":null},"messages":[{"id":"46826","messageId":"Pine.LNX.4.64.0707090106300.6977@iabervon.org","threadId":"8941","inReplyTo":null,"subject":"[PATCH v2] Push code for transport library","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2007-07-09T05:10:21Z","receivedAt":"2007-07-09T05:10:21Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"This moves the code to call push backends into a library that can be\nextended to make matching fetch and push decisions based on the URL it\ngets, and which could be changed to have built-in implementations\ninstead of calling external programs.\n\nSigned-off-by: Daniel Barkalow <barkalow@iabervon.org>\n---\nOkay, no more C99 initializers.\n\n Makefile       |    3 +-\n builtin-push.c |   82 ++++++-----------------\n transport.c    |  197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n transport.h    |   61 +++++++++++++++++\n 4 files changed, 281 insertions(+), 62 deletions(-)\n create mode 100644 transport.c\n create mode 100644 transport.h\n\ndiff --git a/Makefile b/Makefile\nindex 4ea5e45..b8f9af1 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -321,7 +321,8 @@ LIB_OBJS = \\\n \twrite_or_die.o trace.o list-objects.o grep.o match-trees.o \\\n \talloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \\\n \tcolor.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \\\n-\tconvert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o\n+\tconvert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \\\n+\ttransport.o\n \n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 2612f07..845d6fc 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -6,10 +6,11 @@\n #include \"run-command.h\"\n #include \"builtin.h\"\n #include \"remote.h\"\n+#include \"transport.h\"\n \n static const char push_usage[] = \"git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]\";\n \n-static int all, force, thin = 1, verbose;\n+static int all, thin = 1, verbose;\n static const char *receivepack;\n \n static const char **refspec;\n@@ -43,80 +44,38 @@ static void set_refspecs(const char **refs, int nr)\n \t}\n }\n \n-static int do_push(const char *repo)\n+static int do_push(const char *repo, int flags)\n {\n \tint i, errs;\n-\tint common_argc;\n-\tconst char **argv;\n-\tint argc;\n \tstruct remote *remote = remote_get(repo);\n \n \tif (!remote)\n \t\tdie(\"bad repository '%s'\", repo);\n \n-\tif (remote->receivepack) {\n-\t\tchar *rp = xmalloc(strlen(remote->receivepack) + 16);\n-\t\tsprintf(rp, \"--receive-pack=%s\", remote->receivepack);\n-\t\treceivepack = rp;\n-\t}\n \tif (!refspec && !all && remote->push_refspec_nr) {\n \t\trefspec = remote->push_refspec;\n \t\trefspec_nr = remote->push_refspec_nr;\n \t}\n-\n-\targv = xmalloc((refspec_nr + 10) * sizeof(char *));\n-\targv[0] = \"dummy-send-pack\";\n-\targc = 1;\n-\tif (all)\n-\t\targv[argc++] = \"--all\";\n-\tif (force)\n-\t\targv[argc++] = \"--force\";\n-\tif (receivepack)\n-\t\targv[argc++] = receivepack;\n-\tcommon_argc = argc;\n-\n \terrs = 0;\n \tfor (i = 0; i < remote->uri_nr; i++) {\n+\t\tstruct transport *transport =\n+\t\t\ttransport_get(remote, remote->uri[i], 0);\n \t\tint err;\n-\t\tint dest_argc = common_argc;\n-\t\tint dest_refspec_nr = refspec_nr;\n-\t\tconst char **dest_refspec = refspec;\n-\t\tconst char *dest = remote->uri[i];\n-\t\tconst char *sender = \"send-pack\";\n-\t\tif (!prefixcmp(dest, \"http://\") ||\n-\t\t    !prefixcmp(dest, \"https://\"))\n-\t\t\tsender = \"http-push\";\n-\t\telse {\n-\t\t\tchar *rem = xmalloc(strlen(remote->name) + 10);\n-\t\t\tsprintf(rem, \"--remote=%s\", remote->name);\n-\t\t\targv[dest_argc++] = rem;\n-\t\t\tif (thin)\n-\t\t\t\targv[dest_argc++] = \"--thin\";\n-\t\t}\n-\t\targv[0] = sender;\n-\t\targv[dest_argc++] = dest;\n-\t\twhile (dest_refspec_nr--)\n-\t\t\targv[dest_argc++] = *dest_refspec++;\n-\t\targv[dest_argc] = NULL;\n+\t\tif (receivepack)\n+\t\t\ttransport_set_option(transport,\n+\t\t\t\t\t     TRANS_OPT_RECEIVEPACK, receivepack);\n+\t\tif (thin)\n+\t\t\ttransport_set_option(transport, TRANS_OPT_THIN, \"yes\");\n+\n \t\tif (verbose)\n-\t\t\tfprintf(stderr, \"Pushing to %s\\n\", dest);\n-\t\terr = run_command_v_opt(argv, RUN_GIT_CMD);\n+\t\t\tfprintf(stderr, \"Pushing to %s\\n\", remote->uri[i]);\n+\t\terr = transport_push(transport, refspec_nr, refspec, flags);\n+\t\terr |= transport_disconnect(transport);\n+\n \t\tif (!err)\n \t\t\tcontinue;\n \n \t\terror(\"failed to push to '%s'\", remote->uri[i]);\n-\t\tswitch (err) {\n-\t\tcase -ERR_RUN_COMMAND_FORK:\n-\t\t\terror(\"unable to fork for %s\", sender);\n-\t\tcase -ERR_RUN_COMMAND_EXEC:\n-\t\t\terror(\"unable to exec %s\", sender);\n-\t\t\tbreak;\n-\t\tcase -ERR_RUN_COMMAND_WAITPID:\n-\t\tcase -ERR_RUN_COMMAND_WAITPID_WRONG_PID:\n-\t\tcase -ERR_RUN_COMMAND_WAITPID_SIGNAL:\n-\t\tcase -ERR_RUN_COMMAND_WAITPID_NOEXIT:\n-\t\t\terror(\"%s died with strange error\", sender);\n-\t\t}\n \t\terrs++;\n \t}\n \treturn !!errs;\n@@ -125,6 +84,7 @@ static int do_push(const char *repo)\n int cmd_push(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n+\tint flags = 0;\n \tconst char *repo = NULL;\t/* default repository */\n \n \tfor (i = 1; i < argc; i++) {\n@@ -144,7 +104,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"--all\")) {\n-\t\t\tall = 1;\n+\t\t\tflags |= TRANSPORT_PUSH_ALL;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"--tags\")) {\n@@ -152,7 +112,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"--force\") || !strcmp(arg, \"-f\")) {\n-\t\t\tforce = 1;\n+\t\t\tflags |= TRANSPORT_PUSH_FORCE;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"--thin\")) {\n@@ -164,11 +124,11 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!prefixcmp(arg, \"--receive-pack=\")) {\n-\t\t\treceivepack = arg;\n+\t\t\treceivepack = arg + 15;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!prefixcmp(arg, \"--exec=\")) {\n-\t\t\treceivepack = arg;\n+\t\t\treceivepack = arg + 7;\n \t\t\tcontinue;\n \t\t}\n \t\tusage(push_usage);\n@@ -177,5 +137,5 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \tif (all && refspec)\n \t\tusage(push_usage);\n \n-\treturn do_push(repo);\n+\treturn do_push(repo, flags);\n }\ndiff --git a/transport.c b/transport.c\nnew file mode 100644\nindex 0000000..ce03133\n--- /dev/null\n+++ b/transport.c\n@@ -0,0 +1,197 @@\n+#include \"cache.h\"\n+#include \"transport.h\"\n+#include \"run-command.h\"\n+\n+static const struct transport_ops rsync_transport = {\n+};\n+\n+static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {\n+\tconst char **argv;\n+\tint argc;\n+\tint err;\n+\n+\targv = xmalloc((refspec_nr + 11) * sizeof(char *));\n+\targv[0] = \"http-push\";\n+\targc = 1;\n+\tif (flags & TRANSPORT_PUSH_ALL)\n+\t\targv[argc++] = \"--all\";\n+\tif (flags & TRANSPORT_PUSH_FORCE)\n+\t\targv[argc++] = \"--force\";\n+\targv[argc++] = transport->url;\n+\twhile (refspec_nr--)\n+\t\targv[argc++] = *refspec++;\n+\targv[argc] = NULL;\n+\terr = run_command_v_opt(argv, RUN_GIT_CMD);\n+\tswitch (err) {\n+\tcase -ERR_RUN_COMMAND_FORK:\n+\t\terror(\"unable to fork for %s\", argv[0]);\n+\tcase -ERR_RUN_COMMAND_EXEC:\n+\t\terror(\"unable to exec %s\", argv[0]);\n+\t\tbreak;\n+\tcase -ERR_RUN_COMMAND_WAITPID:\n+\tcase -ERR_RUN_COMMAND_WAITPID_WRONG_PID:\n+\tcase -ERR_RUN_COMMAND_WAITPID_SIGNAL:\n+\tcase -ERR_RUN_COMMAND_WAITPID_NOEXIT:\n+\t\terror(\"%s died with strange error\", argv[0]);\n+\t}\n+\treturn !!err;\n+}\n+\n+static const struct transport_ops curl_transport = {\n+\t/* set_option */\tNULL,\n+\t/* push */\t\tcurl_transport_push\n+};\n+\n+static const struct transport_ops bundle_transport = {\n+};\n+\n+struct git_transport_data {\n+\tunsigned thin : 1;\n+\n+\tconst char *receivepack;\n+};\n+\n+static int set_git_option(struct transport *connection,\n+\t\t\t  const char *name, const char *value)\n+{\n+\tstruct git_transport_data *data = connection->data;\n+\tif (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {\n+\t\tdata->receivepack = value;\n+\t\treturn 0;\n+\t} else if (!strcmp(name, TRANS_OPT_THIN)) {\n+\t\tdata->thin = !!value;\n+\t\treturn 0;\n+\t}\n+\treturn 1;\n+}\n+\n+static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {\n+\tstruct git_transport_data *data = transport->data;\n+\tconst char **argv;\n+\tchar *rem;\n+\tint argc;\n+\tint err;\n+\n+\targv = xmalloc((refspec_nr + 11) * sizeof(char *));\n+\targv[0] = \"send-pack\";\n+\targc = 1;\n+\tif (flags & TRANSPORT_PUSH_ALL)\n+\t\targv[argc++] = \"--all\";\n+\tif (flags & TRANSPORT_PUSH_FORCE)\n+\t\targv[argc++] = \"--force\";\n+\tif (data->receivepack) {\n+\t\tchar *rp = xmalloc(strlen(data->receivepack) + 16);\n+\t\tsprintf(rp, \"--receive-pack=%s\", data->receivepack);\n+\t\targv[argc++] = rp;\n+\t}\n+\tif (data->thin)\n+\t\targv[argc++] = \"--thin\";\n+\trem = xmalloc(strlen(transport->remote->name) + 10);\n+\tsprintf(rem, \"--remote=%s\", transport->remote->name);\n+\targv[argc++] = rem;\n+\targv[argc++] = transport->url;\n+\twhile (refspec_nr--)\n+\t\targv[argc++] = *refspec++;\n+\targv[argc] = NULL;\n+\terr = run_command_v_opt(argv, RUN_GIT_CMD);\n+\tswitch (err) {\n+\tcase -ERR_RUN_COMMAND_FORK:\n+\t\terror(\"unable to fork for %s\", argv[0]);\n+\tcase -ERR_RUN_COMMAND_EXEC:\n+\t\terror(\"unable to exec %s\", argv[0]);\n+\t\tbreak;\n+\tcase -ERR_RUN_COMMAND_WAITPID:\n+\tcase -ERR_RUN_COMMAND_WAITPID_WRONG_PID:\n+\tcase -ERR_RUN_COMMAND_WAITPID_SIGNAL:\n+\tcase -ERR_RUN_COMMAND_WAITPID_NOEXIT:\n+\t\terror(\"%s died with strange error\", argv[0]);\n+\t}\n+\treturn !!err;\n+}\n+\n+static const struct transport_ops git_transport = {\n+\t/* set_option */\tset_git_option,\n+\t/* push */\t\tgit_transport_push\n+};\n+\n+static int is_local(const char *url)\n+{\n+\tconst char *colon = strchr(url, ':');\n+\tconst char *slash = strchr(url, '/');\n+\treturn !colon || (slash && slash < colon);\n+}\n+\n+static int is_file(const char *url)\n+{\n+\tstruct stat buf;\n+\tif (stat(url, &buf))\n+\t\treturn 0;\n+\treturn S_ISREG(buf.st_mode);\n+}\n+\n+struct transport *transport_get(struct remote *remote, const char *url,\n+\t\t\t\tint fetch)\n+{\n+\tstruct transport *ret = NULL;\n+\tif (!prefixcmp(url, \"rsync://\")) {\n+\t\tret = xmalloc(sizeof(*ret));\n+\t\tret->data = NULL;\n+\t\tret->ops = &rsync_transport;\n+\t} else if (!prefixcmp(url, \"http://\") || !prefixcmp(url, \"https://\") ||\n+\t\t   !prefixcmp(url, \"ftp://\")) {\n+\t\tret = xmalloc(sizeof(*ret));\n+\t\tret->ops = &curl_transport;\n+\t\tret->data = NULL;\n+\t} else if (is_local(url) && is_file(url)) {\n+\t\tret = xmalloc(sizeof(*ret));\n+\t\tret->data = NULL;\n+\t\tret->ops = &bundle_transport;\n+\t} else {\n+\t\tstruct git_transport_data *data = xcalloc(1, sizeof(*data));\n+\t\tret = xcalloc(1, sizeof(*ret));\n+\t\tret->data = data;\n+\t\tdata->thin = 1;\n+\t\tdata->receivepack = \"git-receive-pack\";\n+\t\tif (remote->receivepack)\n+\t\t\tdata->receivepack = remote->receivepack;\n+\t\tret->ops = &git_transport;\n+\t}\n+\tif (ret) {\n+\t\tret->remote = remote;\n+\t\tret->url = url;\n+\t\tret->fetch = !!fetch;\n+\t}\n+\treturn ret;\n+}\n+\n+int transport_set_option(struct transport *transport,\n+\t\t\t const char *name, const char *value)\n+{\n+\tint ret = 1;\n+\tif (transport->ops->set_option)\n+\t\tret = transport->ops->set_option(transport, name, value);\n+\tif (ret < 0)\n+\t\tfprintf(stderr, \"For '%s' option %s cannot be set to '%s'\\n\",\n+\t\t\ttransport->url, name, value);\n+\tif (ret > 0)\n+\t\tfprintf(stderr, \"For '%s' option %s is ignored\\n\",\n+\t\t\ttransport->url, name);\n+\treturn ret;\n+}\n+\n+int transport_push(struct transport *transport,\n+\t\t   int refspec_nr, const char **refspec, int flags)\n+{\n+\tif (!transport->ops->push)\n+\t\treturn 1;\n+\treturn transport->ops->push(transport, refspec_nr, refspec, flags);\n+}\n+\n+int transport_disconnect(struct transport *transport)\n+{\n+\tint ret = 0;\n+\tif (transport->ops->disconnect)\n+\t\tret = transport->ops->disconnect(transport);\n+\tfree(transport);\n+\treturn ret;\n+}\ndiff --git a/transport.h b/transport.h\nnew file mode 100644\nindex 0000000..5c2eb95\n--- /dev/null\n+++ b/transport.h\n@@ -0,0 +1,61 @@\n+#ifndef TRANSPORT_H\n+#define TRANSPORT_H\n+\n+#include \"cache.h\"\n+#include \"remote.h\"\n+\n+struct transport {\n+\tunsigned verbose : 1;\n+\tunsigned fetch : 1;\n+\tstruct remote *remote;\n+\tconst char *url;\n+\n+\tvoid *data;\n+\n+\tstruct ref *remote_refs;\n+\n+\tconst struct transport_ops *ops;\n+};\n+\n+#define TRANSPORT_PUSH_ALL 1\n+#define TRANSPORT_PUSH_FORCE 2\n+\n+struct transport_ops {\n+\t/**\n+\t * Returns 0 if successful, positive if the option is not\n+\t * recognized or is inapplicable, and negative if the option\n+\t * is applicable but the value is invalid.\n+\t **/\n+\tint (*set_option)(struct transport *connection, const char *name,\n+\t\t\t  const char *value);\n+\n+\tint (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);\n+\n+\tint (*disconnect)(struct transport *connection);\n+};\n+\n+/* Returns a transport suitable for the url */\n+struct transport *transport_get(struct remote *remote, const char *url,\n+\t\t\t\tint fetch);\n+\n+/* Transport options which apply to git:// and scp-style URLs */\n+\n+/* The program to use on the remote side to receive a pack */\n+#define TRANS_OPT_RECEIVEPACK \"receivepack\"\n+\n+/* Transfer the data as a thin pack if not null */\n+#define TRANS_OPT_THIN \"thin\"\n+\n+/**\n+ * Returns 0 if the option was used, non-zero otherwise. Prints a\n+ * message to stderr if the option is not used.\n+ **/\n+int transport_set_option(struct transport *transport, const char *name,\n+\t\t\t const char *value);\n+\n+int transport_push(struct transport *connection,\n+\t\t   int refspec_nr, const char **refspec, int flags);\n+\n+int transport_disconnect(struct transport *transport);\n+\n+#endif\n-- \n1.5.2.2.1399.g097d5-dirty\n"}]}