{"thread":{"id":"32086","subject":"[PATCH v3 0/5] push: update remote tags only with force","startedAt":"2012-11-12T04:08:03Z","lastAt":"2012-11-15T16:50:56Z","messageCount":17,"participants":["Chris Rorvick","Junio C Hamano","Drew Northup","Kacper Kornet","Angelo Borsotti"],"isPatch":true,"patchVersion":3,"patchTotal":5},"messages":[{"id":"202938","messageId":"1352693288-7396-1-git-send-email-chris@rorvick.com","threadId":"32086","inReplyTo":null,"subject":"[PATCH v3 0/5] push: update remote tags only with force","fromName":"Chris Rorvick","fromEmail":"chris@rorvick.com","sentAt":"2012-11-12T04:08:03Z","receivedAt":"2012-11-12T04:08:03Z","isPatch":true,"sender":{"key":"chris@rorvick.com","avatar":"https://avatars.githubusercontent.com/u/824726?v=4"},"body":"Minor changes since from v2 set.  Reposting primarily because I mucked\nup the Cc: list (again) and hoping to route feedback to the appropriate\naudience.\n\nThis patch set can be divided into two sets:\n\n  1. Provide useful advice for rejected tag references.\n\n     push: return reject reasons via a mask\n     push: add advice for rejected tag reference\n\n     Recommending a merge to resolve a rejected tag update seems\n     nonsensical since the tag does not come along for the ride.  These\n     patches change the advice for rejected tags to suggest using\n     \"push -f\".\n\n  2. Require force when updating tag references, even on a fast-forward.\n\n     push: flag updates\n     push: flag updates that require force\n     push: update remote tags only with force\n\n     An email thread initiated by Angelo Borsotti did not come to a\n     consensus on how push should behave with regard to tag references.\n     I think a key point is that you currently cannot be sure your push\n     will not clobber a tag (lightweight or not) in the remote.  Also, I\n     wonder what workflow would rely on this fast-forward feature of\n     pushed tag references that would not be better served a branch?\n\nThis patch set contains some minor updates from the previous set:\n\n  * remote.c: remove redundant check of ref->update\n  * transport.c: remove extraneous tab in indent\n  * builtin/send-pack.c: fix call to transport_print_push_status() (per\n      feedback from Peff)\n\nAlso, rebased against the latest master in git://github.com/peff/git.git\nto pickup changes in nd/builtin-to-libgit.\n\nChris Rorvick (5):\n  push: return reject reasons via a mask\n  push: add advice for rejected tag reference\n  push: flag updates\n  push: flag updates that require force\n  push: update remote tags only with force\n\n Documentation/git-push.txt | 10 +++++-----\n builtin/push.c             | 24 +++++++++++++++---------\n builtin/send-pack.c        |  9 +++++++--\n cache.h                    |  7 ++++++-\n remote.c                   | 38 ++++++++++++++++++++++++++++++--------\n send-pack.c                |  1 +\n t/t5516-fetch-push.sh      | 30 +++++++++++++++++++++++++++++-\n transport-helper.c         |  6 ++++++\n transport.c                | 25 +++++++++++++++----------\n transport.h                | 10 ++++++----\n 10 files changed, 120 insertions(+), 40 deletions(-)\n\n-- \n1.8.0\n"},{"id":"202939","messageId":"1352693288-7396-2-git-send-email-chris@rorvick.com","threadId":"32086","inReplyTo":"1352693288-7396-1-git-send-email-chris@rorvick.com","subject":"[PATCH v3 1/5] push: return reject reasons via a mask","fromName":"Chris Rorvick","fromEmail":"chris@rorvick.com","sentAt":"2012-11-12T04:08:04Z","receivedAt":"2012-11-12T04:08:04Z","isPatch":true,"sender":{"key":"chris@rorvick.com","avatar":"https://avatars.githubusercontent.com/u/824726?v=4"},"body":"Pass all rejection reasons back from transport_push().  The logic is\nsimpler and more flexible with regard to providing useful feedback.\n\nSigned-off-by: Chris Rorvick <chris@rorvick.com>\n---\n builtin/push.c      | 13 ++++---------\n builtin/send-pack.c |  4 ++--\n transport.c         | 17 ++++++++---------\n transport.h         |  9 +++++----\n 4 files changed, 19 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex db9ba30..eaeaf7e 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -244,7 +244,7 @@ static void advise_checkout_pull_push(void)\n static int push_with_options(struct transport *transport, int flags)\n {\n \tint err;\n-\tint nonfastforward;\n+\tunsigned int reject_mask;\n \n \ttransport_set_verbosity(transport, verbosity, progress);\n \n@@ -257,7 +257,7 @@ static int push_with_options(struct transport *transport, int flags)\n \tif (verbosity > 0)\n \t\tfprintf(stderr, _(\"Pushing to %s\\n\"), transport->url);\n \terr = transport_push(transport, refspec_nr, refspec, flags,\n-\t\t\t     &nonfastforward);\n+\t\t\t     &reject_mask);\n \tif (err != 0)\n \t\terror(_(\"failed to push some refs to '%s'\"), transport->url);\n \n@@ -265,18 +265,13 @@ static int push_with_options(struct transport *transport, int flags)\n \tif (!err)\n \t\treturn 0;\n \n-\tswitch (nonfastforward) {\n-\tdefault:\n-\t\tbreak;\n-\tcase NON_FF_HEAD:\n+\tif (reject_mask & NON_FF_HEAD) {\n \t\tadvise_pull_before_push();\n-\t\tbreak;\n-\tcase NON_FF_OTHER:\n+\t} else if (reject_mask & NON_FF_OTHER) {\n \t\tif (default_matching_used)\n \t\t\tadvise_use_upstream();\n \t\telse\n \t\t\tadvise_checkout_pull_push();\n-\t\tbreak;\n \t}\n \n \treturn 1;\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex d342013..fda28bc 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -85,7 +85,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \tint send_all = 0;\n \tconst char *receivepack = \"git-receive-pack\";\n \tint flags;\n-\tint nonfastforward = 0;\n+\tunsigned int reject_mask;\n \tint progress = -1;\n \n \targv++;\n@@ -223,7 +223,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \tret |= finish_connect(conn);\n \n \tif (!helper_status)\n-\t\ttransport_print_push_status(dest, remote_refs, args.verbose, 0, &nonfastforward);\n+\t\ttransport_print_push_status(dest, remote_refs, args.verbose, 0, &reject_mask);\n \n \tif (!args.dry_run && remote) {\n \t\tstruct ref *ref;\ndiff --git a/transport.c b/transport.c\nindex 9932f40..ae9fda8 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -714,7 +714,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n }\n \n void transport_print_push_status(const char *dest, struct ref *refs,\n-\t\t\t\t  int verbose, int porcelain, int *nonfastforward)\n+\t\t\t\t  int verbose, int porcelain, unsigned int *reject_mask)\n {\n \tstruct ref *ref;\n \tint n = 0;\n@@ -733,18 +733,17 @@ void transport_print_push_status(const char *dest, struct ref *refs,\n \t\tif (ref->status == REF_STATUS_OK)\n \t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n \n-\t*nonfastforward = 0;\n+\t*reject_mask = 0;\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, porcelain);\n-\t\tif (ref->status == REF_STATUS_REJECT_NONFASTFORWARD &&\n-\t\t    *nonfastforward != NON_FF_HEAD) {\n+\t\tif (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {\n \t\t\tif (!strcmp(head, ref->name))\n-\t\t\t\t*nonfastforward = NON_FF_HEAD;\n+\t\t\t\t*reject_mask |= NON_FF_HEAD;\n \t\t\telse\n-\t\t\t\t*nonfastforward = NON_FF_OTHER;\n+\t\t\t\t*reject_mask |= NON_FF_OTHER;\n \t\t}\n \t}\n }\n@@ -1031,9 +1030,9 @@ static void die_with_unpushed_submodules(struct string_list *needs_pushing)\n \n int transport_push(struct transport *transport,\n \t\t   int refspec_nr, const char **refspec, int flags,\n-\t\t   int *nonfastforward)\n+\t\t   unsigned int *reject_mask)\n {\n-\t*nonfastforward = 0;\n+\t*reject_mask = 0;\n \ttransport_verify_remote_names(refspec_nr, refspec);\n \n \tif (transport->push) {\n@@ -1099,7 +1098,7 @@ int transport_push(struct transport *transport,\n \t\tif (!quiet || err)\n \t\t\ttransport_print_push_status(transport->url, remote_refs,\n \t\t\t\t\tverbose | porcelain, porcelain,\n-\t\t\t\t\tnonfastforward);\n+\t\t\t\t\treject_mask);\n \n \t\tif (flags & TRANSPORT_PUSH_SET_UPSTREAM)\n \t\t\tset_upstreams(transport, remote_refs, pretend);\ndiff --git a/transport.h b/transport.h\nindex 4a61c0c..1f9699c 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -140,11 +140,12 @@ int transport_set_option(struct transport *transport, const char *name,\n void transport_set_verbosity(struct transport *transport, int verbosity,\n \tint force_progress);\n \n-#define NON_FF_HEAD 1\n-#define NON_FF_OTHER 2\n+#define NON_FF_HEAD     0x01\n+#define NON_FF_OTHER    0x02\n+\n int transport_push(struct transport *connection,\n \t\t   int refspec_nr, const char **refspec, int flags,\n-\t\t   int * nonfastforward);\n+\t\t   unsigned int * reject_mask);\n \n const struct ref *transport_get_remote_refs(struct transport *transport);\n \n@@ -170,7 +171,7 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v\n int transport_refs_pushed(struct ref *ref);\n \n void transport_print_push_status(const char *dest, struct ref *refs,\n-\t\t  int verbose, int porcelain, int *nonfastforward);\n+\t\t  int verbose, int porcelain, unsigned int *reject_mask);\n \n typedef void alternate_ref_fn(const struct ref *, void *);\n extern void for_each_alternate_ref(alternate_ref_fn, void *);\n-- \n1.8.0\n"},{"id":"202940","messageId":"1352693288-7396-3-git-send-email-chris@rorvick.com","threadId":"32086","inReplyTo":"1352693288-7396-1-git-send-email-chris@rorvick.com","subject":"[PATCH v3 2/5] push: add advice for rejected tag reference","fromName":"Chris Rorvick","fromEmail":"chris@rorvick.com","sentAt":"2012-11-12T04:08:05Z","receivedAt":"2012-11-12T04:08:05Z","isPatch":true,"sender":{"key":"chris@rorvick.com","avatar":"https://avatars.githubusercontent.com/u/824726?v=4"},"body":"Advising the user to fetch and merge only makes sense if the rejected\nreference is a branch.  If none of the rejections were for branches,\ntell the user they need to force the update(s).\n\nSigned-off-by: Chris Rorvick <chris@rorvick.com>\n---\n builtin/push.c | 16 ++++++++++++++--\n cache.h        |  1 +\n remote.c       |  7 +++++++\n transport.c    |  6 ++++--\n transport.h    |  5 +++--\n 5 files changed, 29 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex eaeaf7e..77340c0 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -220,6 +220,11 @@ static const char message_advice_checkout_pull_push[] =\n \t   \"(e.g. 'git pull') before pushing again.\\n\"\n \t   \"See the 'Note about fast-forwards' in 'git push --help' for details.\");\n \n+static const char message_advice_ref_already_exists[] =\n+\tN_(\"Updates were rejected because a matching reference already exists in\\n\"\n+\t   \"the remote and the update is not a fast-forward.  Use git push -f if\\n\"\n+\t   \"you really want to make this update.\");\n+\n static void advise_pull_before_push(void)\n {\n \tif (!advice_push_non_ff_current || !advice_push_nonfastforward)\n@@ -241,6 +246,11 @@ static void advise_checkout_pull_push(void)\n \tadvise(_(message_advice_checkout_pull_push));\n }\n \n+static void advise_ref_already_exists(void)\n+{\n+\tadvise(_(message_advice_ref_already_exists));\n+}\n+\n static int push_with_options(struct transport *transport, int flags)\n {\n \tint err;\n@@ -265,13 +275,15 @@ static int push_with_options(struct transport *transport, int flags)\n \tif (!err)\n \t\treturn 0;\n \n-\tif (reject_mask & NON_FF_HEAD) {\n+\tif (reject_mask & REJECT_NON_FF_HEAD) {\n \t\tadvise_pull_before_push();\n-\t} else if (reject_mask & NON_FF_OTHER) {\n+\t} else if (reject_mask & REJECT_NON_FF_OTHER) {\n \t\tif (default_matching_used)\n \t\t\tadvise_use_upstream();\n \t\telse\n \t\t\tadvise_checkout_pull_push();\n+\t} else if (reject_mask & REJECT_ALREADY_EXISTS) {\n+\t\tadvise_ref_already_exists();\n \t}\n \n \treturn 1;\ndiff --git a/cache.h b/cache.h\nindex dbd8018..4e25840 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1002,6 +1002,7 @@ struct ref {\n \tunsigned int force:1,\n \t\tmerge:1,\n \t\tnonfastforward:1,\n+\t\tforwardable:1,\n \t\tdeletion:1;\n \tenum {\n \t\tREF_STATUS_NONE = 0,\ndiff --git a/remote.c b/remote.c\nindex 04fd9ea..5ecd58d 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1316,6 +1316,13 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n \t\t *     always allowed.\n \t\t */\n \n+\t\tif (prefixcmp(ref->name, \"refs/tags/\")) {\n+\t\t\tstruct object *old = parse_object(ref->old_sha1);\n+\t\t\tstruct object *new = parse_object(ref->new_sha1);\n+\t\t\tref->forwardable = (old && new &&\n+\t\t\t  old->type == OBJ_COMMIT && new->type == OBJ_COMMIT);\n+\t\t}\n+\n \t\tref->nonfastforward =\n \t\t\t!ref->deletion &&\n \t\t\t!is_null_sha1(ref->old_sha1) &&\ndiff --git a/transport.c b/transport.c\nindex ae9fda8..1657798 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -740,10 +740,12 @@ void transport_print_push_status(const char *dest, struct ref *refs,\n \t\t    ref->status != REF_STATUS_OK)\n \t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n \t\tif (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {\n+\t\t\tif (!ref->forwardable)\n+\t\t\t\t*reject_mask |= REJECT_ALREADY_EXISTS;\n \t\t\tif (!strcmp(head, ref->name))\n-\t\t\t\t*reject_mask |= NON_FF_HEAD;\n+\t\t\t\t*reject_mask |= REJECT_NON_FF_HEAD;\n \t\t\telse\n-\t\t\t\t*reject_mask |= NON_FF_OTHER;\n+\t\t\t\t*reject_mask |= REJECT_NON_FF_OTHER;\n \t\t}\n \t}\n }\ndiff --git a/transport.h b/transport.h\nindex 1f9699c..7e86352 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -140,8 +140,9 @@ int transport_set_option(struct transport *transport, const char *name,\n void transport_set_verbosity(struct transport *transport, int verbosity,\n \tint force_progress);\n \n-#define NON_FF_HEAD     0x01\n-#define NON_FF_OTHER    0x02\n+#define REJECT_NON_FF_HEAD     0x01\n+#define REJECT_NON_FF_OTHER    0x02\n+#define REJECT_ALREADY_EXISTS  0x04\n \n int transport_push(struct transport *connection,\n \t\t   int refspec_nr, const char **refspec, int flags,\n-- \n1.8.0\n"},{"id":"202943","messageId":"1352693288-7396-4-git-send-email-chris@rorvick.com","threadId":"32086","inReplyTo":"1352693288-7396-1-git-send-email-chris@rorvick.com","subject":"[PATCH v3 3/5] push: flag updates","fromName":"Chris Rorvick","fromEmail":"chris@rorvick.com","sentAt":"2012-11-12T04:08:06Z","receivedAt":"2012-11-12T04:08:06Z","isPatch":true,"sender":{"key":"chris@rorvick.com","avatar":"https://avatars.githubusercontent.com/u/824726?v=4"},"body":"If the reference exists on the remote and the the update is not a\ndelete, then mark as an update.  This is in preparation for handling\ntags and branches differently when pushing.\n\nSigned-off-by: Chris Rorvick <chris@rorvick.com>\n---\n cache.h  |  1 +\n remote.c | 18 +++++++++++-------\n 2 files changed, 12 insertions(+), 7 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 4e25840..9712d1b 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1003,6 +1003,7 @@ struct ref {\n \t\tmerge:1,\n \t\tnonfastforward:1,\n \t\tforwardable:1,\n+\t\tupdate:1,\n \t\tdeletion:1;\n \tenum {\n \t\tREF_STATUS_NONE = 0,\ndiff --git a/remote.c b/remote.c\nindex 5ecd58d..7fa9db6 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1323,15 +1323,19 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n \t\t\t  old->type == OBJ_COMMIT && new->type == OBJ_COMMIT);\n \t\t}\n \n-\t\tref->nonfastforward =\n+\t\tref->update =\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+\t\t\t!is_null_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\tif (ref->update) {\n+\t\t\tref->nonfastforward =\n+\t\t\t\t!has_sha1_file(ref->old_sha1)\n+\t\t\t\t  || !ref_newer(ref->new_sha1, ref->old_sha1);\n+\n+\t\t\tif (ref->nonfastforward && !ref->force && !force_update) {\n+\t\t\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t}\n \t}\n }\n-- \n1.8.0\n"},{"id":"202941","messageId":"1352693288-7396-5-git-send-email-chris@rorvick.com","threadId":"32086","inReplyTo":"1352693288-7396-1-git-send-email-chris@rorvick.com","subject":"[PATCH v3 4/5] push: flag updates that require force","fromName":"Chris Rorvick","fromEmail":"chris@rorvick.com","sentAt":"2012-11-12T04:08:07Z","receivedAt":"2012-11-12T04:08:07Z","isPatch":true,"sender":{"key":"chris@rorvick.com","avatar":"https://avatars.githubusercontent.com/u/824726?v=4"},"body":"Add a flag for indicating an update to a reference requires force.\nCurrently the nonfastforward flag of a ref is used for this when\ngenerating status the status message.  A separate flag insulates the\nstatus logic from the details of set_ref_status_for_push().\n\nSigned-off-by: Chris Rorvick <chris@rorvick.com>\n---\n cache.h     |  4 +++-\n remote.c    | 11 ++++++++---\n transport.c |  2 +-\n 3 files changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 9712d1b..e61dca3 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -999,7 +999,9 @@ struct ref {\n \tunsigned char old_sha1[20];\n \tunsigned char new_sha1[20];\n \tchar *symref;\n-\tunsigned int force:1,\n+\tunsigned int\n+\t\tforce:1,\n+\t\trequires_force:1,\n \t\tmerge:1,\n \t\tnonfastforward:1,\n \t\tforwardable:1,\ndiff --git a/remote.c b/remote.c\nindex 7fa9db6..82ba5ed 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1285,6 +1285,8 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n \tstruct ref *ref;\n \n \tfor (ref = remote_refs; ref; ref = ref->next) {\n+\t\tint force_ref_update = ref->force || force_update;\n+\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@@ -1332,9 +1334,12 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n \t\t\t\t!has_sha1_file(ref->old_sha1)\n \t\t\t\t  || !ref_newer(ref->new_sha1, ref->old_sha1);\n \n-\t\t\tif (ref->nonfastforward && !ref->force && !force_update) {\n-\t\t\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\n-\t\t\t\tcontinue;\n+\t\t\tif (ref->nonfastforward) {\n+\t\t\t\tref->requires_force = 1;\n+\t\t\t\tif (!force_ref_update) {\n+\t\t\t\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n \t\t\t}\n \t\t}\n \t}\ndiff --git a/transport.c b/transport.c\nindex 1657798..632f8b0 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -659,7 +659,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)\n \t\tconst char *msg;\n \n \t\tstrcpy(quickref, status_abbrev(ref->old_sha1));\n-\t\tif (ref->nonfastforward) {\n+\t\tif (ref->requires_force) {\n \t\t\tstrcat(quickref, \"...\");\n \t\t\ttype = '+';\n \t\t\tmsg = \"forced update\";\n-- \n1.8.0\n"},{"id":"202942","messageId":"1352693288-7396-6-git-send-email-chris@rorvick.com","threadId":"32086","inReplyTo":"1352693288-7396-1-git-send-email-chris@rorvick.com","subject":"[PATCH v3 5/5] push: update remote tags only with force","fromName":"Chris Rorvick","fromEmail":"chris@rorvick.com","sentAt":"2012-11-12T04:08:08Z","receivedAt":"2012-11-12T04:08:08Z","isPatch":true,"sender":{"key":"chris@rorvick.com","avatar":"https://avatars.githubusercontent.com/u/824726?v=4"},"body":"References are allowed to update from one commit-ish to another if the\nformer is a ancestor of the latter.  This behavior is oriented to\nbranches which are expected to move with commits.  Tag references are\nexpected to be static in a repository, though, thus an update to a\ntag (lightweight and annotated) should be rejected unless the update is\nforced.\n\nTo enable this functionality, the following checks have been added to\nset_ref_status_for_push() for updating refs (i.e, not new or deletion)\nto restrict fast-forwarding in pushes:\n\n  1) The old and new references must be commits.  If this fails,\n     it is not a valid update for a branch.\n\n  2) The reference name cannot start with \"refs/tags/\".  This\n     catches lightweight tags which (usually) point to commits\n     and therefore would not be caught by (1).\n\nIf either of these checks fails, then it is flagged (by default) with a\nstatus indicating the update is being rejected due to the reference\nalready existing in the remote.  This can be overridden by passing\n--force to git push.\n\nSigned-off-by: Chris Rorvick <chris@rorvick.com>\n---\n Documentation/git-push.txt | 10 +++++-----\n builtin/push.c             |  3 +--\n builtin/send-pack.c        |  5 +++++\n cache.h                    |  1 +\n remote.c                   |  8 +++++++-\n send-pack.c                |  1 +\n t/t5516-fetch-push.sh      | 30 +++++++++++++++++++++++++++++-\n transport-helper.c         |  6 ++++++\n transport.c                |  8 ++++++--\n 9 files changed, 61 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex fe46c42..479e25f 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -51,11 +51,11 @@ be named. If `:`<dst> is omitted, the same ref as <src> will be\n updated.\n +\n The object referenced by <src> is used to update the <dst> reference\n-on the remote side, but by default this is only allowed if the\n-update can fast-forward <dst>.  By having the optional leading `+`,\n-you can tell git to update the <dst> ref even when the update is not a\n-fast-forward.  This does *not* attempt to merge <src> into <dst>.  See\n-EXAMPLES below for details.\n+on the remote side.  By default this is only allowed if the update is\n+a branch, and then only if it can fast-forward <dst>.  By having the\n+optional leading `+`, you can tell git to update the <dst> ref even when\n+the update is not a branch or it is not a fast-forward.  This does *not*\n+attempt to merge <src> into <dst>.  See EXAMPLES below for details.\n +\n `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.\n +\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 77340c0..d097348 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -222,8 +222,7 @@ static const char message_advice_checkout_pull_push[] =\n \n static const char message_advice_ref_already_exists[] =\n \tN_(\"Updates were rejected because a matching reference already exists in\\n\"\n-\t   \"the remote and the update is not a fast-forward.  Use git push -f if\\n\"\n-\t   \"you really want to make this update.\");\n+\t   \"the remote.  Use git push -f if you really want to make this update.\");\n \n static void advise_pull_before_push(void)\n {\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex fda28bc..1eabf42 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -44,6 +44,11 @@ static void print_helper_status(struct ref *ref)\n \t\t\tmsg = \"non-fast forward\";\n \t\t\tbreak;\n \n+\t\tcase REF_STATUS_REJECT_ALREADY_EXISTS:\n+\t\t\tres = \"error\";\n+\t\t\tmsg = \"already exists\";\n+\t\t\tbreak;\n+\n \t\tcase REF_STATUS_REJECT_NODELETE:\n \t\tcase REF_STATUS_REMOTE_REJECT:\n \t\t\tres = \"error\";\ndiff --git a/cache.h b/cache.h\nindex e61dca3..127e504 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1011,6 +1011,7 @@ struct ref {\n \t\tREF_STATUS_NONE = 0,\n \t\tREF_STATUS_OK,\n \t\tREF_STATUS_REJECT_NONFASTFORWARD,\n+\t\tREF_STATUS_REJECT_ALREADY_EXISTS,\n \t\tREF_STATUS_REJECT_NODELETE,\n \t\tREF_STATUS_UPTODATE,\n \t\tREF_STATUS_REMOTE_REJECT,\ndiff --git a/remote.c b/remote.c\nindex 82ba5ed..e5f8a56 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1334,7 +1334,13 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n \t\t\t\t!has_sha1_file(ref->old_sha1)\n \t\t\t\t  || !ref_newer(ref->new_sha1, ref->old_sha1);\n \n-\t\t\tif (ref->nonfastforward) {\n+\t\t\tif (!ref->forwardable) {\n+\t\t\t\tref->requires_force = 1;\n+\t\t\t\tif (!force_ref_update) {\n+\t\t\t\t\tref->status = REF_STATUS_REJECT_ALREADY_EXISTS;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t} else if (ref->nonfastforward) {\n \t\t\t\tref->requires_force = 1;\n \t\t\t\tif (!force_ref_update) {\n \t\t\t\t\tref->status = REF_STATUS_REJECT_NONFASTFORWARD;\ndiff --git a/send-pack.c b/send-pack.c\nindex f50dfd9..1c375f0 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -229,6 +229,7 @@ int send_pack(struct send_pack_args *args,\n \t\t/* Check for statuses set by set_ref_status_for_push() */\n \t\tswitch (ref->status) {\n \t\tcase REF_STATUS_REJECT_NONFASTFORWARD:\n+\t\tcase REF_STATUS_REJECT_ALREADY_EXISTS:\n \t\tcase REF_STATUS_UPTODATE:\n \t\t\tcontinue;\n \t\tdefault:\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex b5417cc..afb9b1b 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -368,7 +368,7 @@ test_expect_success 'push with colon-less refspec (2)' '\n \t\tgit branch -D frotz\n \tfi &&\n \tgit tag -f frotz &&\n-\tgit push testrepo frotz &&\n+\tgit push -f testrepo frotz &&\n \tcheck_push_result $the_commit tags/frotz &&\n \tcheck_push_result $the_first_commit heads/frotz\n \n@@ -929,6 +929,34 @@ test_expect_success 'push into aliased refs (inconsistent)' '\n \t)\n '\n \n+test_expect_success 'push tag requires --force to update remote tag' '\n+\tmk_test heads/master &&\n+\tmk_child child1 &&\n+\tmk_child child2 &&\n+\t(\n+\t\tcd child1 &&\n+\t\tgit tag lw_tag &&\n+\t\tgit tag -a -m \"message 1\" ann_tag &&\n+\t\tgit push ../child2 lw_tag &&\n+\t\tgit push ../child2 ann_tag &&\n+\t\t>file1 &&\n+\t\tgit add file1 &&\n+\t\tgit commit -m \"file1\" &&\n+\t\tgit tag -f lw_tag &&\n+\t\tgit tag -f -a -m \"message 2\" ann_tag &&\n+\t\ttest_must_fail git push ../child2 lw_tag &&\n+\t\ttest_must_fail git push ../child2 ann_tag &&\n+\t\tgit push --force ../child2 lw_tag &&\n+\t\tgit push --force ../child2 ann_tag &&\n+\t\tgit tag -f lw_tag HEAD~ &&\n+\t\tgit tag -f -a -m \"message 3\" ann_tag &&\n+\t\ttest_must_fail git push ../child2 lw_tag &&\n+\t\ttest_must_fail git push ../child2 ann_tag &&\n+\t\tgit push --force ../child2 lw_tag &&\n+\t\tgit push --force ../child2 ann_tag\n+\t)\n+'\n+\n test_expect_success 'push --porcelain' '\n \tmk_empty &&\n \techo >.git/foo  \"To testrepo\" &&\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 4713b69..965b778 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -661,6 +661,11 @@ static void push_update_ref_status(struct strbuf *buf,\n \t\t\tfree(msg);\n \t\t\tmsg = NULL;\n \t\t}\n+\t\telse if (!strcmp(msg, \"already exists\")) {\n+\t\t\tstatus = REF_STATUS_REJECT_ALREADY_EXISTS;\n+\t\t\tfree(msg);\n+\t\t\tmsg = NULL;\n+\t\t}\n \t}\n \n \tif (*ref)\n@@ -720,6 +725,7 @@ static int push_refs_with_push(struct transport *transport,\n \t\t/* Check for statuses set by set_ref_status_for_push() */\n \t\tswitch (ref->status) {\n \t\tcase REF_STATUS_REJECT_NONFASTFORWARD:\n+\t\tcase REF_STATUS_REJECT_ALREADY_EXISTS:\n \t\tcase REF_STATUS_UPTODATE:\n \t\t\tcontinue;\n \t\tdefault:\ndiff --git a/transport.c b/transport.c\nindex 632f8b0..a380ad7 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -695,6 +695,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i\n \t\tprint_ref_status('!', \"[rejected]\", ref, ref->peer_ref,\n \t\t\t\t\t\t \"non-fast-forward\", porcelain);\n \t\tbreak;\n+\tcase REF_STATUS_REJECT_ALREADY_EXISTS:\n+\t\tprint_ref_status('!', \"[rejected]\", ref, ref->peer_ref,\n+\t\t\t\t\t\t \"already exists\", porcelain);\n+\t\tbreak;\n \tcase REF_STATUS_REMOTE_REJECT:\n \t\tprint_ref_status('!', \"[remote rejected]\", ref,\n \t\t\t\t\t\t ref->deletion ? NULL : ref->peer_ref,\n@@ -740,12 +744,12 @@ void transport_print_push_status(const char *dest, struct ref *refs,\n \t\t    ref->status != REF_STATUS_OK)\n \t\t\tn += print_one_push_status(ref, dest, n, porcelain);\n \t\tif (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {\n-\t\t\tif (!ref->forwardable)\n-\t\t\t\t*reject_mask |= REJECT_ALREADY_EXISTS;\n \t\t\tif (!strcmp(head, ref->name))\n \t\t\t\t*reject_mask |= REJECT_NON_FF_HEAD;\n \t\t\telse\n \t\t\t\t*reject_mask |= REJECT_NON_FF_OTHER;\n+\t\t} else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {\n+\t\t\t*reject_mask |= REJECT_ALREADY_EXISTS;\n \t\t}\n \t}\n }\n-- \n1.8.0\n"},{"id":"203149","messageId":"7v4nktdwtp.fsf@alter.siamese.dyndns.org","threadId":"32086","inReplyTo":"1352693288-7396-1-git-send-email-chris@rorvick.com","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-13T21:20:18Z","receivedAt":"2012-11-13T21:20:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Chris Rorvick <chris@rorvick.com> writes:\n\n> Minor changes since from v2 set.  Reposting primarily because I mucked\n> up the Cc: list (again) and hoping to route feedback to the appropriate\n> audience.\n>\n> This patch set can be divided into two sets:\n>\n>   1. Provide useful advice for rejected tag references.\n>\n>      push: return reject reasons via a mask\n>      push: add advice for rejected tag reference\n>\n>      Recommending a merge to resolve a rejected tag update seems\n>      nonsensical since the tag does not come along for the ride.  These\n>      patches change the advice for rejected tags to suggest using\n>      \"push -f\".\n\nBelow, I take that you mean by \"tag reference\" everything under\nrefs/tags/ (not limited to \"annotated tag objects\", but also\nlightweight tags).\n\nGiven that the second point below is to strongly discourage updating\nof existing any tag, it might be even better to advise *not* to push\ntags in the first place, instead of destructive \"push -f\", no?\n\n>   2. Require force when updating tag references, even on a fast-forward.\n>\n>      push: flag updates\n>      push: flag updates that require force\n>      push: update remote tags only with force\n>\n>      An email thread initiated by Angelo Borsotti did not come to a\n>      consensus on how push should behave with regard to tag references.\n\nI think the original motivation of allowing fast-forward updates to\ntags was for people who wanted to have \"today's recommended version\"\ntag that can float from day to day. I tend to think that was a\nmisguided notion and it is better implemented with a tip of a\nbranch (iow, I personally am OK with the change to forbid tag\nupdates altogether, without --force).\n\n>      I think a key point is that you currently cannot be sure your push\n>      will not clobber a tag (lightweight or not) in the remote.\n\n\"Do not update, only add new\" may be a good feature, but at the same\ntime I have this suspicion that its usefulness may not necessarily\nbe limited to refs/tags/* hierarchy.\n\nI dunno.\n"},{"id":"203162","messageId":"CAM9Z-nmy+4yvTXLoaO5mkyMC0sv3Vv8x6s2O6WgVdN+ev-W2Aw@mail.gmail.com","threadId":"32086","inReplyTo":"1352693288-7396-1-git-send-email-chris@rorvick.com","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Drew Northup","fromEmail":"n1xim.email@gmail.com","sentAt":"2012-11-13T23:58:28Z","receivedAt":"2012-11-13T23:58:28Z","isPatch":true,"sender":{"key":"n1xim.email@gmail.com","avatar":null},"body":"On Sun, Nov 11, 2012 at 11:08 PM, Chris Rorvick <chris@rorvick.com> wrote:\n> Minor changes since from v2 set.\n.....\n\n>      An email thread initiated by Angelo Borsotti did not come to a\n>      consensus on how push should behave with regard to tag references.\n\nMinor Nit: Without the link to gmane it is an exercise left to the\nreviewer to find that you're talking about this thread:\nhttp://thread.gmane.org/gmane.comp.version-control.git/208354\n\nCheers.\n\n-- \n-Drew Northup\n--------------------------------------------------------------\n\"As opposed to vegetable or mineral error?\"\n-John Pescatore, SANS NewsBites Vol. 12 Num. 59\n"},{"id":"203171","messageId":"CAEUsAPZtF-L5J_g1L5d44BKveoAnJ81PatX94fFS4FM=iW33KA@mail.gmail.com","threadId":"32086","inReplyTo":"CAEUsAPYvrR6WsVWCvwoEWA21gzL6Sib0sTyx-c_2tH=8ni69yQ@mail.gmail.com","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Chris Rorvick","fromEmail":"chris@rorvick.com","sentAt":"2012-11-14T06:29:14Z","receivedAt":"2012-11-14T06:29:14Z","isPatch":true,"sender":{"key":"chris@rorvick.com","avatar":"https://avatars.githubusercontent.com/u/824726?v=4"},"body":"resending to list ...\n\nOn Tue, Nov 13, 2012 at 3:20 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Chris Rorvick <chris@rorvick.com> writes:\n>\n>> Minor changes since from v2 set.  Reposting primarily because I mucked\n>> up the Cc: list (again) and hoping to route feedback to the appropriate\n>> audience.\n>>\n>> This patch set can be divided into two sets:\n>>\n>>   1. Provide useful advice for rejected tag references.\n>>\n>>      push: return reject reasons via a mask\n>>      push: add advice for rejected tag reference\n>>\n>>      Recommending a merge to resolve a rejected tag update seems\n>>      nonsensical since the tag does not come along for the ride.  These\n>>      patches change the advice for rejected tags to suggest using\n>>      \"push -f\".\n>\n> Below, I take that you mean by \"tag reference\" everything under\n> refs/tags/ (not limited to \"annotated tag objects\", but also\n> lightweight tags).\n\nYes.\n\n> Given that the second point below is to strongly discourage updating\n> of existing any tag, it might be even better to advise *not* to push\n> tags in the first place, instead of destructive \"push -f\", no?\n\nThat does seem like a better idea.  Read the full manual page to\nfigure out how to force the update if that's what you want to do--the\nadvice should not suggest something exceptional.\n\n>>   2. Require force when updating tag references, even on a fast-forward.\n>>\n>>      push: flag updates\n>>      push: flag updates that require force\n>>      push: update remote tags only with force\n>>\n>>      An email thread initiated by Angelo Borsotti did not come to a\n>>      consensus on how push should behave with regard to tag references.\n>\n> I think the original motivation of allowing fast-forward updates to\n> tags was for people who wanted to have \"today's recommended version\"\n> tag that can float from day to day. I tend to think that was a\n> misguided notion and it is better implemented with a tip of a\n> branch (iow, I personally am OK with the change to forbid tag\n> updates altogether, without --force).\n>\n>>      I think a key point is that you currently cannot be sure your push\n>>      will not clobber a tag (lightweight or not) in the remote.\n>\n> \"Do not update, only add new\" may be a good feature, but at the same\n> time I have this suspicion that its usefulness may not necessarily\n> be limited to refs/tags/* hierarchy.\n>\n> I dunno.\n\nAre you suggesting allowing forwards for just refs/heads/*?  I\ninitially went this route based on some feedback in the original\nthread, but being that specific broke a couple tests in t5516 (i.e.,\npushing to refs/remotes/origin/master and another into refs/tmp/*.)\nMy initial thought was that I'd broken something and I need to modify\nthe patch, but now I think I should just modify those tests.  Branches\nare restricted to refs/heads/* (if I understand correctly), so\nallowing fast-forwards when pushing should be limited to this\nhierarchy, too.\n\nThanks,\n\nChris\n"},{"id":"203175","messageId":"20121114081921.GA20876@camk.edu.pl","threadId":"32086","inReplyTo":"CAEUsAPZtF-L5J_g1L5d44BKveoAnJ81PatX94fFS4FM=iW33KA@mail.gmail.com","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Kacper Kornet","fromEmail":"draenog@pld-linux.org","sentAt":"2012-11-14T08:19:21Z","receivedAt":"2012-11-14T08:19:21Z","isPatch":true,"sender":{"key":"draenog@pld-linux.org","avatar":"https://avatars.githubusercontent.com/u/608762?v=4"},"body":"On Wed, Nov 14, 2012 at 12:29:14AM -0600, Chris Rorvick wrote:\n\n> >>   2. Require force when updating tag references, even on a fast-forward.\n\n> >>      push: flag updates\n> >>      push: flag updates that require force\n> >>      push: update remote tags only with force\n\n> >>      An email thread initiated by Angelo Borsotti did not come to a\n> >>      consensus on how push should behave with regard to tag references.\n\n> > I think the original motivation of allowing fast-forward updates to\n> > tags was for people who wanted to have \"today's recommended version\"\n> > tag that can float from day to day. I tend to think that was a\n> > misguided notion and it is better implemented with a tip of a\n> > branch (iow, I personally am OK with the change to forbid tag\n> > updates altogether, without --force).\n\n> >>      I think a key point is that you currently cannot be sure your push\n> >>      will not clobber a tag (lightweight or not) in the remote.\n\n> > \"Do not update, only add new\" may be a good feature, but at the same\n> > time I have this suspicion that its usefulness may not necessarily\n> > be limited to refs/tags/* hierarchy.\n\n> > I dunno.\n\n> Are you suggesting allowing forwards for just refs/heads/*?  I\n> initially went this route based on some feedback in the original\n> thread, but being that specific broke a couple tests in t5516 (i.e.,\n> pushing to refs/remotes/origin/master and another into refs/tmp/*.)\n> My initial thought was that I'd broken something and I need to modify\n> the patch, but now I think I should just modify those tests.  Branches\n> are restricted to refs/heads/* (if I understand correctly), so\n> allowing fast-forwards when pushing should be limited to this\n> hierarchy, too.\n\nWhat about notes? I think they should be treated in the same way as\nbranches. My impression is that tags are exceptional in this respect.\n\n-- \n  Kacper\n"},{"id":"203186","messageId":"7v390ccoak.fsf@alter.siamese.dyndns.org","threadId":"32086","inReplyTo":"CAEUsAPZtF-L5J_g1L5d44BKveoAnJ81PatX94fFS4FM=iW33KA@mail.gmail.com","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-14T13:22:11Z","receivedAt":"2012-11-14T13:22:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Chris Rorvick <chris@rorvick.com> writes:\n\n>> \"Do not update, only add new\" may be a good feature, but at the same\n>> time I have this suspicion that its usefulness may not necessarily\n>> be limited to refs/tags/* hierarchy.\n>>\n>> I dunno.\n>\n> Are you suggesting allowing forwards for just refs/heads/*?\n\nNo, it is a nonsense to unconditionally forbid fast-forwards to refs\noutside refs/heads/ hierarchy.\n\nI was imagining a more general feature to allow the *user* to ask\nGit not to fast-forward some refs (not limited to refs/tags/) during\na push.\n\nIf such a general feature were in place, you can think of your patch\nas automatically making the user to ask Git not to fast-forward refs\nin refs/tags/, which would be a mere special case of it.\n\nAnd I was wondering if such a general feature makes sense.\n"},{"id":"203193","messageId":"CAB9Jk9DAwaLw2bTqj5x_zxRcFqn7s=nmGi=Jc_SD38vFoszBZg@mail.gmail.com","threadId":"32086","inReplyTo":"7v390ccoak.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Angelo Borsotti","fromEmail":"angelo.borsotti@gmail.com","sentAt":"2012-11-14T14:58:54Z","receivedAt":"2012-11-14T14:58:54Z","isPatch":true,"sender":{"key":"angelo.borsotti@gmail.com","avatar":null},"body":"Hi Junio,\n\nactually, I proposed to add a key in config files, e.g.\npushTagsNoChange to be set in the remote repo do disallow changes to\ntags, similar to pushNonFastForward that disallows non-fastforward\nchanges to branches. I still have the impression that this is simple\nand clear, and allows the owner of the remote repository to enforce\nthe policy s/he wants on her/his repository.\n\n-Angelo\n\nOn 14 November 2012 14:22, Junio C Hamano <gitster@pobox.com> wrote:\n> Chris Rorvick <chris@rorvick.com> writes:\n>\n>>> \"Do not update, only add new\" may be a good feature, but at the same\n>>> time I have this suspicion that its usefulness may not necessarily\n>>> be limited to refs/tags/* hierarchy.\n>>>\n>>> I dunno.\n>>\n>> Are you suggesting allowing forwards for just refs/heads/*?\n>\n> No, it is a nonsense to unconditionally forbid fast-forwards to refs\n> outside refs/heads/ hierarchy.\n>\n> I was imagining a more general feature to allow the *user* to ask\n> Git not to fast-forward some refs (not limited to refs/tags/) during\n> a push.\n>\n> If such a general feature were in place, you can think of your patch\n> as automatically making the user to ask Git not to fast-forward refs\n> in refs/tags/, which would be a mere special case of it.\n>\n> And I was wondering if such a general feature makes sense.\n>\n>\n>\n>\n"},{"id":"203202","messageId":"7vmwykay4n.fsf@alter.siamese.dyndns.org","threadId":"32086","inReplyTo":"CAB9Jk9DAwaLw2bTqj5x_zxRcFqn7s=nmGi=Jc_SD38vFoszBZg@mail.gmail.com","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-14T17:32:40Z","receivedAt":"2012-11-14T17:32:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Angelo Borsotti <angelo.borsotti@gmail.com> writes:\n\n> actually, I proposed to add a key in config files, e.g.\n> pushTagsNoChange to be set in the remote repo do disallow changes to\n> tags, similar to pushNonFastForward that disallows non-fastforward\n> changes to branches. I still have the impression that this is simple\n> and clear, and allows the owner of the remote repository to enforce\n> the policy s/he wants on her/his repository.\n\nThat is an independent issue of deciding to accept or reject\nreceiving a push from outside, no?  You can implement any such\npolicy in the pre-receive hook on the receiving end with a simple\nand clear manner, instead of adding specific logic to enforce a\nsingle hardcoded policy to the code that is flipped on with a\nconfiguration variable.\n\nIn any case, I thought this series was about users who run \"push\"\nvoluntarily stopping themselves from pushing updates to tags that\nmay happen to fast-forward, so if we were to go with the\nconfiguration route, the suggestion would be more like\n\n    [push]\n\tupdateNeedsForce = refs/tags/:refs/frotz/\n\nor perhaps\n\n    [remote \"origin\"]\n\tupdateNeedsForce = refs/tags/:refs/frotz/\n\nif we want to configure it per-remote, to specify that you would\nneed to say \"--force\" to update the refs in the listed hierarchies.\n\nThen your patch series could become just the matter of declaring\nthat the value of push.updateNeedsForce, when unspecified, defaults\nto \"refs/tags/\".\n"},{"id":"203229","messageId":"CAB9Jk9CoCAnWtWGPXBq_rNKrdUBgxEcdu3ySzaG-XDnkJn=BvA@mail.gmail.com","threadId":"32086","inReplyTo":"7vmwykay4n.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Angelo Borsotti","fromEmail":"angelo.borsotti@gmail.com","sentAt":"2012-11-14T23:43:45Z","receivedAt":"2012-11-14T23:43:45Z","isPatch":true,"sender":{"key":"angelo.borsotti@gmail.com","avatar":null},"body":"Hi Junio,\n\n> That is an independent issue of deciding to accept or reject\n> receiving a push from outside, no?\n\nYes, it is. Actually I thought some means to let the owner do decide\nwhat to accept were already present (the pushNonFastForward config\nkey), and going along this avenue I thought it could be appropriate to\nextent this a bit.\n\n-Angelo\n\nOn 14 November 2012 18:32, Junio C Hamano <gitster@pobox.com> wrote:\n> Angelo Borsotti <angelo.borsotti@gmail.com> writes:\n>\n>> actually, I proposed to add a key in config files, e.g.\n>> pushTagsNoChange to be set in the remote repo do disallow changes to\n>> tags, similar to pushNonFastForward that disallows non-fastforward\n>> changes to branches. I still have the impression that this is simple\n>> and clear, and allows the owner of the remote repository to enforce\n>> the policy s/he wants on her/his repository.\n>\n> That is an independent issue of deciding to accept or reject\n> receiving a push from outside, no?  You can implement any such\n> policy in the pre-receive hook on the receiving end with a simple\n> and clear manner, instead of adding specific logic to enforce a\n> single hardcoded policy to the code that is flipped on with a\n> configuration variable.\n>\n> In any case, I thought this series was about users who run \"push\"\n> voluntarily stopping themselves from pushing updates to tags that\n> may happen to fast-forward, so if we were to go with the\n> configuration route, the suggestion would be more like\n>\n>     [push]\n>         updateNeedsForce = refs/tags/:refs/frotz/\n>\n> or perhaps\n>\n>     [remote \"origin\"]\n>         updateNeedsForce = refs/tags/:refs/frotz/\n>\n> if we want to configure it per-remote, to specify that you would\n> need to say \"--force\" to update the refs in the listed hierarchies.\n>\n> Then your patch series could become just the matter of declaring\n> that the value of push.updateNeedsForce, when unspecified, defaults\n> to \"refs/tags/\".\n>\n"},{"id":"203232","messageId":"7v8va3afrt.fsf@alter.siamese.dyndns.org","threadId":"32086","inReplyTo":"7vmwykay4n.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-15T00:09:10Z","receivedAt":"2012-11-15T00:09:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\nAddendum.\n\n> In any case, I thought this series was about users who run \"push\"\n> voluntarily stopping themselves from pushing updates to tags that\n> may happen to fast-forward, so if we were to go with the\n> configuration route, the suggestion would be more like\n>\n>     [push]\n> \tupdateNeedsForce = refs/tags/:refs/frotz/\n>\n> or perhaps\n>\n>     [remote \"origin\"]\n> \tupdateNeedsForce = refs/tags/:refs/frotz/\n>\n> if we want to configure it per-remote, to specify that you would\n> need to say \"--force\" to update the refs in the listed hierarchies.\n>\n> Then your patch series could become just the matter of declaring\n> that the value of push.updateNeedsForce, when unspecified, defaults\n> to \"refs/tags/\".\n\nThe above is not a \"you should do it this way\" suggestion, by the\nway.\n\nI was just explaining what I meant by \"it may be a good feature, but\nmay not necessarily be limited to refs/tags\" in my earlier message\nin a different way \"... and a possible design that lifts the\nlimitation may go like this\".\n\nI am *not* convinced that the \"refs/tags/ is the only special\nhierarchy whose contents should not move\" is a bad limitation we\nshould avoid, but if it indeed is a bad limitation, the above is one\npossible way to think about avoiding it.\n\nThanks.\n"},{"id":"203270","messageId":"CAB9Jk9DK9AWBe_cf6=-v0pyD9xdhSoYPsKmRnUOwjuAm=hewfA@mail.gmail.com","threadId":"32086","inReplyTo":"7v8va3afrt.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Angelo Borsotti","fromEmail":"angelo.borsotti@gmail.com","sentAt":"2012-11-15T07:48:52Z","receivedAt":"2012-11-15T07:48:52Z","isPatch":true,"sender":{"key":"angelo.borsotti@gmail.com","avatar":null},"body":"Hi Junio,\n\n> I am *not* convinced that the \"refs/tags/ is the only special\n> hierarchy whose contents should not move\" is a bad limitation we\n> should avoid, but if it indeed is a bad limitation, the above is one\n> possible way to think about avoiding it.\n\nWhat other hierarchy besides branches and tags is there? Do you have\nin mind some other that should not move?\n\n-Angelo\n\nOn 15 November 2012 01:09, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n> Addendum.\n>\n>> In any case, I thought this series was about users who run \"push\"\n>> voluntarily stopping themselves from pushing updates to tags that\n>> may happen to fast-forward, so if we were to go with the\n>> configuration route, the suggestion would be more like\n>>\n>>     [push]\n>>       updateNeedsForce = refs/tags/:refs/frotz/\n>>\n>> or perhaps\n>>\n>>     [remote \"origin\"]\n>>       updateNeedsForce = refs/tags/:refs/frotz/\n>>\n>> if we want to configure it per-remote, to specify that you would\n>> need to say \"--force\" to update the refs in the listed hierarchies.\n>>\n>> Then your patch series could become just the matter of declaring\n>> that the value of push.updateNeedsForce, when unspecified, defaults\n>> to \"refs/tags/\".\n>\n> The above is not a \"you should do it this way\" suggestion, by the\n> way.\n>\n> I was just explaining what I meant by \"it may be a good feature, but\n> may not necessarily be limited to refs/tags\" in my earlier message\n> in a different way \"... and a possible design that lifts the\n> limitation may go like this\".\n>\n> I am *not* convinced that the \"refs/tags/ is the only special\n> hierarchy whose contents should not move\" is a bad limitation we\n> should avoid, but if it indeed is a bad limitation, the above is one\n> possible way to think about avoiding it.\n>\n> Thanks.\n"},{"id":"203298","messageId":"7vzk2i95e7.fsf@alter.siamese.dyndns.org","threadId":"32086","inReplyTo":"CAB9Jk9DK9AWBe_cf6=-v0pyD9xdhSoYPsKmRnUOwjuAm=hewfA@mail.gmail.com","subject":"Re: [PATCH v3 0/5] push: update remote tags only with force","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-11-15T16:50:56Z","receivedAt":"2012-11-15T16:50:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Angelo Borsotti <angelo.borsotti@gmail.com> writes:\n\n>> I am *not* convinced that the \"refs/tags/ is the only special\n>> hierarchy whose contents should not move\" is a bad limitation we\n>> should avoid, but if it indeed is a bad limitation, the above is one\n>> possible way to think about avoiding it.\n>\n> What other hierarchy besides branches and tags is there? Do you have\n> in mind some other that should not move?\n\nPeople use their own hierarchies for various purposes that are not\npre-defined by git-core, e.g. refs/changes/, refs/pull/, etc.\nDepending on the semantics the projects want out of these\nhierarchies, some of them may well be considered \"create-only\".\n"}]}