{"thread":{"id":"18220","subject":"[PATCH 2/2] Move push matching and reporting logic into transport.c","startedAt":"2009-03-09T01:06:07Z","lastAt":"2009-03-09T01:06:07Z","messageCount":1,"participants":["Daniel Barkalow"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"107420","messageId":"alpine.LNX.1.00.0903082104490.19665@iabervon.org","threadId":"18220","inReplyTo":null,"subject":"[PATCH 2/2] Move push matching and reporting logic into transport.c","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2009-03-09T01:06:07Z","receivedAt":"2009-03-09T01:06:07Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"For native-protocol pushes (and other protocols as they are converted\nto the new method), this moves the refspec match, tracking update, and\nreport message out of send-pack() and into transport_push(), where it\ncan be shared completely with other protocols. This also makes fetch\nand push more similar in terms of what code is in what file.\n\nSigned-off-by: Daniel Barkalow <barkalow@iabervon.org>\n---\n builtin-send-pack.c |  143 +++++++++++++-------------\n send-pack.h         |    6 +-\n transport.c         |  283 +++++++++++++++++++++++++++++++++++++++++++++++----\n transport.h         |    3 +-\n 4 files changed, 343 insertions(+), 92 deletions(-)\n\ndiff --git a/builtin-send-pack.c b/builtin-send-pack.c\nindex 43b89ec..91c3651 100644\n--- a/builtin-send-pack.c\n+++ b/builtin-send-pack.c\n@@ -11,7 +11,6 @@ static const char send_pack_usage[] =\n \"  --all and explicit <ref> specification are mutually exclusive.\";\n \n static struct send_pack_args args = {\n-\t/* .receivepack = */ \"git-receive-pack\",\n };\n \n static int feed_object(const unsigned char *sha1, int fd, int negative)\n@@ -31,7 +30,7 @@ static int feed_object(const unsigned char *sha1, int fd, int negative)\n /*\n  * Make a pack stream and spit it out into file descriptor fd\n  */\n-static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra)\n+static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args)\n {\n \t/*\n \t * The child becomes pack-objects --revs; we feed\n@@ -49,7 +48,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext\n \tstruct child_process po;\n \tint i;\n \n-\tif (args.use_thin_pack)\n+\tif (args->use_thin_pack)\n \t\targv[4] = \"--thin\";\n \tmemset(&po, 0, sizeof(po));\n \tpo.argv = argv;\n@@ -83,8 +82,6 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext\n \treturn 0;\n }\n \n-static struct ref *remote_refs, **remote_tail;\n-\n static int receive_status(int in, struct ref *refs)\n {\n \tstruct ref *hint;\n@@ -300,27 +297,19 @@ static int refs_pushed(struct ref *ref)\n \treturn 0;\n }\n \n-static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)\n+int send_pack(struct send_pack_args *args,\n+\t      int fd[], struct child_process *conn,\n+\t      struct ref *remote_refs,\n+\t      struct extra_have_objects *extra_have)\n {\n-\tstruct ref *ref, *local_refs;\n+\tint in = fd[0];\n+\tint out = fd[1];\n+\tstruct ref *ref;\n \tint new_refs;\n \tint ask_for_status_report = 0;\n \tint allow_deleting_refs = 0;\n \tint expect_status_report = 0;\n-\tint flags = MATCH_REFS_NONE;\n \tint ret;\n-\tstruct extra_have_objects extra_have;\n-\n-\tmemset(&extra_have, 0, sizeof(extra_have));\n-\tif (args.send_all)\n-\t\tflags |= MATCH_REFS_ALL;\n-\tif (args.send_mirror)\n-\t\tflags |= MATCH_REFS_MIRROR;\n-\n-\t/* No funny business with the matcher */\n-\tremote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL,\n-\t\t\t\t       &extra_have);\n-\tlocal_refs = get_local_heads();\n \n \t/* Does the other end support the reporting? */\n \tif (server_supports(\"report-status\"))\n@@ -328,19 +317,9 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest\n \tif (server_supports(\"delete-refs\"))\n \t\tallow_deleting_refs = 1;\n \n-\t/* match them up */\n-\tif (!remote_tail)\n-\t\tremote_tail = &remote_refs;\n-\tif (match_refs(local_refs, remote_refs, &remote_tail,\n-\t\t       nr_refspec, refspec, flags)) {\n-\t\tclose(out);\n-\t\treturn -1;\n-\t}\n-\n \tif (!remote_refs) {\n \t\tfprintf(stderr, \"No refs in common and none specified; doing nothing.\\n\"\n \t\t\t\"Perhaps you should specify a branch such as 'master'.\\n\");\n-\t\tclose(out);\n \t\treturn 0;\n \t}\n \n@@ -352,7 +331,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest\n \n \t\tif (ref->peer_ref)\n \t\t\thashcpy(ref->new_sha1, ref->peer_ref->new_sha1);\n-\t\telse if (!args.send_mirror)\n+\t\telse if (!args->send_mirror)\n \t\t\tcontinue;\n \n \t\tref->deletion = is_null_sha1(ref->new_sha1);\n@@ -391,7 +370,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest\n \t\t    (!has_sha1_file(ref->old_sha1)\n \t\t      || !ref_newer(ref->new_sha1, ref->old_sha1));\n \n-\t\tif (ref->nonfastforward && !ref->force && !args.force_update) {\n+\t\tif (ref->nonfastforward && !ref->force && !args->force_update) {\n \t\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\n \t\t\tcontinue;\n \t\t}\n@@ -399,7 +378,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest\n \t\tif (!ref->deletion)\n \t\t\tnew_refs++;\n \n-\t\tif (!args.dry_run) {\n+\t\tif (!args->dry_run) {\n \t\t\tchar *old_hex = sha1_to_hex(ref->old_sha1);\n \t\t\tchar *new_hex = sha1_to_hex(ref->new_sha1);\n \n@@ -420,27 +399,19 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest\n \t}\n \n \tpacket_flush(out);\n-\tif (new_refs && !args.dry_run) {\n-\t\tif (pack_objects(out, remote_refs, &extra_have) < 0)\n+\tif (new_refs && !args->dry_run) {\n+\t\tif (pack_objects(out, remote_refs, extra_have, args) < 0) {\n+\t\t\tfor (ref = remote_refs; ref; ref = ref->next)\n+\t\t\t\tref->status = REF_STATUS_NONE;\n \t\t\treturn -1;\n+\t\t}\n \t}\n-\telse\n-\t\tclose(out);\n \n \tif (expect_status_report)\n \t\tret = receive_status(in, remote_refs);\n \telse\n \t\tret = 0;\n \n-\tprint_push_status(dest, remote_refs);\n-\n-\tif (!args.dry_run && remote) {\n-\t\tfor (ref = remote_refs; ref; ref = ref->next)\n-\t\t\tupdate_tracking_ref(remote, ref);\n-\t}\n-\n-\tif (!refs_pushed(remote_refs))\n-\t\tfprintf(stderr, \"Everything up-to-date\\n\");\n \tif (ret < 0)\n \t\treturn ret;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n@@ -489,11 +460,19 @@ static void verify_remote_names(int nr_heads, const char **heads)\n \n int cmd_send_pack(int argc, const char **argv, const char *prefix)\n {\n-\tint i, nr_heads = 0;\n-\tconst char **heads = NULL;\n+\tint i, nr_refspecs = 0;\n+\tconst char **refspecs = NULL;\n \tconst char *remote_name = NULL;\n \tstruct remote *remote = NULL;\n \tconst char *dest = NULL;\n+\tint fd[2];\n+\tstruct child_process *conn;\n+\tstruct extra_have_objects extra_have;\n+\tstruct ref *remote_refs, **remote_tail, *local_refs;\n+\tint ret;\n+\tint send_all = 0;\n+\tconst char *receivepack = \"git-receive-pack\";\n+\tint flags;\n \n \targv++;\n \tfor (i = 1; i < argc; i++, argv++) {\n@@ -501,11 +480,11 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \n \t\tif (*arg == '-') {\n \t\t\tif (!prefixcmp(arg, \"--receive-pack=\")) {\n-\t\t\t\targs.receivepack = arg + 15;\n+\t\t\t\treceivepack = arg + 15;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!prefixcmp(arg, \"--exec=\")) {\n-\t\t\t\targs.receivepack = arg + 7;\n+\t\t\t\treceivepack = arg + 7;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!prefixcmp(arg, \"--remote=\")) {\n@@ -513,7 +492,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--all\")) {\n-\t\t\t\targs.send_all = 1;\n+\t\t\t\tsend_all = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--dry-run\")) {\n@@ -542,8 +521,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\t\tdest = arg;\n \t\t\tcontinue;\n \t\t}\n-\t\theads = (const char **) argv;\n-\t\tnr_heads = argc - i;\n+\t\trefspecs = (const char **) argv;\n+\t\tnr_refspecs = argc - i;\n \t\tbreak;\n \t}\n \tif (!dest)\n@@ -552,8 +531,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t * --all and --mirror are incompatible; neither makes sense\n \t * with any refspecs.\n \t */\n-\tif ((heads && (args.send_all || args.send_mirror)) ||\n-\t\t\t\t\t(args.send_all && args.send_mirror))\n+\tif ((refspecs && (send_all || args.send_mirror)) ||\n+\t    (send_all && args.send_mirror))\n \t\tusage(send_pack_usage);\n \n \tif (remote_name) {\n@@ -564,24 +543,50 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\t}\n \t}\n \n-\treturn send_pack(&args, dest, remote, nr_heads, heads);\n-}\n+\tconn = git_connect(fd, dest, receivepack, args.verbose ? CONNECT_VERBOSE : 0);\n \n-int send_pack(struct send_pack_args *my_args,\n-\t      const char *dest, struct remote *remote,\n-\t      int nr_heads, const char **heads)\n-{\n-\tint fd[2], ret;\n-\tstruct child_process *conn;\n+\tmemset(&extra_have, 0, sizeof(extra_have));\n+\n+\tget_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL,\n+\t\t\t &extra_have);\n+\n+\tverify_remote_names(nr_refspecs, refspecs);\n+\n+\tlocal_refs = get_local_heads();\n \n-\tmemcpy(&args, my_args, sizeof(args));\n+\tflags = MATCH_REFS_NONE;\n+\n+\tif (send_all)\n+\t\tflags |= MATCH_REFS_ALL;\n+\tif (args.send_mirror)\n+\t\tflags |= MATCH_REFS_MIRROR;\n+\n+\t/* match them up */\n+\tremote_tail = &remote_refs;\n+\twhile (*remote_tail)\n+\t\tremote_tail = &((*remote_tail)->next);\n+\tif (match_refs(local_refs, remote_refs, &remote_tail,\n+\t\t       nr_refspecs, refspecs, flags)) {\n+\t\treturn -1;\n+\t}\n \n-\tverify_remote_names(nr_heads, heads);\n+\tret = send_pack(&args, fd, conn, remote_refs, &extra_have);\n \n-\tconn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);\n-\tret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);\n+\tclose(fd[1]);\n \tclose(fd[0]);\n-\t/* do_send_pack always closes fd[1] */\n+\n \tret |= finish_connect(conn);\n-\treturn !!ret;\n+\n+\tprint_push_status(dest, remote_refs);\n+\n+\tif (!args.dry_run && remote) {\n+\t\tstruct ref *ref;\n+\t\tfor (ref = remote_refs; ref; ref = ref->next)\n+\t\t\tupdate_tracking_ref(remote, ref);\n+\t}\n+\n+\tif (!ret && !refs_pushed(remote_refs))\n+\t\tfprintf(stderr, \"Everything up-to-date\\n\");\n+\n+\treturn ret;\n }\ndiff --git a/send-pack.h b/send-pack.h\nindex 8ff1dc3..83d76c7 100644\n--- a/send-pack.h\n+++ b/send-pack.h\n@@ -2,9 +2,7 @@\n #define SEND_PACK_H\n \n struct send_pack_args {\n-\tconst char *receivepack;\n \tunsigned verbose:1,\n-\t\tsend_all:1,\n \t\tsend_mirror:1,\n \t\tforce_update:1,\n \t\tuse_thin_pack:1,\n@@ -12,7 +10,7 @@ struct send_pack_args {\n };\n \n int send_pack(struct send_pack_args *args,\n-\t      const char *dest, struct remote *remote,\n-\t      int nr_heads, const char **heads);\n+\t      int fd[], struct child_process *conn,\n+\t      struct ref *remote_refs, struct extra_have_objects *extra_have);\n \n #endif\ndiff --git a/transport.c b/transport.c\nindex 9ad4a16..73bb9b5 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -138,7 +138,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list)\n \t}\n }\n \n-static struct ref *get_refs_via_rsync(struct transport *transport)\n+static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)\n {\n \tstruct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;\n \tstruct ref dummy, *tail = &dummy;\n@@ -146,6 +146,9 @@ static struct ref *get_refs_via_rsync(struct transport *transport)\n \tconst char *args[5];\n \tint temp_dir_len;\n \n+\tif (for_push)\n+\t\treturn NULL;\n+\n \t/* copy the refs to the temporary directory */\n \n \tstrbuf_addstr(&temp_dir, git_path(\"rsync-refs-XXXXXX\"));\n@@ -422,7 +425,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons\n \treturn !!err;\n }\n \n-static struct ref *get_refs_via_curl(struct transport *transport)\n+static struct ref *get_refs_via_curl(struct transport *transport, int for_push)\n {\n \tstruct strbuf buffer = STRBUF_INIT;\n \tchar *data, *start, *mid;\n@@ -439,6 +442,9 @@ static struct ref *get_refs_via_curl(struct transport *transport)\n \n \tstruct walker *walker;\n \n+\tif (for_push)\n+\t\treturn NULL;\n+\n \tif (!transport->data)\n \t\ttransport->data = get_http_walker(transport->url,\n \t\t\t\t\t\ttransport->remote);\n@@ -525,12 +531,15 @@ struct bundle_transport_data {\n \tstruct bundle_header header;\n };\n \n-static struct ref *get_refs_from_bundle(struct transport *transport)\n+static struct ref *get_refs_from_bundle(struct transport *transport, int for_push)\n {\n \tstruct bundle_transport_data *data = transport->data;\n \tstruct ref *result = NULL;\n \tint i;\n \n+\tif (for_push)\n+\t\treturn NULL;\n+\n \tif (data->fd > 0)\n \t\tclose(data->fd);\n \tdata->fd = read_bundle_header(transport->url, &data->header);\n@@ -571,6 +580,7 @@ struct git_transport_data {\n \tint fd[2];\n \tconst char *uploadpack;\n \tconst char *receivepack;\n+\tstruct extra_have_objects extra_have;\n };\n \n static int set_git_option(struct transport *connection,\n@@ -602,20 +612,23 @@ static int set_git_option(struct transport *connection,\n \treturn 1;\n }\n \n-static int connect_setup(struct transport *transport)\n+static int connect_setup(struct transport *transport, int for_push, int verbose)\n {\n \tstruct git_transport_data *data = transport->data;\n-\tdata->conn = git_connect(data->fd, transport->url, data->uploadpack, 0);\n+\tdata->conn = git_connect(data->fd, transport->url,\n+\t\t\t\t for_push ? data->receivepack : data->uploadpack,\n+\t\t\t\t verbose ? CONNECT_VERBOSE : 0);\n \treturn 0;\n }\n \n-static struct ref *get_refs_via_connect(struct transport *transport)\n+static struct ref *get_refs_via_connect(struct transport *transport, int for_push)\n {\n \tstruct git_transport_data *data = transport->data;\n \tstruct ref *refs;\n \n-\tconnect_setup(transport);\n-\tget_remote_heads(data->fd[0], &refs, 0, NULL, 0, NULL);\n+\tconnect_setup(transport, for_push, 0);\n+\tget_remote_heads(data->fd[0], &refs, 0, NULL,\n+\t\t\t for_push ? REF_NORMAL : 0, &data->extra_have);\n \n \treturn refs;\n }\n@@ -647,7 +660,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \t\torigh[i] = heads[i] = xstrdup(to_fetch[i]->name);\n \n \tif (!data->conn) {\n-\t\tconnect_setup(transport);\n+\t\tconnect_setup(transport, 0, 0);\n \t\tget_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);\n \t}\n \n@@ -670,20 +683,216 @@ static int fetch_refs_via_pack(struct transport *transport,\n \treturn (refs ? 0 : -1);\n }\n \n-static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)\n+static int refs_pushed(struct ref *ref)\n+{\n+\tfor (; ref; ref = ref->next) {\n+\t\tswitch(ref->status) {\n+\t\tcase REF_STATUS_NONE:\n+\t\tcase REF_STATUS_UPTODATE:\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\treturn 1;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static void update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)\n+{\n+\tstruct refspec rs;\n+\n+\tif (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)\n+\t\treturn;\n+\n+\trs.src = ref->name;\n+\trs.dst = NULL;\n+\n+\tif (!remote_find_tracking(remote, &rs)) {\n+\t\tif (verbose)\n+\t\t\tfprintf(stderr, \"updating local tracking ref '%s'\\n\", rs.dst);\n+\t\tif (ref->deletion) {\n+\t\t\tdelete_ref(rs.dst, NULL, 0);\n+\t\t} else\n+\t\t\tupdate_ref(\"update by push\", rs.dst,\n+\t\t\t\t\tref->new_sha1, NULL, 0, 0);\n+\t\tfree(rs.dst);\n+\t}\n+}\n+\n+#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)\n+\n+static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)\n+{\n+\tfprintf(stderr, \" %c %-*s \", flag, SUMMARY_WIDTH, summary);\n+\tif (from)\n+\t\tfprintf(stderr, \"%s -> %s\", prettify_ref(from), prettify_ref(to));\n+\telse\n+\t\tfputs(prettify_ref(to), stderr);\n+\tif (msg) {\n+\t\tfputs(\" (\", stderr);\n+\t\tfputs(msg, stderr);\n+\t\tfputc(')', stderr);\n+\t}\n+\tfputc('\\n', stderr);\n+}\n+\n+static const char *status_abbrev(unsigned char sha1[20])\n+{\n+\treturn find_unique_abbrev(sha1, DEFAULT_ABBREV);\n+}\n+\n+static void print_ok_ref_status(struct ref *ref)\n+{\n+\tif (ref->deletion)\n+\t\tprint_ref_status('-', \"[deleted]\", ref, NULL, NULL);\n+\telse if (is_null_sha1(ref->old_sha1))\n+\t\tprint_ref_status('*',\n+\t\t\t(!prefixcmp(ref->name, \"refs/tags/\") ? \"[new tag]\" :\n+\t\t\t  \"[new branch]\"),\n+\t\t\tref, ref->peer_ref, NULL);\n+\telse {\n+\t\tchar quickref[84];\n+\t\tchar type;\n+\t\tconst char *msg;\n+\n+\t\tstrcpy(quickref, status_abbrev(ref->old_sha1));\n+\t\tif (ref->nonfastforward) {\n+\t\t\tstrcat(quickref, \"...\");\n+\t\t\ttype = '+';\n+\t\t\tmsg = \"forced update\";\n+\t\t} else {\n+\t\t\tstrcat(quickref, \"..\");\n+\t\t\ttype = ' ';\n+\t\t\tmsg = NULL;\n+\t\t}\n+\t\tstrcat(quickref, status_abbrev(ref->new_sha1));\n+\n+\t\tprint_ref_status(type, quickref, ref, ref->peer_ref, msg);\n+\t}\n+}\n+\n+static int print_one_push_status(struct ref *ref, const char *dest, int count)\n+{\n+\tif (!count)\n+\t\tfprintf(stderr, \"To %s\\n\", dest);\n+\n+\tswitch(ref->status) {\n+\tcase REF_STATUS_NONE:\n+\t\tprint_ref_status('X', \"[no match]\", ref, NULL, NULL);\n+\t\tbreak;\n+\tcase REF_STATUS_REJECT_NODELETE:\n+\t\tprint_ref_status('!', \"[rejected]\", ref, NULL,\n+\t\t\t\t\"remote does not support deleting refs\");\n+\t\tbreak;\n+\tcase REF_STATUS_UPTODATE:\n+\t\tprint_ref_status('=', \"[up to date]\", ref,\n+\t\t\t\tref->peer_ref, NULL);\n+\t\tbreak;\n+\tcase REF_STATUS_REJECT_NONFASTFORWARD:\n+\t\tprint_ref_status('!', \"[rejected]\", ref, ref->peer_ref,\n+\t\t\t\t\"non-fast forward\");\n+\t\tbreak;\n+\tcase REF_STATUS_REMOTE_REJECT:\n+\t\tprint_ref_status('!', \"[remote rejected]\", ref,\n+\t\t\t\tref->deletion ? NULL : ref->peer_ref,\n+\t\t\t\tref->remote_status);\n+\t\tbreak;\n+\tcase REF_STATUS_EXPECTING_REPORT:\n+\t\tprint_ref_status('!', \"[remote failure]\", ref,\n+\t\t\t\tref->deletion ? NULL : ref->peer_ref,\n+\t\t\t\t\"remote failed to report status\");\n+\t\tbreak;\n+\tcase REF_STATUS_OK:\n+\t\tprint_ok_ref_status(ref);\n+\t\tbreak;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+static void print_push_status(const char *dest, struct ref *refs, int verbose)\n+{\n+\tstruct ref *ref;\n+\tint n = 0;\n+\n+\tif (verbose) {\n+\t\tfor (ref = refs; ref; ref = ref->next)\n+\t\t\tif (ref->status == REF_STATUS_UPTODATE)\n+\t\t\t\tn += print_one_push_status(ref, dest, n);\n+\t}\n+\n+\tfor (ref = refs; ref; ref = ref->next)\n+\t\tif (ref->status == REF_STATUS_OK)\n+\t\t\tn += print_one_push_status(ref, dest, n);\n+\n+\tfor (ref = refs; ref; ref = ref->next) {\n+\t\tif (ref->status != REF_STATUS_NONE &&\n+\t\t    ref->status != REF_STATUS_UPTODATE &&\n+\t\t    ref->status != REF_STATUS_OK)\n+\t\t\tn += print_one_push_status(ref, dest, n);\n+\t}\n+}\n+\n+static void verify_remote_names(int nr_heads, const char **heads)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < nr_heads; i++) {\n+\t\tconst char *local = heads[i];\n+\t\tconst char *remote = strrchr(heads[i], ':');\n+\n+\t\tif (*local == '+')\n+\t\t\tlocal++;\n+\n+\t\t/* A matching refspec is okay.  */\n+\t\tif (remote == local && remote[1] == '\\0')\n+\t\t\tcontinue;\n+\n+\t\tremote = remote ? (remote + 1) : local;\n+\t\tswitch (check_ref_format(remote)) {\n+\t\tcase 0: /* ok */\n+\t\tcase CHECK_REF_FORMAT_ONELEVEL:\n+\t\t\t/* ok but a single level -- that is fine for\n+\t\t\t * a match pattern.\n+\t\t\t */\n+\t\tcase CHECK_REF_FORMAT_WILDCARD:\n+\t\t\t/* ok but ends with a pattern-match character */\n+\t\t\tcontinue;\n+\t\t}\n+\t\tdie(\"remote part of refspec is not a valid name in %s\",\n+\t\t    heads[i]);\n+\t}\n+}\n+\n+static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags)\n {\n \tstruct git_transport_data *data = transport->data;\n \tstruct send_pack_args args;\n+\tint ret;\n+\n+\tif (!data->conn) {\n+\t\tstruct ref *tmp_refs;\n+\t\tconnect_setup(transport, 1, 0);\n+\n+\t\tget_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,\n+\t\t\t\t NULL);\n+\t}\n \n-\targs.receivepack = data->receivepack;\n-\targs.send_all = !!(flags & TRANSPORT_PUSH_ALL);\n \targs.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);\n \targs.force_update = !!(flags & TRANSPORT_PUSH_FORCE);\n \targs.use_thin_pack = data->thin;\n \targs.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);\n \targs.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);\n \n-\treturn send_pack(&args, transport->url, transport->remote, refspec_nr, refspec);\n+\tret = send_pack(&args, data->fd, data->conn, remote_refs,\n+\t\t\t&data->extra_have);\n+\n+\tclose(data->fd[1]);\n+\tclose(data->fd[0]);\n+\tret |= finish_connect(data->conn);\n+\tdata->conn = NULL;\n+\n+\treturn ret;\n }\n \n static int disconnect_git(struct transport *transport)\n@@ -753,7 +962,7 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \t\tret->set_option = set_git_option;\n \t\tret->get_refs_list = get_refs_via_connect;\n \t\tret->fetch = fetch_refs_via_pack;\n-\t\tret->push = git_transport_push;\n+\t\tret->push_refs = git_transport_push;\n \t\tret->disconnect = disconnect_git;\n \n \t\tdata->thin = 1;\n@@ -780,15 +989,53 @@ int transport_set_option(struct transport *transport,\n int transport_push(struct transport *transport,\n \t\t   int refspec_nr, const char **refspec, int flags)\n {\n-\tif (!transport->push)\n-\t\treturn 1;\n-\treturn transport->push(transport, refspec_nr, refspec, flags);\n+\tverify_remote_names(refspec_nr, refspec);\n+\n+\tif (transport->push)\n+\t\treturn transport->push(transport, refspec_nr, refspec, flags);\n+\tif (transport->push_refs) {\n+\t\tstruct ref *remote_refs =\n+\t\t\ttransport->get_refs_list(transport, 1);\n+\t\tstruct ref **remote_tail;\n+\t\tstruct ref *local_refs = get_local_heads();\n+\t\tint match_flags = MATCH_REFS_NONE;\n+\t\tint verbose = flags & TRANSPORT_PUSH_VERBOSE;\n+\t\tint ret;\n+\n+\t\tif (flags & TRANSPORT_PUSH_ALL)\n+\t\t\tmatch_flags |= MATCH_REFS_ALL;\n+\t\tif (flags & TRANSPORT_PUSH_MIRROR)\n+\t\t\tmatch_flags |= MATCH_REFS_MIRROR;\n+\n+\t\tremote_tail = &remote_refs;\n+\t\twhile (*remote_tail)\n+\t\t\tremote_tail = &((*remote_tail)->next);\n+\t\tif (match_refs(local_refs, remote_refs, &remote_tail,\n+\t\t\t       refspec_nr, refspec, match_flags)) {\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tret = transport->push_refs(transport, remote_refs, flags);\n+\n+\t\tprint_push_status(transport->url, remote_refs, verbose);\n+\n+\t\tif (!(flags & TRANSPORT_PUSH_DRY_RUN)) {\n+\t\t\tstruct ref *ref;\n+\t\t\tfor (ref = remote_refs; ref; ref = ref->next)\n+\t\t\t\tupdate_tracking_ref(transport->remote, ref, verbose);\n+\t\t}\n+\n+\t\tif (!ret && !refs_pushed(remote_refs))\n+\t\t\tfprintf(stderr, \"Everything up-to-date\\n\");\n+\t\treturn ret;\n+\t}\n+\treturn 1;\n }\n \n const struct ref *transport_get_remote_refs(struct transport *transport)\n {\n \tif (!transport->remote_refs)\n-\t\ttransport->remote_refs = transport->get_refs_list(transport);\n+\t\ttransport->remote_refs = transport->get_refs_list(transport, 0);\n \treturn transport->remote_refs;\n }\n \ndiff --git a/transport.h b/transport.h\nindex 6bbc1a8..b1c2252 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -18,8 +18,9 @@ struct transport {\n \tint (*set_option)(struct transport *connection, const char *name,\n \t\t\t  const char *value);\n \n-\tstruct ref *(*get_refs_list)(struct transport *transport);\n+\tstruct ref *(*get_refs_list)(struct transport *transport, int for_push);\n \tint (*fetch)(struct transport *transport, int refs_nr, const struct ref **refs);\n+\tint (*push_refs)(struct transport *transport, struct ref *refs, int flags);\n \tint (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);\n \n \tint (*disconnect)(struct transport *connection);\n-- \n1.6.1.286.gd33a4.dirty\n"}]}