push: --force-if-includes should allow fast-forward
In set_ref_status_for_push, we apply --force-if-includes's reflog
reachability checks before fast-forward rules. As a result, valid
fast-forward pushes may be rejected when a force push is unneeded; like
when the reflog is expired:
git clone repo.git repo
git commit --allow-empty -m 1
git reflog expire --expire=all --all
git push --force-with-lease --force-if-includes origin main
! [rejected] main -> main (remote ref updated since checkout)
Rejecting fast-forwards is a mismatch with the --force-if-includes
documentation "Force an update only if the tip of the remote-tracking
ref has been integrated locally."
Instead, defer reflog rejection for --force-if-includes until after
determining if a force push is needed.
Remember the reflog rejection reason as needs_force_reject_reason. If
the fast-forward rules reject the push for a ref, show the reflog
rejection reason to preserve existing behavior. But if fast-forward
rules allow a push (a fast-forward, deletion, or new ref), then a force
push is unneeded, the reflog rejection reason is discarded, and the push
proceeds.
Signed-off-by: Tyler Cipriani <tyler@tylercipriani.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
seen
parent
1af0f4f9ea
commit
795ce211df
16
remote.c
16
remote.c
|
|
@ -1669,6 +1669,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
|||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
int force_ref_update = ref->force || force_update;
|
||||
int reject_reason = 0;
|
||||
int needs_force_reject_reason = 0;
|
||||
|
||||
if (ref->peer_ref)
|
||||
oidcpy(&ref->new_oid, &ref->peer_ref->new_oid);
|
||||
|
|
@ -1693,16 +1694,17 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
|||
*
|
||||
* If the tip of the remote-tracking ref is unreachable
|
||||
* from any reflog entry of its local ref indicating a
|
||||
* possible update since checkout; reject the push.
|
||||
* possible update since checkout, then remember the
|
||||
* rejection in case force push is needed.
|
||||
*/
|
||||
if (ref->expect_old_sha1) {
|
||||
if (!oideq(&ref->old_oid, &ref->old_oid_expect))
|
||||
reject_reason = REF_STATUS_REJECT_STALE;
|
||||
else if (ref->check_reachable && ref->unreachable)
|
||||
reject_reason =
|
||||
needs_force_reject_reason =
|
||||
REF_STATUS_REJECT_REMOTE_UPDATED;
|
||||
else if (ref->check_reachable && ref->unverifiable)
|
||||
reject_reason =
|
||||
needs_force_reject_reason =
|
||||
REF_STATUS_REJECT_UNVERIFIABLE;
|
||||
else
|
||||
/*
|
||||
|
|
@ -1746,6 +1748,14 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
|||
reject_reason = REF_STATUS_REJECT_NONFASTFORWARD;
|
||||
}
|
||||
|
||||
/*
|
||||
* If fast-forward rules rejected the push and we were
|
||||
* asked to verify the reflog but were unable to, then
|
||||
* reflog verification is the right reject_reason.
|
||||
*/
|
||||
if (needs_force_reject_reason && reject_reason)
|
||||
reject_reason = needs_force_reject_reason;
|
||||
|
||||
/*
|
||||
* "--force" will defeat any rejection implemented
|
||||
* by the rules above.
|
||||
|
|
|
|||
|
|
@ -421,6 +421,33 @@ test_expect_success '"--force-if-includes" should allow forced update from HEAD'
|
|||
)
|
||||
'
|
||||
|
||||
test_expect_success '"--force-if-includes" should allow fast-forward push without local reflog' '
|
||||
setup_src_dup_dst &&
|
||||
test_when_finished "rm -fr dst src dup" &&
|
||||
(
|
||||
cd src &&
|
||||
git fetch &&
|
||||
git switch main &&
|
||||
git reset --hard origin/main &&
|
||||
test_commit I &&
|
||||
git reflog expire --expire=all --all &&
|
||||
git push --force-with-lease --force-if-includes origin main
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success '"--force-if-includes" should allow fast-forward push from tag' '
|
||||
setup_src_dup_dst &&
|
||||
test_when_finished "rm -fr dst src dup" &&
|
||||
(
|
||||
cd src &&
|
||||
git fetch &&
|
||||
git switch -c newbranch origin/main &&
|
||||
test_commit I &&
|
||||
git tag T &&
|
||||
git push --force-with-lease --force-if-includes origin T:main
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success '"--force-if-includes" should reject forced update from differently named branches when local lacks remote ref' '
|
||||
setup_src_dup_dst &&
|
||||
test_when_finished "rm -fr dst src dup" &&
|
||||
|
|
|
|||
Loading…
Reference in New Issue