Merge branch 'cr/push-force-tag-update'
Regression fix to stop "git push" complaining "target ref already exists", when it is not the real reason the command rejected the request (e.g. non-fast-forward). * cr/push-force-tag-update: push: fix "refs/tags/ hierarchy cannot be updated without --force"maint
commit
d82dd26964
1
cache.h
1
cache.h
|
@ -1011,7 +1011,6 @@ struct ref {
|
||||||
requires_force:1,
|
requires_force:1,
|
||||||
merge:1,
|
merge:1,
|
||||||
nonfastforward:1,
|
nonfastforward:1,
|
||||||
not_forwardable:1,
|
|
||||||
update:1,
|
update:1,
|
||||||
deletion:1;
|
deletion:1;
|
||||||
enum {
|
enum {
|
||||||
|
|
43
remote.c
43
remote.c
|
@ -1279,26 +1279,6 @@ int match_push_refs(struct ref *src, struct ref **dst,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int is_forwardable(struct ref* ref)
|
|
||||||
{
|
|
||||||
struct object *o;
|
|
||||||
|
|
||||||
if (!prefixcmp(ref->name, "refs/tags/"))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* old object must be a commit */
|
|
||||||
o = parse_object(ref->old_sha1);
|
|
||||||
if (!o || o->type != OBJ_COMMIT)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* new object must be commit-ish */
|
|
||||||
o = deref_tag(parse_object(ref->new_sha1), NULL, 0);
|
|
||||||
if (!o || o->type != OBJ_COMMIT)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
||||||
int force_update)
|
int force_update)
|
||||||
{
|
{
|
||||||
|
@ -1320,32 +1300,23 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The below logic determines whether an individual
|
* Decide whether an individual refspec A:B can be
|
||||||
* refspec A:B can be pushed. The push will succeed
|
* pushed. The push will succeed if any of the
|
||||||
* if any of the following are true:
|
* following are true:
|
||||||
*
|
*
|
||||||
* (1) the remote reference B does not exist
|
* (1) the remote reference B does not exist
|
||||||
*
|
*
|
||||||
* (2) the remote reference B is being removed (i.e.,
|
* (2) the remote reference B is being removed (i.e.,
|
||||||
* pushing :B where no source is specified)
|
* pushing :B where no source is specified)
|
||||||
*
|
*
|
||||||
* (3) the update meets all fast-forwarding criteria:
|
* (3) the destination is not under refs/tags/, and
|
||||||
*
|
* if the old and new value is a commit, the new
|
||||||
* (a) the destination is not under refs/tags/
|
* is a descendant of the old.
|
||||||
* (b) the old is a commit
|
|
||||||
* (c) the new is a descendant of the old
|
|
||||||
*
|
|
||||||
* NOTE: We must actually have the old object in
|
|
||||||
* order to overwrite it in the remote reference,
|
|
||||||
* and the new object must be commit-ish. These are
|
|
||||||
* implied by (b) and (c) respectively.
|
|
||||||
*
|
*
|
||||||
* (4) it is forced using the +A:B notation, or by
|
* (4) it is forced using the +A:B notation, or by
|
||||||
* passing the --force argument
|
* passing the --force argument
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ref->not_forwardable = !is_forwardable(ref);
|
|
||||||
|
|
||||||
ref->update =
|
ref->update =
|
||||||
!ref->deletion &&
|
!ref->deletion &&
|
||||||
!is_null_sha1(ref->old_sha1);
|
!is_null_sha1(ref->old_sha1);
|
||||||
|
@ -1355,7 +1326,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
||||||
!has_sha1_file(ref->old_sha1)
|
!has_sha1_file(ref->old_sha1)
|
||||||
|| !ref_newer(ref->new_sha1, ref->old_sha1);
|
|| !ref_newer(ref->new_sha1, ref->old_sha1);
|
||||||
|
|
||||||
if (ref->not_forwardable) {
|
if (!prefixcmp(ref->name, "refs/tags/")) {
|
||||||
ref->requires_force = 1;
|
ref->requires_force = 1;
|
||||||
if (!force_ref_update) {
|
if (!force_ref_update) {
|
||||||
ref->status = REF_STATUS_REJECT_ALREADY_EXISTS;
|
ref->status = REF_STATUS_REJECT_ALREADY_EXISTS;
|
||||||
|
|
|
@ -950,27 +950,6 @@ test_expect_success 'push requires --force to update lightweight tag' '
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'push requires --force to update annotated tag' '
|
|
||||||
mk_test heads/master &&
|
|
||||||
mk_child child1 &&
|
|
||||||
mk_child child2 &&
|
|
||||||
(
|
|
||||||
cd child1 &&
|
|
||||||
git tag -a -m "message 1" Tag &&
|
|
||||||
git push ../child2 Tag:refs/tmp/Tag &&
|
|
||||||
git push ../child2 Tag:refs/tmp/Tag &&
|
|
||||||
>file1 &&
|
|
||||||
git add file1 &&
|
|
||||||
git commit -m "file1" &&
|
|
||||||
git tag -f -a -m "message 2" Tag &&
|
|
||||||
test_must_fail git push ../child2 Tag:refs/tmp/Tag &&
|
|
||||||
git push --force ../child2 Tag:refs/tmp/Tag &&
|
|
||||||
git tag -f -a -m "message 3" Tag HEAD~ &&
|
|
||||||
test_must_fail git push ../child2 Tag:refs/tmp/Tag &&
|
|
||||||
git push --force ../child2 Tag:refs/tmp/Tag
|
|
||||||
)
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'push --porcelain' '
|
test_expect_success 'push --porcelain' '
|
||||||
mk_empty &&
|
mk_empty &&
|
||||||
echo >.git/foo "To testrepo" &&
|
echo >.git/foo "To testrepo" &&
|
||||||
|
|
Loading…
Reference in New Issue