{"thread":{"id":"21853","subject":"[PATCH 1/3] refactor ref status logic for pushing","startedAt":"2009-12-04T04:54:44Z","lastAt":"2010-01-08T01:04:45Z","messageCount":25,"participants":["Tay Ray Chuan","Daniel Barkalow","Jeff King","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"129157","messageId":"20091204125444.fe943056.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091204125042.c64f347d.rctay89@gmail.com","subject":"[PATCH 1/3] refactor ref status logic for pushing","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-04T04:54:44Z","receivedAt":"2009-12-04T04:54:44Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Move the logic to a new function in remote.[ch],\n set_ref_status_for_push().\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n builtin-send-pack.c |   39 +++------------------------------------\n remote.c            |   42 ++++++++++++++++++++++++++++++++++++++++++\n remote.h            |    1 +\n 3 files changed, 46 insertions(+), 36 deletions(-)\n\ndiff --git a/builtin-send-pack.c b/builtin-send-pack.c\nindex 8fffdbf..8c98624 100644\n--- a/builtin-send-pack.c\n+++ b/builtin-send-pack.c\n@@ -412,44 +412,11 @@ int send_pack(struct send_pack_args *args,\n \t\telse if (!args->send_mirror)\n \t\t\tcontinue;\n\n-\t\tref->deletion = is_null_sha1(ref->new_sha1);\n-\t\tif (ref->deletion && !allow_deleting_refs) {\n-\t\t\tref->status = REF_STATUS_REJECT_NODELETE;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!ref->deletion &&\n-\t\t    !hashcmp(ref->old_sha1, ref->new_sha1)) {\n-\t\t\tref->status = REF_STATUS_UPTODATE;\n+\t\tif (set_ref_status_for_push(ref, args->force_update))\n \t\t\tcontinue;\n-\t\t}\n\n-\t\t/* This part determines what can overwrite what.\n-\t\t * The rules are:\n-\t\t *\n-\t\t * (0) you can always use --force or +A:B notation to\n-\t\t *     selectively force individual ref pairs.\n-\t\t *\n-\t\t * (1) if the old thing does not exist, it is OK.\n-\t\t *\n-\t\t * (2) if you do not have the old thing, you are not allowed\n-\t\t *     to overwrite it; you would not know what you are losing\n-\t\t *     otherwise.\n-\t\t *\n-\t\t * (3) if both new and old are commit-ish, and new is a\n-\t\t *     descendant of old, it is OK.\n-\t\t *\n-\t\t * (4) regardless of all of the above, removing :B is\n-\t\t *     always allowed.\n-\t\t */\n-\n-\t\tref->nonfastforward =\n-\t\t    !ref->deletion &&\n-\t\t    !is_null_sha1(ref->old_sha1) &&\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\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\n+\t\tif (ref->deletion && !allow_deleting_refs) {\n+\t\t\tref->status = REF_STATUS_REJECT_NODELETE;\n \t\t\tcontinue;\n \t\t}\n\ndiff --git a/remote.c b/remote.c\nindex e3afecd..188869a 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1247,6 +1247,48 @@ int match_refs(struct ref *src, struct ref **dst,\n \treturn 0;\n }\n\n+int set_ref_status_for_push(struct ref *ref, int force_update)\n+{\n+\tref->deletion = is_null_sha1(ref->new_sha1);\n+\tif (!ref->deletion &&\n+\t\t!hashcmp(ref->old_sha1, ref->new_sha1)) {\n+\t\tref->status = REF_STATUS_UPTODATE;\n+\t\treturn 1;\n+\t}\n+\n+\t/* This part determines what can overwrite what.\n+\t * The rules are:\n+\t *\n+\t * (0) you can always use --force or +A:B notation to\n+\t *     selectively force individual ref pairs.\n+\t *\n+\t * (1) if the old thing does not exist, it is OK.\n+\t *\n+\t * (2) if you do not have the old thing, you are not allowed\n+\t *     to overwrite it; you would not know what you are losing\n+\t *     otherwise.\n+\t *\n+\t * (3) if both new and old are commit-ish, and new is a\n+\t *     descendant of old, it is OK.\n+\t *\n+\t * (4) regardless of all of the above, removing :B is\n+\t *     always allowed.\n+\t */\n+\n+\tref->nonfastforward =\n+\t\t!ref->deletion &&\n+\t\t!is_null_sha1(ref->old_sha1) &&\n+\t\t(!has_sha1_file(ref->old_sha1)\n+\t\t  || !ref_newer(ref->new_sha1, ref->old_sha1));\n+\n+\tif (ref->nonfastforward && !ref->force && !force_update) {\n+\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\n+\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n struct branch *branch_get(const char *name)\n {\n \tstruct branch *ret;\ndiff --git a/remote.h b/remote.h\nindex 8b7ecf9..0f45553 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -98,6 +98,7 @@ char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n\n int match_refs(struct ref *src, struct ref **dst,\n \t       int nr_refspec, const char **refspec, int all);\n+int set_ref_status_for_push(struct ref *ref, int force_update);\n\n /*\n  * Given a list of the remote refs and the specification of things to\n--\n1.6.6.rc1.286.gbc15a\n"},{"id":"129541","messageId":"20091208223413.98e99d3e.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091204125042.c64f347d.rctay89@gmail.com","subject":"[PATCH v2 0/3] transport: catch non-fast-forwards","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-08T14:34:13Z","receivedAt":"2009-12-08T14:34:13Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Daniel, I've reworked the refactoring of ref status logic, like you\nmentioned, such that transport_push() calls the generic function before\ncalling the transport->push_refs() implementation.\n\nJunio, although this series affects http transports (smart), please treat\nthis series as separate from 'tr/http-updates' in 'next'.\n\nSummary for those who missed v1:\n  This patch series applies on top of 'next', and deals with alerting\n  the user to non-fast-forward pushes when using helpers (smart).\n\n  Previously, git silently exited. This situation involves the curl\n  helper and the smart protocol. The fast-forward push is only detected\n  when curl executes the rpc client (git-send-pack). Now, we detect it\n  before telling the helper to push.\n\nChanges from v1:\n - reworked refactoring of ref status logic (patch 1)\n - rewrote the patch for transport_push() (patch 2)\n - minor rewording of commit message (patch 3)\n\nTay Ray Chuan (3):\n  refactor ref status logic for pushing\n  transport.c::transport_push(): make ref status affect return value\n  transport-helper.c::push_refs(): emit \"no refs\" error message\n\n builtin-send-pack.c |   50 +++++++++++---------------------------------------\n remote.c            |   50 ++++++++++++++++++++++++++++++++++++++++++++++++++\n remote.h            |    2 ++\n transport-helper.c  |   18 ++++++++++--------\n transport.c         |   11 +++++++++--\n 5 files changed, 82 insertions(+), 49 deletions(-)\n\n--\nCheers,\nRay Chuan\n"},{"id":"129542","messageId":"20091208223543.c7f88afe.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091208223413.98e99d3e.rctay89@gmail.com","subject":"[PATCH v2 1/3] refactor ref status logic for pushing","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-08T14:35:43Z","receivedAt":"2009-12-08T14:35:43Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Move the logic that detects up-to-date and non-fast-forward refs to a\nnew function in remote.[ch], set_ref_status_for_push().\n\nMake transport_push() invoke set_ref_status_for_push() before invoking\nthe push_refs() implementation. (As a side-effect, the push_refs()\nimplementation in transport-helper.c now knows of non-fast-forward\npushes.)\n\nRemoved logic for detecting up-to-date refs from the push_refs()\nimplementation in transport-helper.c, as transport_push() has already\ndone so for it.\n\nMake cmd_send_pack() invoke set_ref_status_for_push() before invoking\nsend_pack(), as transport_push() can't do it for send_pack() here.\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n\nIn v1, this was spread over two patches. I squashed them into one\n(here).\n\nset_ref_status_for_push() used to be called inside the for loop over\nremote_refs; now, it has its own for loop.\n\n builtin-send-pack.c |   50 +++++++++++---------------------------------------\n remote.c            |   50 ++++++++++++++++++++++++++++++++++++++++++++++++++\n remote.h            |    2 ++\n transport-helper.c  |   13 ++++++-------\n transport.c         |    4 ++++\n 5 files changed, 73 insertions(+), 46 deletions(-)\n\ndiff --git a/builtin-send-pack.c b/builtin-send-pack.c\nindex 8fffdbf..38580c3 100644\n--- a/builtin-send-pack.c\n+++ b/builtin-send-pack.c\n@@ -406,50 +406,19 @@ int send_pack(struct send_pack_args *args,\n \t */\n \tnew_refs = 0;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\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\tif (!ref->peer_ref && !args->send_mirror)\n \t\t\tcontinue;\n\n-\t\tref->deletion = is_null_sha1(ref->new_sha1);\n-\t\tif (ref->deletion && !allow_deleting_refs) {\n-\t\t\tref->status = REF_STATUS_REJECT_NODELETE;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!ref->deletion &&\n-\t\t    !hashcmp(ref->old_sha1, ref->new_sha1)) {\n-\t\t\tref->status = REF_STATUS_UPTODATE;\n+\t\tswitch (ref->status) {\n+\t\tcase REF_STATUS_REJECT_NONFASTFORWARD:\n+\t\tcase REF_STATUS_UPTODATE:\n \t\t\tcontinue;\n+\t\tdefault:\n+\t\t\t; /* do nothing */\n \t\t}\n\n-\t\t/* This part determines what can overwrite what.\n-\t\t * The rules are:\n-\t\t *\n-\t\t * (0) you can always use --force or +A:B notation to\n-\t\t *     selectively force individual ref pairs.\n-\t\t *\n-\t\t * (1) if the old thing does not exist, it is OK.\n-\t\t *\n-\t\t * (2) if you do not have the old thing, you are not allowed\n-\t\t *     to overwrite it; you would not know what you are losing\n-\t\t *     otherwise.\n-\t\t *\n-\t\t * (3) if both new and old are commit-ish, and new is a\n-\t\t *     descendant of old, it is OK.\n-\t\t *\n-\t\t * (4) regardless of all of the above, removing :B is\n-\t\t *     always allowed.\n-\t\t */\n-\n-\t\tref->nonfastforward =\n-\t\t    !ref->deletion &&\n-\t\t    !is_null_sha1(ref->old_sha1) &&\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\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\n+\t\tif (ref->deletion && !allow_deleting_refs) {\n+\t\t\tref->status = REF_STATUS_REJECT_NODELETE;\n \t\t\tcontinue;\n \t\t}\n\n@@ -673,6 +642,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \tif (match_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))\n \t\treturn -1;\n\n+\tset_ref_status_for_push(remote_refs, args.send_mirror,\n+\t\targs.force_update);\n+\n \tret = send_pack(&args, fd, conn, remote_refs, &extra_have);\n\n \tif (helper_status)\ndiff --git a/remote.c b/remote.c\nindex e3afecd..c70181c 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1247,6 +1247,56 @@ int match_refs(struct ref *src, struct ref **dst,\n \treturn 0;\n }\n\n+void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n+\tint force_update)\n+{\n+\tstruct ref *ref;\n+\n+\tfor (ref = remote_refs; ref; ref = ref->next) {\n+\t\tif (ref->peer_ref)\n+\t\t\thashcpy(ref->new_sha1, ref->peer_ref->new_sha1);\n+\t\telse if (!send_mirror)\n+\t\t\tcontinue;\n+\n+\t\tref->deletion = is_null_sha1(ref->new_sha1);\n+\t\tif (!ref->deletion &&\n+\t\t\t!hashcmp(ref->old_sha1, ref->new_sha1)) {\n+\t\t\tref->status = REF_STATUS_UPTODATE;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* This part determines what can overwrite what.\n+\t\t * The rules are:\n+\t\t *\n+\t\t * (0) you can always use --force or +A:B notation to\n+\t\t *     selectively force individual ref pairs.\n+\t\t *\n+\t\t * (1) if the old thing does not exist, it is OK.\n+\t\t *\n+\t\t * (2) if you do not have the old thing, you are not allowed\n+\t\t *     to overwrite it; you would not know what you are losing\n+\t\t *     otherwise.\n+\t\t *\n+\t\t * (3) if both new and old are commit-ish, and new is a\n+\t\t *     descendant of old, it is OK.\n+\t\t *\n+\t\t * (4) regardless of all of the above, removing :B is\n+\t\t *     always allowed.\n+\t\t */\n+\n+\t\tref->nonfastforward =\n+\t\t\t!ref->deletion &&\n+\t\t\t!is_null_sha1(ref->old_sha1) &&\n+\t\t\t(!has_sha1_file(ref->old_sha1)\n+\t\t\t  || !ref_newer(ref->new_sha1, ref->old_sha1));\n+\n+\t\tif (ref->nonfastforward && !ref->force && !force_update) {\n+\t\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\n+\t\t\tcontinue;\n+\t\t}\n+\t}\n+}\n+\n struct branch *branch_get(const char *name)\n {\n \tstruct branch *ret;\ndiff --git a/remote.h b/remote.h\nindex 8b7ecf9..6e13643 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -98,6 +98,8 @@ char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n\n int match_refs(struct ref *src, struct ref **dst,\n \t       int nr_refspec, const char **refspec, int all);\n+void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n+\tint force_update);\n\n /*\n  * Given a list of the remote refs and the specification of things to\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 11f3d7e..6b1f778 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -329,16 +329,15 @@ static int push_refs(struct transport *transport,\n \t\treturn 1;\n\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n-\t\tif (ref->peer_ref)\n-\t\t\thashcpy(ref->new_sha1, ref->peer_ref->new_sha1);\n-\t\telse if (!mirror)\n+\t\tif (!ref->peer_ref && !mirror)\n \t\t\tcontinue;\n\n-\t\tref->deletion = is_null_sha1(ref->new_sha1);\n-\t\tif (!ref->deletion &&\n-\t\t\t!hashcmp(ref->old_sha1, ref->new_sha1)) {\n-\t\t\tref->status = REF_STATUS_UPTODATE;\n+\t\tswitch (ref->status) {\n+\t\tcase REF_STATUS_REJECT_NONFASTFORWARD:\n+\t\tcase REF_STATUS_UPTODATE:\n \t\t\tcontinue;\n+\t\tdefault:\n+\t\t\t; /* do nothing */\n \t\t}\n\n \t\tif (force_all)\ndiff --git a/transport.c b/transport.c\nindex 3eea836..12c4423 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -887,6 +887,10 @@ int transport_push(struct transport *transport,\n \t\t\treturn -1;\n \t\t}\n\n+\t\tset_ref_status_for_push(remote_refs,\n+\t\t\tflags & TRANSPORT_PUSH_MIRROR,\n+\t\t\tflags & TRANSPORT_PUSH_FORCE);\n+\n \t\tret = transport->push_refs(transport, remote_refs, flags);\n\n \t\tif (!quiet || push_had_errors(remote_refs))\n--\n1.6.4.4\n"},{"id":"129543","messageId":"20091208223649.d9ff4fed.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091208223413.98e99d3e.rctay89@gmail.com","subject":"[PATCH v2 2/3] transport.c::transport_push(): make ref status affect return value","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-08T14:36:49Z","receivedAt":"2009-12-08T14:36:49Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Use push_had_errors() to check the refs for errors and modify the\nreturn value.\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n\nRewrote this.\n\n transport.c |    7 +++++--\n 1 files changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/transport.c b/transport.c\nindex 12c4423..9b23989 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -875,7 +875,7 @@ int transport_push(struct transport *transport,\n \t\tint verbose = flags & TRANSPORT_PUSH_VERBOSE;\n \t\tint quiet = flags & TRANSPORT_PUSH_QUIET;\n \t\tint porcelain = flags & TRANSPORT_PUSH_PORCELAIN;\n-\t\tint ret;\n+\t\tint ret, err;\n\n \t\tif (flags & TRANSPORT_PUSH_ALL)\n \t\t\tmatch_flags |= MATCH_REFS_ALL;\n@@ -892,8 +892,11 @@ int transport_push(struct transport *transport,\n \t\t\tflags & TRANSPORT_PUSH_FORCE);\n\n \t\tret = transport->push_refs(transport, remote_refs, flags);\n+\t\terr = push_had_errors(remote_refs);\n\n-\t\tif (!quiet || push_had_errors(remote_refs))\n+\t\tret |= err;\n+\n+\t\tif (!quiet || err)\n \t\t\tprint_push_status(transport->url, remote_refs,\n \t\t\t\t\tverbose | porcelain, porcelain,\n \t\t\t\t\tnonfastforward);\n--\n1.6.4.4\n"},{"id":"129545","messageId":"20091208223733.5528e781.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091208223413.98e99d3e.rctay89@gmail.com","subject":"[PATCH v2 3/3] transport-helper.c::push_refs(): emit \"no refs\" error message","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-08T14:37:33Z","receivedAt":"2009-12-08T14:37:33Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Emit an error message when remote_refs is not set.\n\nThis behaviour is consistent with that of builtin-send-pack.c and\nhttp-push.c.\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n transport-helper.c |    5 ++++-\n 1 files changed, 4 insertions(+), 1 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 6b1f778..96fc48b 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -321,8 +321,11 @@ static int push_refs(struct transport *transport,\n \tstruct child_process *helper;\n \tstruct ref *ref;\n\n-\tif (!remote_refs)\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\treturn 0;\n+\t}\n\n \thelper = get_helper(transport);\n \tif (!data->push)\n--\n1.6.4.4\n"},{"id":"129561","messageId":"alpine.LNX.2.00.0912081158040.14365@iabervon.org","threadId":"21853","inReplyTo":"20091208223543.c7f88afe.rctay89@gmail.com","subject":"Re: [PATCH v2 1/3] refactor ref status logic for pushing","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2009-12-08T17:17:21Z","receivedAt":"2009-12-08T17:17:21Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Tue, 8 Dec 2009, Tay Ray Chuan wrote:\n\n> Move the logic that detects up-to-date and non-fast-forward refs to a\n> new function in remote.[ch], set_ref_status_for_push().\n\nIs there some reason to not have set_ref_status_for_push() be static in \ntransport.c now? (Sorry for not suggesting this before.)\n\nOther than that, it looks good to me.\n\nI think it might be a good idea to have it generate a peer ref for refs \nthat don't have one when mirror is used, which would eliminate a couple \nmore lines, but there's no reason that has to be done in the same patch.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"129594","messageId":"be6fef0d0912081940p63628d5eoab93a1a1f2676baa@mail.gmail.com","threadId":"21853","inReplyTo":"alpine.LNX.2.00.0912081158040.14365@iabervon.org","subject":"Re: [PATCH v2 1/3] refactor ref status logic for pushing","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-09T03:40:40Z","receivedAt":"2009-12-09T03:40:40Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Hi,\n\nOn Wed, Dec 9, 2009 at 1:17 AM, Daniel Barkalow <barkalow@iabervon.org> wrote:\n> On Tue, 8 Dec 2009, Tay Ray Chuan wrote:\n>\n>> Move the logic that detects up-to-date and non-fast-forward refs to a\n>> new function in remote.[ch], set_ref_status_for_push().\n>\n> Is there some reason to not have set_ref_status_for_push() be static in\n> transport.c now? (Sorry for not suggesting this before.)\n\nit can't be static, because builtin-send-pack.c::main() needs it too.\n\n> Other than that, it looks good to me.\n\nThanks.\n\n-- \nCheers,\nRay Chuan\n"},{"id":"129608","messageId":"alpine.LNX.2.00.0912090211020.14365@iabervon.org","threadId":"21853","inReplyTo":"be6fef0d0912081940p63628d5eoab93a1a1f2676baa@mail.gmail.com","subject":"Re: [PATCH v2 1/3] refactor ref status logic for pushing","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2009-12-09T07:13:29Z","receivedAt":"2009-12-09T07:13:29Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Wed, 9 Dec 2009, Tay Ray Chuan wrote:\n\n> Hi,\n> \n> On Wed, Dec 9, 2009 at 1:17 AM, Daniel Barkalow <barkalow@iabervon.org> wrote:\n> > On Tue, 8 Dec 2009, Tay Ray Chuan wrote:\n> >\n> >> Move the logic that detects up-to-date and non-fast-forward refs to a\n> >> new function in remote.[ch], set_ref_status_for_push().\n> >\n> > Is there some reason to not have set_ref_status_for_push() be static in\n> > transport.c now? (Sorry for not suggesting this before.)\n> \n> it can't be static, because builtin-send-pack.c::main() needs it too.\n\nOh, right, fair enough.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"130302","messageId":"20091224154005.a642c8ec.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091208223413.98e99d3e.rctay89@gmail.com","subject":"[PATCH v3 0/6] transport: catch non-fast forwards","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-24T07:40:05Z","receivedAt":"2009-12-24T07:40:05Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Junio, this is re-roll of the 'tr/http-push-ref-status' branch in 'pu'.\n\nSummary:\n  This patch series applies on top of 'next', and deals with alerting\n  the user to rejected non-fast-forward pushes when using helpers\n  (smart).\n\n  Previously, git silently exited. This situation involves the curl\n  helper and the smart protocol. The non-fast-forward push is only\n  detected when curl executes the rpc client (git-send-pack). Now, we\n  detect it before telling the helper to push.\n\nChanges from v2:\n - add tests\n - modify commit messages to mention tests\n - report rejected non-fast-forward pushes for unmatched refs\n   (without an explicit refspec) by the remote helper (see patches #2\n   and #5).\n\n   This remedies the scenario where a user would not be aware of\n   rejected non-fast-forward pushes.\n\n   It occurs when\n     1) there are one or more pushes that succeed and\n     2) there are one or more rejected non-fast-forward pushes that\n\t    involve refs that cannot be matched without explicit refspecs.\n\n   This is due to the re-marking of ref status in transport-helper.c::\n   push_refs() when interacting with the remote helper.\n\n   If only non-matched, non-fast-forward refs are involved (ie.\n   condition #2 is present without #1), then the situtation does not\n   occur - no 'push' commands are passed to the remote helper, no\n   interaction with the helper takes place, and no re-marking of ref\n   status takes place. The user will be alerted to the rejected\n   non-fast-forward push.\n\n   If both are present, re-marking occurs. Even if a ref was not part\n   of a 'push' command to the helper, 'ok'/'error' status reports might\n   be made for it, since the remote helper does ref matching on its\n   side. This led to refs that cannot be matched without an explicit\n   refspec marked as REF_STATUS_REJECT_NONFASTFORWARD to be re-marked\n   REF_STATUS_NONE.\n\n   (Note: helpers fail to match refs as explicit refspecs are always\n   available to the top-level transport mechanism, but only on a need-\n   to-know basis to the remote helper via a 'push' command when a ref\n   is to be pushed.)\n\n   Interestingly, it is possible for a user to be forever unaware of\n   the rejected push if the user relies on information from git push\n   alone. As long as the user ensures that one or more other pushes are\n   successful (eg. change a tracked ref fast-forwardedly) between the\n   execution of git push, the rejected non-fast-forward push won't be\n   reported.\n\nTay Ray Chuan (6):\n  t5541-http-push.sh: add tests for non-fast-forward pushes\n  t5541-http-push.sh: add test for unmatched, non-fast-forwarded refs\n  refactor ref status logic for pushing\n  transport.c::transport_push(): make ref status affect return value\n  transport-helper.c::push_refs(): ignore helper-reported status if ref\n    is not to be pushed\n  transport-helper.c::push_refs(): emit \"no refs\" error message\n\n builtin-send-pack.c  |   50 +++++++++++---------------------------------------\n remote.c             |   50 ++++++++++++++++++++++++++++++++++++++++++++++++++\n remote.h             |    2 ++\n t/t5541-http-push.sh |   43 +++++++++++++++++++++++++++++++++++++++++++\n transport-helper.c   |   32 ++++++++++++++++++++++----------\n transport.c          |   11 +++++++++--\n 6 files changed, 137 insertions(+), 51 deletions(-)\n\n--\nCheers,\nRay Chuan\n"},{"id":"130308","messageId":"20091224154057.33611ae7.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091224154005.a642c8ec.rctay89@gmail.com","subject":"[PATCH v3 1/6] t5541-http-push.sh: add tests for non-fast-forward pushes","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-24T07:40:57Z","receivedAt":"2009-12-24T07:40:57Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n t/t5541-http-push.sh |   23 +++++++++++++++++++++++\n 1 files changed, 23 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh\nindex 2a58d0c..f49c7c4 100755\n--- a/t/t5541-http-push.sh\n+++ b/t/t5541-http-push.sh\n@@ -88,5 +88,28 @@ test_expect_success 'used receive-pack service' '\n \ttest_cmp exp act\n '\n\n+test_expect_success 'non-fast-forward push fails' '\n+\tcd \"$ROOT_PATH\"/test_repo_clone &&\n+\tgit checkout master &&\n+\techo \"changed\" > path2 &&\n+\tgit commit -a -m path2 --amend &&\n+\n+\tHEAD=$(git rev-parse --verify HEAD) &&\n+\t!(git push -v origin >output 2>&1) &&\n+\t(cd \"$HTTPD_DOCUMENT_ROOT_PATH\"/test_repo.git &&\n+\t test $HEAD != $(git rev-parse --verify HEAD))\n+'\n+\n+test_expect_failure 'non-fast-forward push show ref status' '\n+\tgrep \"^ ! \\[rejected\\][ ]*master -> master (non-fast-forward)$\" output\n+'\n+\n+test_expect_failure 'non-fast-forward push shows help message' '\n+\tgrep \\\n+\"To prevent you from losing history, non-fast-forward updates were rejected\n+Merge the remote changes before pushing again.  See the '\"'non-fast-forward'\"'\n+section of '\"'git push --help'\"' for details.\" output\n+'\n+\n stop_httpd\n test_done\n--\n1.6.6.rc1.249.g048b3\n"},{"id":"130303","messageId":"20091224154158.15ba580f.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091224154057.33611ae7.rctay89@gmail.com","subject":"[PATCH v3 2/6] t5541-http-push.sh: add test for unmatched, non-fast-forwarded refs","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-24T07:41:58Z","receivedAt":"2009-12-24T07:41:58Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Test that when non-fast-forwarded refs cannot be matched without an\nexplicit refspect, the push fails with a non-fast-forward ref status and\nhelp message.\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n t/t5541-http-push.sh |   20 ++++++++++++++++++++\n 1 files changed, 20 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh\nindex f49c7c4..5ebe04a 100755\n--- a/t/t5541-http-push.sh\n+++ b/t/t5541-http-push.sh\n@@ -111,5 +111,25 @@ Merge the remote changes before pushing again.  See the '\"'non-fast-forward'\"'\n section of '\"'git push --help'\"' for details.\" output\n '\n\n+test_expect_failure 'push fails for non-fast-forward refs unmatched by remote helper' '\n+\t# create a dissimilarly-named ref so that git is unable to match the refs\n+\tgit push origin master:retsam\n+\n+\techo \"change changed\" > path2 &&\n+\tgit commit -a -m path2 --amend &&\n+\n+\t# push master too. This ensures there is at least one '\"'push'\"' command to\n+\t# the remote helper and triggers interaction with the helper.\n+\t!(git push -v origin +master master:retsam >output 2>&1) &&\n+\n+\tgrep \"^ + [a-z0-9]\\+\\.\\.\\.[a-z0-9]\\+[ ]*master -> master (forced update)$\" output &&\n+\tgrep \"^ ! \\[rejected\\][ ]*master -> retsam (non-fast-forward)$\" output &&\n+\n+\tgrep \\\n+\"To prevent you from losing history, non-fast-forward updates were rejected\n+Merge the remote changes before pushing again.  See the '\"'non-fast-forward'\"'\n+section of '\"'git push --help'\"' for details.\" output\n+'\n+\n stop_httpd\n test_done\n--\n1.6.6.rc1.249.g048b3\n"},{"id":"130304","messageId":"20091224154258.08b4fe44.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091224154158.15ba580f.rctay89@gmail.com","subject":"[PATCH v3 3/6] refactor ref status logic for pushing","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-24T07:42:58Z","receivedAt":"2009-12-24T07:42:58Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Move the logic that detects up-to-date and non-fast-forward refs to a\nnew function in remote.[ch], set_ref_status_for_push().\n\nMake transport_push() invoke set_ref_status_for_push() before invoking\nthe push_refs() implementation. (As a side-effect, the push_refs()\nimplementation in transport-helper.c now knows of non-fast-forward\npushes.)\n\nRemoved logic for detecting up-to-date refs from the push_refs()\nimplementation in transport-helper.c, as transport_push() has already\ndone so for it.\n\nMake cmd_send_pack() invoke set_ref_status_for_push() before invoking\nsend_pack(), as transport_push() can't do it for send_pack() here.\n\nMark the test on the return status of non-fast-forward push to fail.\nGit now exits with success, as transport.c::transport_push() does not\ncheck for refs with status REF_STATUS_REJECT_NONFASTFORWARD nor does it\nindicate rejected pushes with its return value.\n\nMark the test for ref status to succeed. As mentioned earlier, refs\nmight be marked as non-fast-forwards, triggering the push status\nprinting mechanism in transport.c.\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n\nChanged nothing from v2 except tests and mentioning it in the commit\nmessage.\n\n builtin-send-pack.c  |   50 +++++++++++---------------------------------------\n remote.c             |   50 ++++++++++++++++++++++++++++++++++++++++++++++++++\n remote.h             |    2 ++\n t/t5541-http-push.sh |    4 ++--\n transport-helper.c   |   13 ++++++-------\n transport.c          |    4 ++++\n 6 files changed, 75 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin-send-pack.c b/builtin-send-pack.c\nindex 8fffdbf..38580c3 100644\n--- a/builtin-send-pack.c\n+++ b/builtin-send-pack.c\n@@ -406,50 +406,19 @@ int send_pack(struct send_pack_args *args,\n \t */\n \tnew_refs = 0;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\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\tif (!ref->peer_ref && !args->send_mirror)\n \t\t\tcontinue;\n\n-\t\tref->deletion = is_null_sha1(ref->new_sha1);\n-\t\tif (ref->deletion && !allow_deleting_refs) {\n-\t\t\tref->status = REF_STATUS_REJECT_NODELETE;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!ref->deletion &&\n-\t\t    !hashcmp(ref->old_sha1, ref->new_sha1)) {\n-\t\t\tref->status = REF_STATUS_UPTODATE;\n+\t\tswitch (ref->status) {\n+\t\tcase REF_STATUS_REJECT_NONFASTFORWARD:\n+\t\tcase REF_STATUS_UPTODATE:\n \t\t\tcontinue;\n+\t\tdefault:\n+\t\t\t; /* do nothing */\n \t\t}\n\n-\t\t/* This part determines what can overwrite what.\n-\t\t * The rules are:\n-\t\t *\n-\t\t * (0) you can always use --force or +A:B notation to\n-\t\t *     selectively force individual ref pairs.\n-\t\t *\n-\t\t * (1) if the old thing does not exist, it is OK.\n-\t\t *\n-\t\t * (2) if you do not have the old thing, you are not allowed\n-\t\t *     to overwrite it; you would not know what you are losing\n-\t\t *     otherwise.\n-\t\t *\n-\t\t * (3) if both new and old are commit-ish, and new is a\n-\t\t *     descendant of old, it is OK.\n-\t\t *\n-\t\t * (4) regardless of all of the above, removing :B is\n-\t\t *     always allowed.\n-\t\t */\n-\n-\t\tref->nonfastforward =\n-\t\t    !ref->deletion &&\n-\t\t    !is_null_sha1(ref->old_sha1) &&\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\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\n+\t\tif (ref->deletion && !allow_deleting_refs) {\n+\t\t\tref->status = REF_STATUS_REJECT_NODELETE;\n \t\t\tcontinue;\n \t\t}\n\n@@ -673,6 +642,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \tif (match_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))\n \t\treturn -1;\n\n+\tset_ref_status_for_push(remote_refs, args.send_mirror,\n+\t\targs.force_update);\n+\n \tret = send_pack(&args, fd, conn, remote_refs, &extra_have);\n\n \tif (helper_status)\ndiff --git a/remote.c b/remote.c\nindex e3afecd..c70181c 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1247,6 +1247,56 @@ int match_refs(struct ref *src, struct ref **dst,\n \treturn 0;\n }\n\n+void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n+\tint force_update)\n+{\n+\tstruct ref *ref;\n+\n+\tfor (ref = remote_refs; ref; ref = ref->next) {\n+\t\tif (ref->peer_ref)\n+\t\t\thashcpy(ref->new_sha1, ref->peer_ref->new_sha1);\n+\t\telse if (!send_mirror)\n+\t\t\tcontinue;\n+\n+\t\tref->deletion = is_null_sha1(ref->new_sha1);\n+\t\tif (!ref->deletion &&\n+\t\t\t!hashcmp(ref->old_sha1, ref->new_sha1)) {\n+\t\t\tref->status = REF_STATUS_UPTODATE;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* This part determines what can overwrite what.\n+\t\t * The rules are:\n+\t\t *\n+\t\t * (0) you can always use --force or +A:B notation to\n+\t\t *     selectively force individual ref pairs.\n+\t\t *\n+\t\t * (1) if the old thing does not exist, it is OK.\n+\t\t *\n+\t\t * (2) if you do not have the old thing, you are not allowed\n+\t\t *     to overwrite it; you would not know what you are losing\n+\t\t *     otherwise.\n+\t\t *\n+\t\t * (3) if both new and old are commit-ish, and new is a\n+\t\t *     descendant of old, it is OK.\n+\t\t *\n+\t\t * (4) regardless of all of the above, removing :B is\n+\t\t *     always allowed.\n+\t\t */\n+\n+\t\tref->nonfastforward =\n+\t\t\t!ref->deletion &&\n+\t\t\t!is_null_sha1(ref->old_sha1) &&\n+\t\t\t(!has_sha1_file(ref->old_sha1)\n+\t\t\t  || !ref_newer(ref->new_sha1, ref->old_sha1));\n+\n+\t\tif (ref->nonfastforward && !ref->force && !force_update) {\n+\t\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\n+\t\t\tcontinue;\n+\t\t}\n+\t}\n+}\n+\n struct branch *branch_get(const char *name)\n {\n \tstruct branch *ret;\ndiff --git a/remote.h b/remote.h\nindex 8b7ecf9..6e13643 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -98,6 +98,8 @@ char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n\n int match_refs(struct ref *src, struct ref **dst,\n \t       int nr_refspec, const char **refspec, int all);\n+void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n+\tint force_update);\n\n /*\n  * Given a list of the remote refs and the specification of things to\ndiff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh\nindex 5ebe04a..86dbcb2 100755\n--- a/t/t5541-http-push.sh\n+++ b/t/t5541-http-push.sh\n@@ -88,7 +88,7 @@ test_expect_success 'used receive-pack service' '\n \ttest_cmp exp act\n '\n\n-test_expect_success 'non-fast-forward push fails' '\n+test_expect_failure 'non-fast-forward push fails' '\n \tcd \"$ROOT_PATH\"/test_repo_clone &&\n \tgit checkout master &&\n \techo \"changed\" > path2 &&\n@@ -100,7 +100,7 @@ test_expect_success 'non-fast-forward push fails' '\n \t test $HEAD != $(git rev-parse --verify HEAD))\n '\n\n-test_expect_failure 'non-fast-forward push show ref status' '\n+test_expect_success 'non-fast-forward push show ref status' '\n \tgrep \"^ ! \\[rejected\\][ ]*master -> master (non-fast-forward)$\" output\n '\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 11f3d7e..6b1f778 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -329,16 +329,15 @@ static int push_refs(struct transport *transport,\n \t\treturn 1;\n\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n-\t\tif (ref->peer_ref)\n-\t\t\thashcpy(ref->new_sha1, ref->peer_ref->new_sha1);\n-\t\telse if (!mirror)\n+\t\tif (!ref->peer_ref && !mirror)\n \t\t\tcontinue;\n\n-\t\tref->deletion = is_null_sha1(ref->new_sha1);\n-\t\tif (!ref->deletion &&\n-\t\t\t!hashcmp(ref->old_sha1, ref->new_sha1)) {\n-\t\t\tref->status = REF_STATUS_UPTODATE;\n+\t\tswitch (ref->status) {\n+\t\tcase REF_STATUS_REJECT_NONFASTFORWARD:\n+\t\tcase REF_STATUS_UPTODATE:\n \t\t\tcontinue;\n+\t\tdefault:\n+\t\t\t; /* do nothing */\n \t\t}\n\n \t\tif (force_all)\ndiff --git a/transport.c b/transport.c\nindex 3eea836..12c4423 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -887,6 +887,10 @@ int transport_push(struct transport *transport,\n \t\t\treturn -1;\n \t\t}\n\n+\t\tset_ref_status_for_push(remote_refs,\n+\t\t\tflags & TRANSPORT_PUSH_MIRROR,\n+\t\t\tflags & TRANSPORT_PUSH_FORCE);\n+\n \t\tret = transport->push_refs(transport, remote_refs, flags);\n\n \t\tif (!quiet || push_had_errors(remote_refs))\n--\n1.6.6.rc1.249.g048b3\n"},{"id":"130305","messageId":"20091224154352.ecefd242.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091224154258.08b4fe44.rctay89@gmail.com","subject":"[PATCH v3 4/6] transport.c::transport_push(): make ref status affect return value","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-24T07:43:52Z","receivedAt":"2009-12-24T07:43:52Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Use push_had_errors() to check the refs for errors and modify the\nreturn value.\n\nMark the non-fast-forward push tests to succeed.\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n\nChanged nothing from v2 except tests and mentioning it in the commit\nmessage.\n\n t/t5541-http-push.sh |    4 ++--\n transport.c          |    7 +++++--\n 2 files changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh\nindex 86dbcb2..fee9494 100755\n--- a/t/t5541-http-push.sh\n+++ b/t/t5541-http-push.sh\n@@ -88,7 +88,7 @@ test_expect_success 'used receive-pack service' '\n \ttest_cmp exp act\n '\n\n-test_expect_failure 'non-fast-forward push fails' '\n+test_expect_success 'non-fast-forward push fails' '\n \tcd \"$ROOT_PATH\"/test_repo_clone &&\n \tgit checkout master &&\n \techo \"changed\" > path2 &&\n@@ -104,7 +104,7 @@ test_expect_success 'non-fast-forward push show ref status' '\n \tgrep \"^ ! \\[rejected\\][ ]*master -> master (non-fast-forward)$\" output\n '\n\n-test_expect_failure 'non-fast-forward push shows help message' '\n+test_expect_success 'non-fast-forward push shows help message' '\n \tgrep \\\n \"To prevent you from losing history, non-fast-forward updates were rejected\n Merge the remote changes before pushing again.  See the '\"'non-fast-forward'\"'\ndiff --git a/transport.c b/transport.c\nindex 12c4423..9b23989 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -875,7 +875,7 @@ int transport_push(struct transport *transport,\n \t\tint verbose = flags & TRANSPORT_PUSH_VERBOSE;\n \t\tint quiet = flags & TRANSPORT_PUSH_QUIET;\n \t\tint porcelain = flags & TRANSPORT_PUSH_PORCELAIN;\n-\t\tint ret;\n+\t\tint ret, err;\n\n \t\tif (flags & TRANSPORT_PUSH_ALL)\n \t\t\tmatch_flags |= MATCH_REFS_ALL;\n@@ -892,8 +892,11 @@ int transport_push(struct transport *transport,\n \t\t\tflags & TRANSPORT_PUSH_FORCE);\n\n \t\tret = transport->push_refs(transport, remote_refs, flags);\n+\t\terr = push_had_errors(remote_refs);\n\n-\t\tif (!quiet || push_had_errors(remote_refs))\n+\t\tret |= err;\n+\n+\t\tif (!quiet || err)\n \t\t\tprint_push_status(transport->url, remote_refs,\n \t\t\t\t\tverbose | porcelain, porcelain,\n \t\t\t\t\tnonfastforward);\n--\n1.6.6.rc1.249.g048b3\n"},{"id":"130306","messageId":"20091224154445.ad4b7a01.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091224154352.ecefd242.rctay89@gmail.com","subject":"[PATCH v3 5/6] transport-helper.c::push_refs(): ignore helper-reported status if ref is not to be pushed","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-24T07:44:45Z","receivedAt":"2009-12-24T07:44:45Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"If the status of a ref is REF_STATUS_REJECT_NONFASTFORWARD or\nREF_STATUS_UPTODATE, the remote helper will not be told to push the ref\n(via a 'push' command).\n\nTherefore, if a ref is not to be pushed, ignore the status report by the\nremote helper for that ref - don't overwrite the status of the ref with\nthe status reported by the helper, nor the message in the remote_status\nmember.\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n t/t5541-http-push.sh |    2 +-\n transport-helper.c   |   14 ++++++++++++--\n 2 files changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh\nindex fee9494..79867bc 100755\n--- a/t/t5541-http-push.sh\n+++ b/t/t5541-http-push.sh\n@@ -111,7 +111,7 @@ Merge the remote changes before pushing again.  See the '\"'non-fast-forward'\"'\n section of '\"'git push --help'\"' for details.\" output\n '\n\n-test_expect_failure 'push fails for non-fast-forward refs unmatched by remote helper' '\n+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '\n \t# create a dissimilarly-named ref so that git is unable to match the refs\n \tgit push origin master:retsam\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 6b1f778..bdfa07e 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -429,8 +429,18 @@ static int push_refs(struct transport *transport,\n \t\t\tcontinue;\n \t\t}\n\n-\t\tref->status = status;\n-\t\tref->remote_status = msg;\n+\t\tswitch (ref->status) {\n+\t\tcase REF_STATUS_REJECT_NONFASTFORWARD:\n+\t\tcase REF_STATUS_UPTODATE:\n+\t\t\t/*\n+\t\t\t * Earlier, the ref was marked not to be pushed, so ignore what\n+\t\t\t * the remote helper said about the ref.\n+\t\t\t */\n+\t\t\tcontinue;\n+\t\tdefault:\n+\t\t\tref->status = status;\n+\t\t\tref->remote_status = msg;\n+\t\t}\n \t}\n \tstrbuf_release(&buf);\n \treturn 0;\n--\n1.6.6.rc1.249.g048b3\n"},{"id":"130307","messageId":"20091224154530.7fbdff2e.rctay89@gmail.com","threadId":"21853","inReplyTo":"20091224154445.ad4b7a01.rctay89@gmail.com","subject":"[PATCH v3 6/6] transport-helper.c::push_refs(): emit \"no refs\" error message","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2009-12-24T07:45:30Z","receivedAt":"2009-12-24T07:45:30Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Emit an error message when remote_refs is not set.\n\nThis behaviour is consistent with that of builtin-send-pack.c and\nhttp-push.c.\n\nSigned-off-by: Tay Ray Chuan <rctay89@gmail.com>\n---\n\nChanged nothing from v2.\n\n transport-helper.c |    5 ++++-\n 1 files changed, 4 insertions(+), 1 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex bdfa07e..5910384 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -321,8 +321,11 @@ static int push_refs(struct transport *transport,\n \tstruct child_process *helper;\n \tstruct ref *ref;\n\n-\tif (!remote_refs)\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\treturn 0;\n+\t}\n\n \thelper = get_helper(transport);\n \tif (!data->push)\n--\n1.6.6.rc1.249.g048b3\n"},{"id":"130820","messageId":"20100105063253.GA19368@coredump.intra.peff.net","threadId":"21853","inReplyTo":"20091224154445.ad4b7a01.rctay89@gmail.com","subject":"Re: [PATCH v3 5/6] transport-helper.c::push_refs(): ignore helper-reported status if ref is not to be pushed","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-01-05T06:32:53Z","receivedAt":"2010-01-05T06:32:53Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 24, 2009 at 03:44:45PM +0800, Tay Ray Chuan wrote:\n\n> -\t\tref->status = status;\n> -\t\tref->remote_status = msg;\n> +\t\tswitch (ref->status) {\n> +\t\tcase REF_STATUS_REJECT_NONFASTFORWARD:\n> +\t\tcase REF_STATUS_UPTODATE:\n> +\t\t\t/*\n> +\t\t\t * Earlier, the ref was marked not to be pushed, so ignore what\n> +\t\t\t * the remote helper said about the ref.\n> +\t\t\t */\n> +\t\t\tcontinue;\n> +\t\tdefault:\n> +\t\t\tref->status = status;\n> +\t\t\tref->remote_status = msg;\n> +\t\t}\n\nIt seems like this should be checking for REF_STATUS_NONE explicitly\ninstead of trying to enumerate the reasons we might not have tried to\npush. Shouldn't helpers _only_ be pushing REF_STATUS_NONE refs?\n\nI think right now the two cases are equivalent, since non-ff and\nuptodate are the only two states set before the helper is invoked. But\nwe have discussed in the past (and I still have a patch floating around\nfor) a REF_STATUS_REWIND which would treat strict rewinds differently\n(silently ignoring them instead of making an error). Explicitly checking\nREF_STATUS_NONE future-proofs against new states being added.\n\n-Peff\n"},{"id":"130821","messageId":"20100105063546.GB19368@coredump.intra.peff.net","threadId":"21853","inReplyTo":"20091224154158.15ba580f.rctay89@gmail.com","subject":"Re: [PATCH v3 2/6] t5541-http-push.sh: add test for unmatched, non-fast-forwarded refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-01-05T06:35:46Z","receivedAt":"2010-01-05T06:35:46Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 24, 2009 at 03:41:58PM +0800, Tay Ray Chuan wrote:\n\n> Test that when non-fast-forwarded refs cannot be matched without an\n> explicit refspect, the push fails with a non-fast-forward ref status and\n> help message.\n\nI don't understand what you're testing here. If it's not matched, then\nhow is it a non-fast-forward? Isn't it simply unmatched?\n\nYour test:\n\n> +test_expect_failure 'push fails for non-fast-forward refs unmatched by remote helper' '\n> +\t# create a dissimilarly-named ref so that git is unable to match the refs\n> +\tgit push origin master:retsam\n> +\n> +\techo \"change changed\" > path2 &&\n> +\tgit commit -a -m path2 --amend &&\n> +\n> +\t# push master too. This ensures there is at least one '\"'push'\"' command to\n> +\t# the remote helper and triggers interaction with the helper.\n> +\t!(git push -v origin +master master:retsam >output 2>&1) &&\n> +\n> +\tgrep \"^ + [a-z0-9]\\+\\.\\.\\.[a-z0-9]\\+[ ]*master -> master (forced update)$\" output &&\n> +\tgrep \"^ ! \\[rejected\\][ ]*master -> retsam (non-fast-forward)$\" output &&\n\nLooks like you're just testing the usual \"master -> retsam is not a\nfast-forward\" case. I don't understand how this is different from the\nprevious tests. Can you elaborate?\n\n-Peff\n"},{"id":"130833","messageId":"20100105180113.6e0572dc.rctay89@gmail.com","threadId":"21853","inReplyTo":"20100105063253.GA19368@coredump.intra.peff.net","subject":"Re: [PATCH v3 5/6] transport-helper.c::push_refs(): ignore helper-reported status if ref is not to be pushed","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-01-05T10:01:13Z","receivedAt":"2010-01-05T10:01:13Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Hi,\n\nOn Tue, Jan 5, 2010 at 2:32 PM, Jeff King <peff@peff.net> wrote:\n> On Thu, Dec 24, 2009 at 03:44:45PM +0800, Tay Ray Chuan wrote:\n>\n>> -             ref->status = status;\n>> -             ref->remote_status = msg;\n>> +             switch (ref->status) {\n>> +             case REF_STATUS_REJECT_NONFASTFORWARD:\n>> +             case REF_STATUS_UPTODATE:\n>> +                     /*\n>> +                      * Earlier, the ref was marked not to be pushed, so ignore what\n>> +                      * the remote helper said about the ref.\n>> +                      */\n>> +                     continue;\n>> +             default:\n>> +                     ref->status = status;\n>> +                     ref->remote_status = msg;\n>> +             }\n>\n> It seems like this should be checking for REF_STATUS_NONE explicitly\n> instead of trying to enumerate the reasons we might not have tried to\n> push. Shouldn't helpers _only_ be pushing REF_STATUS_NONE refs?\n>\n> I think right now the two cases are equivalent, since non-ff and\n> uptodate are the only two states set before the helper is invoked. But\n> we have discussed in the past (and I still have a patch floating around\n> for) a REF_STATUS_REWIND which would treat strict rewinds differently\n> (silently ignoring them instead of making an error). Explicitly checking\n> REF_STATUS_NONE future-proofs against new states being added.\n\nI'm not really sure if this is true (ie. that if status is not non-ff\nor uptodate, then it is REF_STATUS_NONE), but we could step around this\nby introducing a property, say, ref->should_push, that is set to 1,\nafter all the vetting has been carried out and just before we talk to\nthe server.\n\nThat way, we just check that property, without having to know the ref\nstatuses that would mark a ref not-for-pushing. Sounds future-proof (in\nyour sense) to me.\n\n--\nCheers,\nRay Chuan\n"},{"id":"130834","messageId":"20100105180132.e573fff2.rctay89@gmail.com","threadId":"21853","inReplyTo":"20100105063546.GB19368@coredump.intra.peff.net","subject":"Re: [PATCH v3 2/6] t5541-http-push.sh: add test for unmatched, non-fast-forwarded refs","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-01-05T10:01:32Z","receivedAt":"2010-01-05T10:01:32Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Hi,\n\nOn Tue, Jan 5, 2010 at 2:35 PM, Jeff King <peff@peff.net> wrote:\n> On Thu, Dec 24, 2009 at 03:41:58PM +0800, Tay Ray Chuan wrote:\n>\n>> Test that when non-fast-forwarded refs cannot be matched without an\n>> explicit refspect, the push fails with a non-fast-forward ref status and\n>> help message.\n>\n> I don't understand what you're testing here. If it's not matched, then\n> how is it a non-fast-forward? Isn't it simply unmatched?\n\nLet me rephrase this as:\n\n  Some refs can only be matched to a remote ref with an explicit\n  refspec. When such a ref is a non-fast-forward of its remote ref,\n  test that pushing them (with the explicit refspec specified) fails\n  with a non-fast-foward-type error.\n\n> Your test:\n>\n>> +test_expect_failure 'push fails for non-fast-forward refs unmatched by remote helper' '\n>> +     # create a dissimilarly-named ref so that git is unable to match the refs\n>> +     git push origin master:retsam\n>> +\n>> +     echo \"change changed\" > path2 &&\n>> +     git commit -a -m path2 --amend &&\n>> +\n>> +     # push master too. This ensures there is at least one '\"'push'\"' command to\n>> +     # the remote helper and triggers interaction with the helper.\n>> +     !(git push -v origin +master master:retsam >output 2>&1) &&\n>> +\n>> +     grep \"^ + [a-z0-9]\\+\\.\\.\\.[a-z0-9]\\+[ ]*master -> master (forced update)$\" output &&\n>> +     grep \"^ ! \\[rejected\\][ ]*master -> retsam (non-fast-forward)$\" output &&\n>\n> Looks like you're just testing the usual \"master -> retsam is not a\n> fast-forward\" case. I don't understand how this is different from the\n> previous tests. Can you elaborate?\n\nThe problem in question is that a non-fast-forward error is not being\nreported, and this test sets up a situation to trigger this - it's not\nmeant to be just another non-fast-forward test.\n\n-- \nCheers,\nRay Chuan\n"},{"id":"130880","messageId":"7viqbgoxgg.fsf@alter.siamese.dyndns.org","threadId":"21853","inReplyTo":"20091224154158.15ba580f.rctay89@gmail.com","subject":"Re: [PATCH v3 2/6] t5541-http-push.sh: add test for unmatched, non-fast-forwarded refs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-06T01:04:31Z","receivedAt":"2010-01-06T01:04:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Tay Ray Chuan <rctay89@gmail.com> writes:\n\n> Test that when non-fast-forwarded refs cannot be matched without an\n> explicit refspect, the push fails with a non-fast-forward ref status and\n> help message.\n>\n> Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>\n> ---\n>  t/t5541-http-push.sh |   20 ++++++++++++++++++++\n>  1 files changed, 20 insertions(+), 0 deletions(-)\n>\n> diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh\n> index f49c7c4..5ebe04a 100755\n> --- a/t/t5541-http-push.sh\n> +++ b/t/t5541-http-push.sh\n> @@ -111,5 +111,25 @@ Merge the remote changes before pushing again.  See the '\"'non-fast-forward'\"'\n>  section of '\"'git push --help'\"' for details.\" output\n>  '\n>\n> +test_expect_failure 'push fails for non-fast-forward refs unmatched by remote helper' '\n> +\t# create a dissimilarly-named ref so that git is unable to match the refs\n> +\tgit push origin master:retsam\n> +\n> +\techo \"change changed\" > path2 &&\n> +\tgit commit -a -m path2 --amend &&\n> +\n> +\t# push master too. This ensures there is at least one '\"'push'\"' command to\n> +\t# the remote helper and triggers interaction with the helper.\n> +\t!(git push -v origin +master master:retsam >output 2>&1) &&\n\nA dumb question.  Why is this done in a sub-shell?\n\n> +\n> +\tgrep \"^ + [a-z0-9]\\+\\.\\.\\.[a-z0-9]\\+[ ]*master -> master (forced update)$\" output &&\n> +\tgrep \"^ ! \\[rejected\\][ ]*master -> retsam (non-fast-forward)$\" output &&\n\n[a-z0-9] seems too broad to catch hexadecimal.  \"\\+\" to introduce ERE\nelements to grep that expects BRE is a GNU extension, IIRC.  You could use\negrep if you really want to say one-or-more, but I think in this case it\nis better to simply replace it with a zero-or-more '*'.  Why is a single\nSP made into character class with \"[ ]\" pair?\n\n> +\n> +\tgrep \\\n> +\"To prevent you from losing history, non-fast-forward updates were rejected\n> +Merge the remote changes before pushing again.  See the '\"'non-fast-forward'\"'\n> +section of '\"'git push --help'\"' for details.\" output\n> +'\n> +\n>  stop_httpd\n>  test_done\n> --\n> 1.6.6.rc1.249.g048b3\n"},{"id":"130889","messageId":"be6fef0d1001051812nce6ab9aic3cd71e2e5188c1a@mail.gmail.com","threadId":"21853","inReplyTo":"7viqbgoxgg.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 2/6] t5541-http-push.sh: add test for unmatched, non-fast-forwarded refs","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-01-06T02:12:57Z","receivedAt":"2010-01-06T02:12:57Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Hi,\n\nOn Wed, Jan 6, 2010 at 9:04 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Tay Ray Chuan <rctay89@gmail.com> writes:\n>\n>> Test that when non-fast-forwarded refs cannot be matched without an\n>> explicit refspect, the push fails with a non-fast-forward ref status and\n>> help message.\n>>\n>> Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>\n>> ---\n>>  t/t5541-http-push.sh |   20 ++++++++++++++++++++\n>>  1 files changed, 20 insertions(+), 0 deletions(-)\n>>\n>> diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh\n>> index f49c7c4..5ebe04a 100755\n>> --- a/t/t5541-http-push.sh\n>> +++ b/t/t5541-http-push.sh\n>> @@ -111,5 +111,25 @@ Merge the remote changes before pushing again.  See the '\"'non-fast-forward'\"'\n>>  section of '\"'git push --help'\"' for details.\" output\n>>  '\n>>\n>> +test_expect_failure 'push fails for non-fast-forward refs unmatched by remote helper' '\n>> +     # create a dissimilarly-named ref so that git is unable to match the refs\n>> +     git push origin master:retsam\n>> +\n>> +     echo \"change changed\" > path2 &&\n>> +     git commit -a -m path2 --amend &&\n>> +\n>> +     # push master too. This ensures there is at least one '\"'push'\"' command to\n>> +     # the remote helper and triggers interaction with the helper.\n>> +     !(git push -v origin +master master:retsam >output 2>&1) &&\n>\n> A dumb question.  Why is this done in a sub-shell?\n>\n>> +\n>> +     grep \"^ + [a-z0-9]\\+\\.\\.\\.[a-z0-9]\\+[ ]*master -> master (forced update)$\" output &&\n>> +     grep \"^ ! \\[rejected\\][ ]*master -> retsam (non-fast-forward)$\" output &&\n>\n> [a-z0-9] seems too broad to catch hexadecimal.\n\nOops, my bad.\n\n>  \"\\+\" to introduce ERE\n> elements to grep that expects BRE is a GNU extension, IIRC.  You could use\n> egrep if you really want to say one-or-more, but I think in this case it\n> is better to simply replace it with a zero-or-more '*'.\n\nOk.\n\n>  Why is a single\n> SP made into character class with \"[ ]\" pair?\n\nTo make it clearer that I'm trying to match a SP.\n\n-- \nCheers,\nRay Chuan\n"},{"id":"130914","messageId":"20100106120456.GA7221@coredump.intra.peff.net","threadId":"21853","inReplyTo":"20100105180113.6e0572dc.rctay89@gmail.com","subject":"Re: [PATCH v3 5/6] transport-helper.c::push_refs(): ignore helper-reported status if ref is not to be pushed","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-01-06T12:04:56Z","receivedAt":"2010-01-06T12:04:56Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jan 05, 2010 at 06:01:13PM +0800, Tay Ray Chuan wrote:\n\n> > It seems like this should be checking for REF_STATUS_NONE explicitly\n> > instead of trying to enumerate the reasons we might not have tried to\n> > push. Shouldn't helpers _only_ be pushing REF_STATUS_NONE refs?\n> >\n> > I think right now the two cases are equivalent, since non-ff and\n> > uptodate are the only two states set before the helper is invoked. But\n> > we have discussed in the past (and I still have a patch floating around\n> > for) a REF_STATUS_REWIND which would treat strict rewinds differently\n> > (silently ignoring them instead of making an error). Explicitly checking\n> > REF_STATUS_NONE future-proofs against new states being added.\n> \n> I'm not really sure if this is true (ie. that if status is not non-ff\n> or uptodate, then it is REF_STATUS_NONE), but we could step around this\n\nWell, consider it this way. If it's _not_ REF_STATUS_NONE, then what is\nit, and what does it mean to be overwriting it?\n\nMaybe I am misunderstanding the problem the patch is addressing, but the\npoint of these REF_STATUS feels was to act as a small state machine.\nEverything starts as NONE, and then:\n\n  - we compare locally against remote refs. We may transition:\n      NONE -> UPTODATE\n      NONE -> REJECT_NONFASTFORWARD\n      NONE -> REJECT_NODELETE\n\n  - we send the push list\n      NONE -> EXPECTING_REPORT (if the remote supports individual status)\n      NONE -> OK (otherwise)\n\n  - we get back status responses\n      EXPECTING_REPORT -> OK\n      EXPECTING_REPORT -> REMOTE_REJECT\n\nI haven't looked closely at the new transport helper code, but I would\nthink it should stick more or less to those transitions. The exception\nwould be that some transports don't necessarily handle EXPECTING_REPORT\nin the same way, and may transition directly from NONE to\nOK/REMOTE_REJECT.\n\nSo offhand, I would say that your list should also probably include\nREJECT_NODELETE. However, I think that status is just for old servers\nwhich didn't support the delete-refs protocol extension. So presumably\nthat is none of the new helpers, as they all post-date the addition of\nthat feature by quite a few years.\n\n> by introducing a property, say, ref->should_push, that is set to 1,\n> after all the vetting has been carried out and just before we talk to\n> the server.\n\nI'd rather not introduce new state. The point of the status flag was to\nencapsulate all of that information, and a new state variable just seems\nlike introducing extra complexity. If we are not in the NONE state, I\ndon't see why we would tell the helper about a ref at all.\n\n-Peff\n"},{"id":"130916","messageId":"20100106120546.GB7221@coredump.intra.peff.net","threadId":"21853","inReplyTo":"20100105180132.e573fff2.rctay89@gmail.com","subject":"Re: [PATCH v3 2/6] t5541-http-push.sh: add test for unmatched, non-fast-forwarded refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-01-06T12:05:46Z","receivedAt":"2010-01-06T12:05:46Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jan 05, 2010 at 06:01:32PM +0800, Tay Ray Chuan wrote:\n\n> > I don't understand what you're testing here. If it's not matched, then\n> > how is it a non-fast-forward? Isn't it simply unmatched?\n> \n> Let me rephrase this as:\n> \n>   Some refs can only be matched to a remote ref with an explicit\n>   refspec. When such a ref is a non-fast-forward of its remote ref,\n>   test that pushing them (with the explicit refspec specified) fails\n>   with a non-fast-foward-type error.\n\nThanks, that makes sense to me now.\n\n-Peff\n"},{"id":"130953","messageId":"20100107054120.84972788.rctay89@gmail.com","threadId":"21853","inReplyTo":"20100106120456.GA7221@coredump.intra.peff.net","subject":"Re: [PATCH v3 5/6] transport-helper.c::push_refs(): ignore helper-reported status if ref is not to be pushed","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-01-06T21:41:20Z","receivedAt":"2010-01-06T21:41:20Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Hi,\n\nOn Wed, Jan 6, 2010 at 8:04 PM, Jeff King <peff@peff.net> wrote:\n> On Tue, Jan 05, 2010 at 06:01:13PM +0800, Tay Ray Chuan wrote:\n> \n> > > It seems like this should be checking for REF_STATUS_NONE explicitly\n> > > instead of trying to enumerate the reasons we might not have tried to\n> > > push. Shouldn't helpers _only_ be pushing REF_STATUS_NONE refs?\n> > >\n> > > I think right now the two cases are equivalent, since non-ff and\n> > > uptodate are the only two states set before the helper is invoked. But\n> > > we have discussed in the past (and I still have a patch floating around\n> > > for) a REF_STATUS_REWIND which would treat strict rewinds differently\n> > > (silently ignoring them instead of making an error). Explicitly checking\n> > > REF_STATUS_NONE future-proofs against new states being added.\n> > \n> > I'm not really sure if this is true (ie. that if status is not non-ff\n> > or uptodate, then it is REF_STATUS_NONE), but we could step around this\n> \n> Well, consider it this way. If it's _not_ REF_STATUS_NONE, then what is\n> it, and what does it mean to be overwriting it?\n\nOk, I'll take your suggestion from your previous email and do this:\n\n@@ -429,8 +429,16 @@ static int push_refs(struct transport *transport,\n \t\t\tcontinue;\n \t\t}\n\n-\t\tref->status = status;\n-\t\tref->remote_status = msg;\n+\t\tif (ref->status == REF_STATUS_NONE) {\n+\t\t\tref->status = status;\n+\t\t\tref->remote_status = msg;\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * Earlier, the ref was marked not to be pushed, so ignore what\n+\t\t\t * the remote helper said about the ref.\n+\t\t\t */\n+\t\t\tcontinue;\n+\t\t}\n \t}\n \tstrbuf_release(&buf);\n \treturn 0;\n\nGoing by this principle (only refs with status of none will be pushed),\nI think I should also squash the below into patch 3 (refactor ref status\nlogic for pushing):\n\n@@ -336,11 +336,10 @@ static int push_refs(struct transport *transport,\n \t\t\tcontinue;\n \n \t\tswitch (ref->status) {\n-\t\tcase REF_STATUS_REJECT_NONFASTFORWARD:\n-\t\tcase REF_STATUS_UPTODATE:\n-\t\t\tcontinue;\n+\t\tcase REF_STATUS_NONE:\n+\t\t\t; /* carry on with pushing */\n \t\tdefault:\n-\t\t\t; /* do nothing */\n+\t\t\tcontinue;\n \t\t}\n \n \t\tif (force_all)\n\n> Maybe I am misunderstanding the problem the patch is addressing, but the\n> point of these REF_STATUS feels was to act as a small state machine.\n> Everything starts as NONE, and then:\n> \n>   - we compare locally against remote refs. We may transition:\n>       NONE -> UPTODATE\n>       NONE -> REJECT_NONFASTFORWARD\n>       NONE -> REJECT_NODELETE\n> \n>   - we send the push list\n>       NONE -> EXPECTING_REPORT (if the remote supports individual status)\n>       NONE -> OK (otherwise)\n>\n>   - we get back status responses\n>       EXPECTING_REPORT -> OK\n>       EXPECTING_REPORT -> REMOTE_REJECT\n> \n> I haven't looked closely at the new transport helper code, but I would\n> think it should stick more or less to those transitions. The exception\n> would be that some transports don't necessarily handle EXPECTING_REPORT\n> in the same way, and may transition directly from NONE to\n> OK/REMOTE_REJECT.\n\nminor nit: yes, this may differ from transport-to-transport, but\nEXPECTING_REPORT is not used at all in the top-level transport (the\nlevel above the helper).\n\nThere's also something I'd like to point out for accuracy: it's that\nthis sequence of transitions occur at two levels, separately: one at\nthe top-level transport/transport-helper, and another at the helper.\n\nSo, for certain non-ff refs (the type this patch series is looking at),\nthe sequence of state transitions stops and doesn't continue to step 2\nin the top-level transport (sending the push list); but separately, in\nthe helper, the ref goes through another sequence of state transitions.\n\nWhat this patch touches is the part in the top-level transport that\nsyncs the ref status between the helper and the top-level transport: do\nwe take and present to the user what the helper has done, or not?\n\nRegarding this point, I now think that we should ignore the\nhelper-reported status only if that status is none, and continue\nupdating the ref status in the top-level transport if the helper did\npush successfully/failed, even if we didn't tell it to push:\n\n@@ -429,7 +429,7 @@ static int push_refs(struct transport *transport,\n\n\t\tref->status = status;\n\t\tref->remote_status = msg;\n-\t\tif (ref->status == REF_STATUS_NONE) {\n+\t\tif (ref->status == REF_STATUS_NONE && status == REF_STATUS_NONE) {\n\t\t\tref->status = status;\n\t\t\tref->remote_status = msg;\n\t\t} else {\n\n> So offhand, I would say that your list should also probably include\n> REJECT_NODELETE. However, I think that status is just for old servers\n> which didn't support the delete-refs protocol extension. So presumably\n> that is none of the new helpers, as they all post-date the addition of\n> that feature by quite a few years.\n\nYou're right, AFAIK, for the smart http protocol; I don't think it\nsupports NODELETE.\n\n> > by introducing a property, say, ref->should_push, that is set to 1,\n> > after all the vetting has been carried out and just before we talk to\n> > the server.\n> \n> I'd rather not introduce new state. The point of the status flag was to\n> encapsulate all of that information, and a new state variable just seems\n> like introducing extra complexity. If we are not in the NONE state, I\n> don't see why we would tell the helper about a ref at all.\n\nNoted.\n\n-- \nCheers,\nRay Chuan\n"},{"id":"131055","messageId":"be6fef0d1001071704s3d3fd0a3wb9b46172a8e891d@mail.gmail.com","threadId":"21853","inReplyTo":"20100107054120.84972788.rctay89@gmail.com","subject":"Re: [PATCH v3 5/6] transport-helper.c::push_refs(): ignore helper-reported status if ref is not to be pushed","fromName":"Tay Ray Chuan","fromEmail":"rctay89@gmail.com","sentAt":"2010-01-08T01:04:45Z","receivedAt":"2010-01-08T01:04:45Z","isPatch":true,"sender":{"key":"rctay89@gmail.com","avatar":"https://avatars.githubusercontent.com/u/61553?v=4"},"body":"Hi,\n\nOn Thu, Jan 7, 2010 at 5:41 AM, Tay Ray Chuan <rctay89@gmail.com> wrote:\n> Regarding this point, I now think that we should ignore the\n> helper-reported status only if that status is none, and continue\n> updating the ref status in the top-level transport if the helper did\n> push successfully/failed, even if we didn't tell it to push:\n>\n> @@ -429,7 +429,7 @@ static int push_refs(struct transport *transport,\n>\n>                ref->status = status;\n>                ref->remote_status = msg;\n> -               if (ref->status == REF_STATUS_NONE) {\n> +               if (ref->status == REF_STATUS_NONE && status == REF_STATUS_NONE) {\n>                        ref->status = status;\n>                        ref->remote_status = msg;\n>                } else {\n\nsorry for this broken hunk. I'll be re-sending shortly to make things clearer.\n\n-- \nCheers,\nRay Chuan\n"}]}