{"thread":{"id":"8933","subject":"[PATCH] Push code for transport library","startedAt":"2007-07-08T20:09:00Z","lastAt":"2007-07-08T21:14:18Z","messageCount":3,"participants":["Daniel Barkalow","Johannes Schindelin","Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"46785","messageId":"Pine.LNX.4.64.0707081608100.6977@iabervon.org","threadId":"8933","inReplyTo":null,"subject":"[PATCH] Push code for transport library","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2007-07-08T20:09:00Z","receivedAt":"2007-07-08T20:09:00Z","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---\n Makefile       |    3 +-\n builtin-push.c |   92 ++++++++++--------------------\n transport.c    |  177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n transport.h    |   55 +++++++++++++++++\n 4 files changed, 264 insertions(+), 63 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..ac8cfa2 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,46 @@ 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\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\tstruct transport *transport =\n+\t\t\ttransport_get(remote, remote->uri[i]);\n+\t\tint err = 1;\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 (transport->ops->push) {\n+\t\t\terr = 0;\n+\t\t\tif (transport->ops->connect_for_send)\n+\t\t\t\terr = transport->ops->connect_for_send(transport);\n+\t\t\tif (!err) {\n+\t\t\t\tif (verbose)\n+\t\t\t\t\tfprintf(stderr, \"Pushing to %s\\n\", remote->uri[i]);\n+\t\t\t\terr = transport->ops->push(transport, refspec_nr, refspec, flags);\n+\t\t\t}\n+\t\t\tif (transport->ops->disconnect)\n+\t\t\t\terr |= transport->ops->disconnect(transport);\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 (verbose)\n-\t\t\tfprintf(stderr, \"Pushing to %s\\n\", dest);\n-\t\terr = run_command_v_opt(argv, RUN_GIT_CMD);\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 +92,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 +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, \"--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 +120,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 +132,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 +145,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..3b84983\n--- /dev/null\n+++ b/transport.c\n@@ -0,0 +1,177 @@\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.push = curl_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 = set_git_option,\n+\t.push = git_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+{\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->data = NULL;\n+\t\tret->ops = &curl_transport;\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}\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+}\ndiff --git a/transport.h b/transport.h\nnew file mode 100644\nindex 0000000..bff265a\n--- /dev/null\n+++ b/transport.h\n@@ -0,0 +1,55 @@\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 connected : 1;\n+\tstruct remote *remote;\n+\tconst char *url;\n+\n+\tvoid *data;\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 (*connect_for_send)(struct transport *connection);\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+\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+#endif\n-- \n1.5.2.2.1399.g097d5-dirty\n"},{"id":"46788","messageId":"Pine.LNX.4.64.0707082128290.4248@racer.site","threadId":"8933","inReplyTo":"Pine.LNX.4.64.0707081608100.6977@iabervon.org","subject":"Re: [PATCH] Push code for transport library","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-08T20:29:18Z","receivedAt":"2007-07-08T20:29:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 8 Jul 2007, Daniel Barkalow wrote:\n\n> This moves the code to call push backends into a library that can be \n> extended to make matching fetch and push decisions based on the URL it \n> gets, and which could be changed to have built-in implementations \n> instead of calling external programs.\n\nI do not have time to look at the patch more closely, but I like the \ndirection this takes very much.\n\nThanks,\nDscho\n"},{"id":"46792","messageId":"20070708211418.GA4436@spearce.org","threadId":"8933","inReplyTo":"Pine.LNX.4.64.0707081608100.6977@iabervon.org","subject":"Re: [PATCH] Push code for transport library","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-07-08T21:14:18Z","receivedAt":"2007-07-08T21:14:18Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Daniel Barkalow <barkalow@iabervon.org> wrote:\n> This moves the code to call push backends into a library that can be\n> extended to make matching fetch and push decisions based on the URL it\n> gets, and which could be changed to have built-in implementations\n> instead of calling external programs.\n...\n> +static const struct transport_ops git_transport = {\n> +\t.set_option = set_git_option,\n> +\t.push = git_transport_push\n> +};\n\nInteresting.\n\nBut can we please avoid that struct initialization syntax?\nI build Git with a C compiler that doesn't understand it.  No, I\ncan't upgrade the compiler.  In the past we've largely been able\nto avoid using this feature of C99.  If you look at the current\ncodebase this would again become the first usage of the feature,\nas I have long since eliminated all of them.\n\n-- \nShawn.\n"}]}