* [PATCH v3 1/5] push: return reject reasons via a mask
2012-11-12 4:08 [PATCH v3 0/5] push: update remote tags only with force Chris Rorvick
@ 2012-11-12 4:08 ` Chris Rorvick
2012-11-12 4:08 ` [PATCH v3 2/5] push: add advice for rejected tag reference Chris Rorvick
` (5 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Chris Rorvick @ 2012-11-12 4:08 UTC (permalink / raw)
To: git
Cc: Chris Rorvick, Angelo Borsotti, Drew Northup, Michael Haggerty,
Philip Oakley, Johannes Sixt, Kacper Kornet, Jeff King,
Felipe Contreras
Pass all rejection reasons back from transport_push(). The logic is
simpler and more flexible with regard to providing useful feedback.
Signed-off-by: Chris Rorvick <chris@rorvick.com>
---
builtin/push.c | 13 ++++---------
builtin/send-pack.c | 4 ++--
transport.c | 17 ++++++++---------
transport.h | 9 +++++----
4 files changed, 19 insertions(+), 24 deletions(-)
diff --git a/builtin/push.c b/builtin/push.c
index db9ba30..eaeaf7e 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -244,7 +244,7 @@ static void advise_checkout_pull_push(void)
static int push_with_options(struct transport *transport, int flags)
{
int err;
- int nonfastforward;
+ unsigned int reject_mask;
transport_set_verbosity(transport, verbosity, progress);
@@ -257,7 +257,7 @@ static int push_with_options(struct transport *transport, int flags)
if (verbosity > 0)
fprintf(stderr, _("Pushing to %s\n"), transport->url);
err = transport_push(transport, refspec_nr, refspec, flags,
- &nonfastforward);
+ &reject_mask);
if (err != 0)
error(_("failed to push some refs to '%s'"), transport->url);
@@ -265,18 +265,13 @@ static int push_with_options(struct transport *transport, int flags)
if (!err)
return 0;
- switch (nonfastforward) {
- default:
- break;
- case NON_FF_HEAD:
+ if (reject_mask & NON_FF_HEAD) {
advise_pull_before_push();
- break;
- case NON_FF_OTHER:
+ } else if (reject_mask & NON_FF_OTHER) {
if (default_matching_used)
advise_use_upstream();
else
advise_checkout_pull_push();
- break;
}
return 1;
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index d342013..fda28bc 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -85,7 +85,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
int send_all = 0;
const char *receivepack = "git-receive-pack";
int flags;
- int nonfastforward = 0;
+ unsigned int reject_mask;
int progress = -1;
argv++;
@@ -223,7 +223,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
ret |= finish_connect(conn);
if (!helper_status)
- transport_print_push_status(dest, remote_refs, args.verbose, 0, &nonfastforward);
+ transport_print_push_status(dest, remote_refs, args.verbose, 0, &reject_mask);
if (!args.dry_run && remote) {
struct ref *ref;
diff --git a/transport.c b/transport.c
index 9932f40..ae9fda8 100644
--- a/transport.c
+++ b/transport.c
@@ -714,7 +714,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
}
void transport_print_push_status(const char *dest, struct ref *refs,
- int verbose, int porcelain, int *nonfastforward)
+ int verbose, int porcelain, unsigned int *reject_mask)
{
struct ref *ref;
int n = 0;
@@ -733,18 +733,17 @@ void transport_print_push_status(const char *dest, struct ref *refs,
if (ref->status == REF_STATUS_OK)
n += print_one_push_status(ref, dest, n, porcelain);
- *nonfastforward = 0;
+ *reject_mask = 0;
for (ref = refs; ref; ref = ref->next) {
if (ref->status != REF_STATUS_NONE &&
ref->status != REF_STATUS_UPTODATE &&
ref->status != REF_STATUS_OK)
n += print_one_push_status(ref, dest, n, porcelain);
- if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD &&
- *nonfastforward != NON_FF_HEAD) {
+ if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
if (!strcmp(head, ref->name))
- *nonfastforward = NON_FF_HEAD;
+ *reject_mask |= NON_FF_HEAD;
else
- *nonfastforward = NON_FF_OTHER;
+ *reject_mask |= NON_FF_OTHER;
}
}
}
@@ -1031,9 +1030,9 @@ static void die_with_unpushed_submodules(struct string_list *needs_pushing)
int transport_push(struct transport *transport,
int refspec_nr, const char **refspec, int flags,
- int *nonfastforward)
+ unsigned int *reject_mask)
{
- *nonfastforward = 0;
+ *reject_mask = 0;
transport_verify_remote_names(refspec_nr, refspec);
if (transport->push) {
@@ -1099,7 +1098,7 @@ int transport_push(struct transport *transport,
if (!quiet || err)
transport_print_push_status(transport->url, remote_refs,
verbose | porcelain, porcelain,
- nonfastforward);
+ reject_mask);
if (flags & TRANSPORT_PUSH_SET_UPSTREAM)
set_upstreams(transport, remote_refs, pretend);
diff --git a/transport.h b/transport.h
index 4a61c0c..1f9699c 100644
--- a/transport.h
+++ b/transport.h
@@ -140,11 +140,12 @@ int transport_set_option(struct transport *transport, const char *name,
void transport_set_verbosity(struct transport *transport, int verbosity,
int force_progress);
-#define NON_FF_HEAD 1
-#define NON_FF_OTHER 2
+#define NON_FF_HEAD 0x01
+#define NON_FF_OTHER 0x02
+
int transport_push(struct transport *connection,
int refspec_nr, const char **refspec, int flags,
- int * nonfastforward);
+ unsigned int * reject_mask);
const struct ref *transport_get_remote_refs(struct transport *transport);
@@ -170,7 +171,7 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v
int transport_refs_pushed(struct ref *ref);
void transport_print_push_status(const char *dest, struct ref *refs,
- int verbose, int porcelain, int *nonfastforward);
+ int verbose, int porcelain, unsigned int *reject_mask);
typedef void alternate_ref_fn(const struct ref *, void *);
extern void for_each_alternate_ref(alternate_ref_fn, void *);
--
1.8.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 2/5] push: add advice for rejected tag reference
2012-11-12 4:08 [PATCH v3 0/5] push: update remote tags only with force Chris Rorvick
2012-11-12 4:08 ` [PATCH v3 1/5] push: return reject reasons via a mask Chris Rorvick
@ 2012-11-12 4:08 ` Chris Rorvick
2012-11-12 4:08 ` [PATCH v3 3/5] push: flag updates Chris Rorvick
` (4 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Chris Rorvick @ 2012-11-12 4:08 UTC (permalink / raw)
To: git
Cc: Chris Rorvick, Angelo Borsotti, Drew Northup, Michael Haggerty,
Philip Oakley, Johannes Sixt, Kacper Kornet, Jeff King,
Felipe Contreras
Advising the user to fetch and merge only makes sense if the rejected
reference is a branch. If none of the rejections were for branches,
tell the user they need to force the update(s).
Signed-off-by: Chris Rorvick <chris@rorvick.com>
---
builtin/push.c | 16 ++++++++++++++--
cache.h | 1 +
remote.c | 7 +++++++
transport.c | 6 ++++--
transport.h | 5 +++--
5 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/builtin/push.c b/builtin/push.c
index eaeaf7e..77340c0 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -220,6 +220,11 @@ static const char message_advice_checkout_pull_push[] =
"(e.g. 'git pull') before pushing again.\n"
"See the 'Note about fast-forwards' in 'git push --help' for details.");
+static const char message_advice_ref_already_exists[] =
+ N_("Updates were rejected because a matching reference already exists in\n"
+ "the remote and the update is not a fast-forward. Use git push -f if\n"
+ "you really want to make this update.");
+
static void advise_pull_before_push(void)
{
if (!advice_push_non_ff_current || !advice_push_nonfastforward)
@@ -241,6 +246,11 @@ static void advise_checkout_pull_push(void)
advise(_(message_advice_checkout_pull_push));
}
+static void advise_ref_already_exists(void)
+{
+ advise(_(message_advice_ref_already_exists));
+}
+
static int push_with_options(struct transport *transport, int flags)
{
int err;
@@ -265,13 +275,15 @@ static int push_with_options(struct transport *transport, int flags)
if (!err)
return 0;
- if (reject_mask & NON_FF_HEAD) {
+ if (reject_mask & REJECT_NON_FF_HEAD) {
advise_pull_before_push();
- } else if (reject_mask & NON_FF_OTHER) {
+ } else if (reject_mask & REJECT_NON_FF_OTHER) {
if (default_matching_used)
advise_use_upstream();
else
advise_checkout_pull_push();
+ } else if (reject_mask & REJECT_ALREADY_EXISTS) {
+ advise_ref_already_exists();
}
return 1;
diff --git a/cache.h b/cache.h
index dbd8018..4e25840 100644
--- a/cache.h
+++ b/cache.h
@@ -1002,6 +1002,7 @@ struct ref {
unsigned int force:1,
merge:1,
nonfastforward:1,
+ forwardable:1,
deletion:1;
enum {
REF_STATUS_NONE = 0,
diff --git a/remote.c b/remote.c
index 04fd9ea..5ecd58d 100644
--- a/remote.c
+++ b/remote.c
@@ -1316,6 +1316,13 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
* always allowed.
*/
+ if (prefixcmp(ref->name, "refs/tags/")) {
+ struct object *old = parse_object(ref->old_sha1);
+ struct object *new = parse_object(ref->new_sha1);
+ ref->forwardable = (old && new &&
+ old->type == OBJ_COMMIT && new->type == OBJ_COMMIT);
+ }
+
ref->nonfastforward =
!ref->deletion &&
!is_null_sha1(ref->old_sha1) &&
diff --git a/transport.c b/transport.c
index ae9fda8..1657798 100644
--- a/transport.c
+++ b/transport.c
@@ -740,10 +740,12 @@ void transport_print_push_status(const char *dest, struct ref *refs,
ref->status != REF_STATUS_OK)
n += print_one_push_status(ref, dest, n, porcelain);
if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
+ if (!ref->forwardable)
+ *reject_mask |= REJECT_ALREADY_EXISTS;
if (!strcmp(head, ref->name))
- *reject_mask |= NON_FF_HEAD;
+ *reject_mask |= REJECT_NON_FF_HEAD;
else
- *reject_mask |= NON_FF_OTHER;
+ *reject_mask |= REJECT_NON_FF_OTHER;
}
}
}
diff --git a/transport.h b/transport.h
index 1f9699c..7e86352 100644
--- a/transport.h
+++ b/transport.h
@@ -140,8 +140,9 @@ int transport_set_option(struct transport *transport, const char *name,
void transport_set_verbosity(struct transport *transport, int verbosity,
int force_progress);
-#define NON_FF_HEAD 0x01
-#define NON_FF_OTHER 0x02
+#define REJECT_NON_FF_HEAD 0x01
+#define REJECT_NON_FF_OTHER 0x02
+#define REJECT_ALREADY_EXISTS 0x04
int transport_push(struct transport *connection,
int refspec_nr, const char **refspec, int flags,
--
1.8.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 3/5] push: flag updates
2012-11-12 4:08 [PATCH v3 0/5] push: update remote tags only with force Chris Rorvick
2012-11-12 4:08 ` [PATCH v3 1/5] push: return reject reasons via a mask Chris Rorvick
2012-11-12 4:08 ` [PATCH v3 2/5] push: add advice for rejected tag reference Chris Rorvick
@ 2012-11-12 4:08 ` Chris Rorvick
2012-11-12 4:08 ` [PATCH v3 4/5] push: flag updates that require force Chris Rorvick
` (3 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Chris Rorvick @ 2012-11-12 4:08 UTC (permalink / raw)
To: git
Cc: Chris Rorvick, Angelo Borsotti, Drew Northup, Michael Haggerty,
Philip Oakley, Johannes Sixt, Kacper Kornet, Jeff King,
Felipe Contreras
If the reference exists on the remote and the the update is not a
delete, then mark as an update. This is in preparation for handling
tags and branches differently when pushing.
Signed-off-by: Chris Rorvick <chris@rorvick.com>
---
cache.h | 1 +
remote.c | 18 +++++++++++-------
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/cache.h b/cache.h
index 4e25840..9712d1b 100644
--- a/cache.h
+++ b/cache.h
@@ -1003,6 +1003,7 @@ struct ref {
merge:1,
nonfastforward:1,
forwardable:1,
+ update:1,
deletion:1;
enum {
REF_STATUS_NONE = 0,
diff --git a/remote.c b/remote.c
index 5ecd58d..7fa9db6 100644
--- a/remote.c
+++ b/remote.c
@@ -1323,15 +1323,19 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
old->type == OBJ_COMMIT && new->type == OBJ_COMMIT);
}
- ref->nonfastforward =
+ ref->update =
!ref->deletion &&
- !is_null_sha1(ref->old_sha1) &&
- (!has_sha1_file(ref->old_sha1)
- || !ref_newer(ref->new_sha1, ref->old_sha1));
+ !is_null_sha1(ref->old_sha1);
- if (ref->nonfastforward && !ref->force && !force_update) {
- ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
- continue;
+ if (ref->update) {
+ ref->nonfastforward =
+ !has_sha1_file(ref->old_sha1)
+ || !ref_newer(ref->new_sha1, ref->old_sha1);
+
+ if (ref->nonfastforward && !ref->force && !force_update) {
+ ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
+ continue;
+ }
}
}
}
--
1.8.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 4/5] push: flag updates that require force
2012-11-12 4:08 [PATCH v3 0/5] push: update remote tags only with force Chris Rorvick
` (2 preceding siblings ...)
2012-11-12 4:08 ` [PATCH v3 3/5] push: flag updates Chris Rorvick
@ 2012-11-12 4:08 ` Chris Rorvick
2012-11-12 4:08 ` [PATCH v3 5/5] push: update remote tags only with force Chris Rorvick
` (2 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Chris Rorvick @ 2012-11-12 4:08 UTC (permalink / raw)
To: git
Cc: Chris Rorvick, Angelo Borsotti, Drew Northup, Michael Haggerty,
Philip Oakley, Johannes Sixt, Kacper Kornet, Jeff King,
Felipe Contreras
Add a flag for indicating an update to a reference requires force.
Currently the nonfastforward flag of a ref is used for this when
generating status the status message. A separate flag insulates the
status logic from the details of set_ref_status_for_push().
Signed-off-by: Chris Rorvick <chris@rorvick.com>
---
cache.h | 4 +++-
remote.c | 11 ++++++++---
transport.c | 2 +-
3 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/cache.h b/cache.h
index 9712d1b..e61dca3 100644
--- a/cache.h
+++ b/cache.h
@@ -999,7 +999,9 @@ struct ref {
unsigned char old_sha1[20];
unsigned char new_sha1[20];
char *symref;
- unsigned int force:1,
+ unsigned int
+ force:1,
+ requires_force:1,
merge:1,
nonfastforward:1,
forwardable:1,
diff --git a/remote.c b/remote.c
index 7fa9db6..82ba5ed 100644
--- a/remote.c
+++ b/remote.c
@@ -1285,6 +1285,8 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
struct ref *ref;
for (ref = remote_refs; ref; ref = ref->next) {
+ int force_ref_update = ref->force || force_update;
+
if (ref->peer_ref)
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
else if (!send_mirror)
@@ -1332,9 +1334,12 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
!has_sha1_file(ref->old_sha1)
|| !ref_newer(ref->new_sha1, ref->old_sha1);
- if (ref->nonfastforward && !ref->force && !force_update) {
- ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
- continue;
+ if (ref->nonfastforward) {
+ ref->requires_force = 1;
+ if (!force_ref_update) {
+ ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
+ continue;
+ }
}
}
}
diff --git a/transport.c b/transport.c
index 1657798..632f8b0 100644
--- a/transport.c
+++ b/transport.c
@@ -659,7 +659,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
const char *msg;
strcpy(quickref, status_abbrev(ref->old_sha1));
- if (ref->nonfastforward) {
+ if (ref->requires_force) {
strcat(quickref, "...");
type = '+';
msg = "forced update";
--
1.8.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 5/5] push: update remote tags only with force
2012-11-12 4:08 [PATCH v3 0/5] push: update remote tags only with force Chris Rorvick
` (3 preceding siblings ...)
2012-11-12 4:08 ` [PATCH v3 4/5] push: flag updates that require force Chris Rorvick
@ 2012-11-12 4:08 ` Chris Rorvick
2012-11-13 21:20 ` [PATCH v3 0/5] " Junio C Hamano
2012-11-13 23:58 ` Drew Northup
6 siblings, 0 replies; 17+ messages in thread
From: Chris Rorvick @ 2012-11-12 4:08 UTC (permalink / raw)
To: git
Cc: Chris Rorvick, Angelo Borsotti, Drew Northup, Michael Haggerty,
Philip Oakley, Johannes Sixt, Kacper Kornet, Jeff King,
Felipe Contreras
References are allowed to update from one commit-ish to another if the
former is a ancestor of the latter. This behavior is oriented to
branches which are expected to move with commits. Tag references are
expected to be static in a repository, though, thus an update to a
tag (lightweight and annotated) should be rejected unless the update is
forced.
To enable this functionality, the following checks have been added to
set_ref_status_for_push() for updating refs (i.e, not new or deletion)
to restrict fast-forwarding in pushes:
1) The old and new references must be commits. If this fails,
it is not a valid update for a branch.
2) The reference name cannot start with "refs/tags/". This
catches lightweight tags which (usually) point to commits
and therefore would not be caught by (1).
If either of these checks fails, then it is flagged (by default) with a
status indicating the update is being rejected due to the reference
already existing in the remote. This can be overridden by passing
--force to git push.
Signed-off-by: Chris Rorvick <chris@rorvick.com>
---
Documentation/git-push.txt | 10 +++++-----
builtin/push.c | 3 +--
builtin/send-pack.c | 5 +++++
cache.h | 1 +
remote.c | 8 +++++++-
send-pack.c | 1 +
t/t5516-fetch-push.sh | 30 +++++++++++++++++++++++++++++-
transport-helper.c | 6 ++++++
transport.c | 8 ++++++--
9 files changed, 61 insertions(+), 11 deletions(-)
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index fe46c42..479e25f 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -51,11 +51,11 @@ be named. If `:`<dst> is omitted, the same ref as <src> will be
updated.
+
The object referenced by <src> is used to update the <dst> reference
-on the remote side, but by default this is only allowed if the
-update can fast-forward <dst>. By having the optional leading `+`,
-you can tell git to update the <dst> ref even when the update is not a
-fast-forward. This does *not* attempt to merge <src> into <dst>. See
-EXAMPLES below for details.
+on the remote side. By default this is only allowed if the update is
+a branch, and then only if it can fast-forward <dst>. By having the
+optional leading `+`, you can tell git to update the <dst> ref even when
+the update is not a branch or it is not a fast-forward. This does *not*
+attempt to merge <src> into <dst>. See EXAMPLES below for details.
+
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
+
diff --git a/builtin/push.c b/builtin/push.c
index 77340c0..d097348 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -222,8 +222,7 @@ static const char message_advice_checkout_pull_push[] =
static const char message_advice_ref_already_exists[] =
N_("Updates were rejected because a matching reference already exists in\n"
- "the remote and the update is not a fast-forward. Use git push -f if\n"
- "you really want to make this update.");
+ "the remote. Use git push -f if you really want to make this update.");
static void advise_pull_before_push(void)
{
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index fda28bc..1eabf42 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -44,6 +44,11 @@ static void print_helper_status(struct ref *ref)
msg = "non-fast forward";
break;
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
+ res = "error";
+ msg = "already exists";
+ break;
+
case REF_STATUS_REJECT_NODELETE:
case REF_STATUS_REMOTE_REJECT:
res = "error";
diff --git a/cache.h b/cache.h
index e61dca3..127e504 100644
--- a/cache.h
+++ b/cache.h
@@ -1011,6 +1011,7 @@ struct ref {
REF_STATUS_NONE = 0,
REF_STATUS_OK,
REF_STATUS_REJECT_NONFASTFORWARD,
+ REF_STATUS_REJECT_ALREADY_EXISTS,
REF_STATUS_REJECT_NODELETE,
REF_STATUS_UPTODATE,
REF_STATUS_REMOTE_REJECT,
diff --git a/remote.c b/remote.c
index 82ba5ed..e5f8a56 100644
--- a/remote.c
+++ b/remote.c
@@ -1334,7 +1334,13 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
!has_sha1_file(ref->old_sha1)
|| !ref_newer(ref->new_sha1, ref->old_sha1);
- if (ref->nonfastforward) {
+ if (!ref->forwardable) {
+ ref->requires_force = 1;
+ if (!force_ref_update) {
+ ref->status = REF_STATUS_REJECT_ALREADY_EXISTS;
+ continue;
+ }
+ } else if (ref->nonfastforward) {
ref->requires_force = 1;
if (!force_ref_update) {
ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
diff --git a/send-pack.c b/send-pack.c
index f50dfd9..1c375f0 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -229,6 +229,7 @@ int send_pack(struct send_pack_args *args,
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
case REF_STATUS_UPTODATE:
continue;
default:
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index b5417cc..afb9b1b 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -368,7 +368,7 @@ test_expect_success 'push with colon-less refspec (2)' '
git branch -D frotz
fi &&
git tag -f frotz &&
- git push testrepo frotz &&
+ git push -f testrepo frotz &&
check_push_result $the_commit tags/frotz &&
check_push_result $the_first_commit heads/frotz
@@ -929,6 +929,34 @@ test_expect_success 'push into aliased refs (inconsistent)' '
)
'
+test_expect_success 'push tag requires --force to update remote tag' '
+ mk_test heads/master &&
+ mk_child child1 &&
+ mk_child child2 &&
+ (
+ cd child1 &&
+ git tag lw_tag &&
+ git tag -a -m "message 1" ann_tag &&
+ git push ../child2 lw_tag &&
+ git push ../child2 ann_tag &&
+ >file1 &&
+ git add file1 &&
+ git commit -m "file1" &&
+ git tag -f lw_tag &&
+ git tag -f -a -m "message 2" ann_tag &&
+ test_must_fail git push ../child2 lw_tag &&
+ test_must_fail git push ../child2 ann_tag &&
+ git push --force ../child2 lw_tag &&
+ git push --force ../child2 ann_tag &&
+ git tag -f lw_tag HEAD~ &&
+ git tag -f -a -m "message 3" ann_tag &&
+ test_must_fail git push ../child2 lw_tag &&
+ test_must_fail git push ../child2 ann_tag &&
+ git push --force ../child2 lw_tag &&
+ git push --force ../child2 ann_tag
+ )
+'
+
test_expect_success 'push --porcelain' '
mk_empty &&
echo >.git/foo "To testrepo" &&
diff --git a/transport-helper.c b/transport-helper.c
index 4713b69..965b778 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -661,6 +661,11 @@ static void push_update_ref_status(struct strbuf *buf,
free(msg);
msg = NULL;
}
+ else if (!strcmp(msg, "already exists")) {
+ status = REF_STATUS_REJECT_ALREADY_EXISTS;
+ free(msg);
+ msg = NULL;
+ }
}
if (*ref)
@@ -720,6 +725,7 @@ static int push_refs_with_push(struct transport *transport,
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
case REF_STATUS_UPTODATE:
continue;
default:
diff --git a/transport.c b/transport.c
index 632f8b0..a380ad7 100644
--- a/transport.c
+++ b/transport.c
@@ -695,6 +695,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
"non-fast-forward", porcelain);
break;
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
+ print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+ "already exists", porcelain);
+ break;
case REF_STATUS_REMOTE_REJECT:
print_ref_status('!', "[remote rejected]", ref,
ref->deletion ? NULL : ref->peer_ref,
@@ -740,12 +744,12 @@ void transport_print_push_status(const char *dest, struct ref *refs,
ref->status != REF_STATUS_OK)
n += print_one_push_status(ref, dest, n, porcelain);
if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
- if (!ref->forwardable)
- *reject_mask |= REJECT_ALREADY_EXISTS;
if (!strcmp(head, ref->name))
*reject_mask |= REJECT_NON_FF_HEAD;
else
*reject_mask |= REJECT_NON_FF_OTHER;
+ } else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {
+ *reject_mask |= REJECT_ALREADY_EXISTS;
}
}
}
--
1.8.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v3 0/5] push: update remote tags only with force
2012-11-12 4:08 [PATCH v3 0/5] push: update remote tags only with force Chris Rorvick
` (4 preceding siblings ...)
2012-11-12 4:08 ` [PATCH v3 5/5] push: update remote tags only with force Chris Rorvick
@ 2012-11-13 21:20 ` Junio C Hamano
[not found] ` <CAEUsAPYvrR6WsVWCvwoEWA21gzL6Sib0sTyx-c_2tH=8ni69yQ@mail.gmail.com>
2012-11-13 23:58 ` Drew Northup
6 siblings, 1 reply; 17+ messages in thread
From: Junio C Hamano @ 2012-11-13 21:20 UTC (permalink / raw)
To: Chris Rorvick
Cc: git, Angelo Borsotti, Drew Northup, Michael Haggerty,
Philip Oakley, Johannes Sixt, Kacper Kornet, Jeff King,
Felipe Contreras
Chris Rorvick <chris@rorvick.com> writes:
> Minor changes since from v2 set. Reposting primarily because I mucked
> up the Cc: list (again) and hoping to route feedback to the appropriate
> audience.
>
> This patch set can be divided into two sets:
>
> 1. Provide useful advice for rejected tag references.
>
> push: return reject reasons via a mask
> push: add advice for rejected tag reference
>
> Recommending a merge to resolve a rejected tag update seems
> nonsensical since the tag does not come along for the ride. These
> patches change the advice for rejected tags to suggest using
> "push -f".
Below, I take that you mean by "tag reference" everything under
refs/tags/ (not limited to "annotated tag objects", but also
lightweight tags).
Given that the second point below is to strongly discourage updating
of existing any tag, it might be even better to advise *not* to push
tags in the first place, instead of destructive "push -f", no?
> 2. Require force when updating tag references, even on a fast-forward.
>
> push: flag updates
> push: flag updates that require force
> push: update remote tags only with force
>
> An email thread initiated by Angelo Borsotti did not come to a
> consensus on how push should behave with regard to tag references.
I think the original motivation of allowing fast-forward updates to
tags was for people who wanted to have "today's recommended version"
tag that can float from day to day. I tend to think that was a
misguided notion and it is better implemented with a tip of a
branch (iow, I personally am OK with the change to forbid tag
updates altogether, without --force).
> I think a key point is that you currently cannot be sure your push
> will not clobber a tag (lightweight or not) in the remote.
"Do not update, only add new" may be a good feature, but at the same
time I have this suspicion that its usefulness may not necessarily
be limited to refs/tags/* hierarchy.
I dunno.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 0/5] push: update remote tags only with force
2012-11-12 4:08 [PATCH v3 0/5] push: update remote tags only with force Chris Rorvick
` (5 preceding siblings ...)
2012-11-13 21:20 ` [PATCH v3 0/5] " Junio C Hamano
@ 2012-11-13 23:58 ` Drew Northup
6 siblings, 0 replies; 17+ messages in thread
From: Drew Northup @ 2012-11-13 23:58 UTC (permalink / raw)
To: Chris Rorvick
Cc: git, Angelo Borsotti, Michael Haggerty, Philip Oakley,
Johannes Sixt, Kacper Kornet, Jeff King, Felipe Contreras,
Junio C Hamano
On Sun, Nov 11, 2012 at 11:08 PM, Chris Rorvick <chris@rorvick.com> wrote:
> Minor changes since from v2 set.
.....
> An email thread initiated by Angelo Borsotti did not come to a
> consensus on how push should behave with regard to tag references.
Minor Nit: Without the link to gmane it is an exercise left to the
reviewer to find that you're talking about this thread:
http://thread.gmane.org/gmane.comp.version-control.git/208354
Cheers.
--
-Drew Northup
--------------------------------------------------------------
"As opposed to vegetable or mineral error?"
-John Pescatore, SANS NewsBites Vol. 12 Num. 59
^ permalink raw reply [flat|nested] 17+ messages in thread