push: fix --force-if-includes non-branch advice
When a --force-if-includes push is rejected due lacking reflog to
consult, the advice is misleading:
! [rejected] HEAD -> main (remote ref updated since checkout)
error: failed to push some refs to '<remote>'
hint: Updates were rejected because the tip of the remote-tracking
hint: branch has been updated since the last checkout. If you want
hint: to integrate the remote changes, use 'git pull' before
hint: pushing again. See the 'Note about fast-forwards' in 'git
hint: push --help' for details.
But a `git pull` will not fix this rejection. What is required is either
- Specify the expected remote tip with --force-with-lease=<ref>:<expect>
- Ignore the error with --no-force-if-includes
Add ref->unverifiable to differentiate pushing something without a
reflog to consult vs. a remote update rejection.
Ensure tests check the rejection message.
Reported-by: D. Ben Knoble <ben.knoble@gmail.com>
Signed-off-by: Tyler Cipriani <tyler@tylercipriani.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
parent
c9d6141b2a
commit
ce389b9556
|
|
@ -90,6 +90,10 @@ all advice messages.
|
|||
Shown when linkgit:git-push[1] rejects a forced update of
|
||||
a branch when its remote-tracking ref has updates that we
|
||||
do not have locally.
|
||||
pushRefUnverifiable::
|
||||
Shown when linkgit:git-push[1] rejects a forced update of
|
||||
a branch when we are unable to verify the remote-tracking
|
||||
ref is integrated locally.
|
||||
pushUnqualifiedRefname::
|
||||
Shown when linkgit:git-push[1] gives up trying to
|
||||
guess based on the source and destination refs what
|
||||
|
|
|
|||
1
advice.c
1
advice.c
|
|
@ -69,6 +69,7 @@ static struct {
|
|||
[ADVICE_PUSH_NON_FF_CURRENT] = { "pushNonFFCurrent" },
|
||||
[ADVICE_PUSH_NON_FF_MATCHING] = { "pushNonFFMatching" },
|
||||
[ADVICE_PUSH_REF_NEEDS_UPDATE] = { "pushRefNeedsUpdate" },
|
||||
[ADVICE_PUSH_REF_UNVERIFIABLE] = { "pushRefUnverifiable" },
|
||||
[ADVICE_PUSH_UNQUALIFIED_REF_NAME] = { "pushUnqualifiedRefName" },
|
||||
[ADVICE_PUSH_UPDATE_REJECTED] = { "pushUpdateRejected" },
|
||||
[ADVICE_PUSH_UPDATE_REJECTED_ALIAS] = { "pushNonFastForward" }, /* backwards compatibility */
|
||||
|
|
|
|||
1
advice.h
1
advice.h
|
|
@ -36,6 +36,7 @@ enum advice_type {
|
|||
ADVICE_PUSH_NON_FF_CURRENT,
|
||||
ADVICE_PUSH_NON_FF_MATCHING,
|
||||
ADVICE_PUSH_REF_NEEDS_UPDATE,
|
||||
ADVICE_PUSH_REF_UNVERIFIABLE,
|
||||
ADVICE_PUSH_UNQUALIFIED_REF_NAME,
|
||||
ADVICE_PUSH_UPDATE_REJECTED,
|
||||
ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
|
||||
|
|
|
|||
|
|
@ -319,6 +319,13 @@ static const char message_advice_ref_needs_update[] =
|
|||
"remote changes, use 'git pull' before pushing again.\n"
|
||||
"See the 'Note about fast-forwards' in 'git push --help' for details.");
|
||||
|
||||
static const char message_advice_ref_unverifiable[] =
|
||||
N_("Updates were rejected because what you are pushing is not a branch,\n"
|
||||
"so there is no reflog to check against the tip of the remote-tracking\n"
|
||||
"branch. If you want to push anyway, specify the expected value with\n"
|
||||
"'--force-with-lease=<ref>:<expect>' or use '--no-force-if-includes'\n"
|
||||
"to skip this check.");
|
||||
|
||||
static void advise_pull_before_push(void)
|
||||
{
|
||||
if (!advice_enabled(ADVICE_PUSH_NON_FF_CURRENT) || !advice_enabled(ADVICE_PUSH_UPDATE_REJECTED))
|
||||
|
|
@ -361,6 +368,14 @@ static void advise_ref_needs_update(void)
|
|||
advise(_(message_advice_ref_needs_update));
|
||||
}
|
||||
|
||||
static void advise_ref_unverifiable(void)
|
||||
{
|
||||
if (!advice_enabled(ADVICE_PUSH_REF_UNVERIFIABLE) ||
|
||||
!advice_enabled(ADVICE_PUSH_UPDATE_REJECTED))
|
||||
return;
|
||||
advise(_(message_advice_ref_unverifiable));
|
||||
}
|
||||
|
||||
static int push_with_options(struct transport *transport, struct refspec *rs,
|
||||
int flags)
|
||||
{
|
||||
|
|
@ -412,6 +427,8 @@ static int push_with_options(struct transport *transport, struct refspec *rs,
|
|||
advise_ref_needs_force();
|
||||
} else if (reject_reasons & REJECT_REF_NEEDS_UPDATE) {
|
||||
advise_ref_needs_update();
|
||||
} else if (reject_reasons & REJECT_REF_UNVERIFIABLE) {
|
||||
advise_ref_unverifiable();
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -76,6 +76,11 @@ static void print_helper_status(struct ref *ref)
|
|||
msg = "remote ref updated since checkout";
|
||||
break;
|
||||
|
||||
case REF_STATUS_REJECT_UNVERIFIABLE:
|
||||
res = "error";
|
||||
msg = "remote ref unverifiable";
|
||||
break;
|
||||
|
||||
case REF_STATUS_REJECT_ALREADY_EXISTS:
|
||||
res = "error";
|
||||
msg = "already exists";
|
||||
|
|
|
|||
5
remote.c
5
remote.c
|
|
@ -1701,6 +1701,9 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
|||
else if (ref->check_reachable && ref->unreachable)
|
||||
reject_reason =
|
||||
REF_STATUS_REJECT_REMOTE_UPDATED;
|
||||
else if (ref->check_reachable && ref->unverifiable)
|
||||
reject_reason =
|
||||
REF_STATUS_REJECT_UNVERIFIABLE;
|
||||
else
|
||||
/*
|
||||
* If the ref isn't stale, and is reachable
|
||||
|
|
@ -2826,7 +2829,7 @@ static void check_if_includes_upstream(struct ref *remote)
|
|||
* then there is no reliable reflog to check
|
||||
*/
|
||||
if (!name || !starts_with(name, "refs/heads/")) {
|
||||
remote->unreachable = 1;
|
||||
remote->unverifiable = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
10
remote.h
10
remote.h
|
|
@ -169,10 +169,13 @@ struct ref {
|
|||
/* Need to check if local reflog reaches the remote tip. */
|
||||
check_reachable:1,
|
||||
/*
|
||||
* Store the result of the check enabled by "check_reachable";
|
||||
* implies the local reflog does not reach the remote tip.
|
||||
* Store the result of the check enabled by "check_reachable".
|
||||
* "unreachable" implies the local reflog does not reach the remote
|
||||
* tip. "unverifiable" implies no local branch reflog to check; i.e.,
|
||||
* detached HEAD.
|
||||
*/
|
||||
unreachable:1;
|
||||
unreachable:1,
|
||||
unverifiable:1;
|
||||
|
||||
enum {
|
||||
REF_NOT_MATCHED = 0, /* initial value */
|
||||
|
|
@ -203,6 +206,7 @@ struct ref {
|
|||
REF_STATUS_REJECT_STALE,
|
||||
REF_STATUS_REJECT_SHALLOW,
|
||||
REF_STATUS_REJECT_REMOTE_UPDATED,
|
||||
REF_STATUS_REJECT_UNVERIFIABLE,
|
||||
REF_STATUS_UPTODATE,
|
||||
REF_STATUS_REMOTE_REJECT,
|
||||
REF_STATUS_EXPECTING_REPORT,
|
||||
|
|
|
|||
|
|
@ -322,6 +322,7 @@ static int check_to_send_update(const struct ref *ref, const struct send_pack_ar
|
|||
case REF_STATUS_REJECT_NEEDS_FORCE:
|
||||
case REF_STATUS_REJECT_STALE:
|
||||
case REF_STATUS_REJECT_REMOTE_UPDATED:
|
||||
case REF_STATUS_REJECT_UNVERIFIABLE:
|
||||
case REF_STATUS_REJECT_NODELETE:
|
||||
return CHECK_REF_STATUS_REJECTED;
|
||||
case REF_STATUS_UPTODATE:
|
||||
|
|
|
|||
|
|
@ -311,7 +311,8 @@ test_expect_success 'background updates to remote can be mitigated with "--force
|
|||
git switch main &&
|
||||
test_commit J &&
|
||||
git fetch --all &&
|
||||
test_must_fail git push --force-with-lease --force-if-includes --all
|
||||
test_must_fail git push --force-with-lease --force-if-includes --all 2>err &&
|
||||
test_grep "remote ref updated since checkout" err
|
||||
) &&
|
||||
git ls-remote dst refs/heads/main >actual.main &&
|
||||
git ls-remote dst refs/heads/branch >actual.branch &&
|
||||
|
|
@ -458,7 +459,9 @@ test_expect_success '"--force-if-includes" should reject forced update from deta
|
|||
git reset --hard origin/main &&
|
||||
git switch -c newbranch origin/main &&
|
||||
git checkout HEAD^ &&
|
||||
test_must_fail git push --force-if-includes --force-with-lease origin HEAD:main
|
||||
test_must_fail git push --force-if-includes --force-with-lease origin HEAD:main 2>err &&
|
||||
test_grep "remote ref unverifiable" err &&
|
||||
test_grep "no-force-if-includes" err
|
||||
)
|
||||
'
|
||||
|
||||
|
|
@ -473,7 +476,9 @@ test_expect_success '"--force-if-includes" should reject forced update from tag'
|
|||
git switch -c newbranch origin/main &&
|
||||
git checkout HEAD^ &&
|
||||
git tag stable &&
|
||||
test_must_fail git push --force-if-includes --force-with-lease origin stable:main
|
||||
test_must_fail git push --force-if-includes --force-with-lease origin stable:main 2>err &&
|
||||
test_grep "remote ref unverifiable" err &&
|
||||
test_grep "no-force-if-includes" err
|
||||
)
|
||||
'
|
||||
|
||||
|
|
|
|||
|
|
@ -893,6 +893,10 @@ static int push_update_ref_status(struct strbuf *buf,
|
|||
status = REF_STATUS_REJECT_REMOTE_UPDATED;
|
||||
FREE_AND_NULL(msg);
|
||||
}
|
||||
else if (!strcmp(msg, "remote ref unverifiable")) {
|
||||
status = REF_STATUS_REJECT_UNVERIFIABLE;
|
||||
FREE_AND_NULL(msg);
|
||||
}
|
||||
else if (!strcmp(msg, "forced update")) {
|
||||
forced = 1;
|
||||
FREE_AND_NULL(msg);
|
||||
|
|
@ -1046,6 +1050,7 @@ static int push_refs_with_push(struct transport *transport,
|
|||
case REF_STATUS_REJECT_STALE:
|
||||
case REF_STATUS_REJECT_ALREADY_EXISTS:
|
||||
case REF_STATUS_REJECT_REMOTE_UPDATED:
|
||||
case REF_STATUS_REJECT_UNVERIFIABLE:
|
||||
if (atomic) {
|
||||
reject_atomic_push(remote_refs, mirror);
|
||||
string_list_clear(&cas_options, 0);
|
||||
|
|
|
|||
|
|
@ -779,6 +779,11 @@ static int print_one_push_report(struct ref *ref, const char *dest, int count,
|
|||
"remote ref updated since checkout",
|
||||
report, porcelain, summary_width);
|
||||
break;
|
||||
case REF_STATUS_REJECT_UNVERIFIABLE:
|
||||
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
||||
"remote ref unverifiable",
|
||||
report, porcelain, summary_width);
|
||||
break;
|
||||
case REF_STATUS_REJECT_SHALLOW:
|
||||
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
||||
"new shallow roots not allowed",
|
||||
|
|
@ -893,6 +898,8 @@ void transport_print_push_status(const char *dest, struct ref *refs,
|
|||
*reject_reasons |= REJECT_NEEDS_FORCE;
|
||||
} else if (ref->status == REF_STATUS_REJECT_REMOTE_UPDATED) {
|
||||
*reject_reasons |= REJECT_REF_NEEDS_UPDATE;
|
||||
} else if (ref->status == REF_STATUS_REJECT_UNVERIFIABLE) {
|
||||
*reject_reasons |= REJECT_REF_UNVERIFIABLE;
|
||||
}
|
||||
}
|
||||
free(head);
|
||||
|
|
@ -1348,6 +1355,7 @@ static int pre_push_hook_feed_stdin(int hook_stdin_fd, void *pp_cb UNUSED, void
|
|||
switch (r->status) {
|
||||
case REF_STATUS_REJECT_NONFASTFORWARD:
|
||||
case REF_STATUS_REJECT_REMOTE_UPDATED:
|
||||
case REF_STATUS_REJECT_UNVERIFIABLE:
|
||||
case REF_STATUS_REJECT_STALE:
|
||||
case REF_STATUS_UPTODATE:
|
||||
return 0; /* skip refs which won't be pushed */
|
||||
|
|
|
|||
|
|
@ -256,6 +256,7 @@ void transport_set_verbosity(struct transport *transport, int verbosity,
|
|||
#define REJECT_FETCH_FIRST 0x08
|
||||
#define REJECT_NEEDS_FORCE 0x10
|
||||
#define REJECT_REF_NEEDS_UPDATE 0x20
|
||||
#define REJECT_REF_UNVERIFIABLE 0x40
|
||||
|
||||
int transport_push(struct repository *repo,
|
||||
struct transport *connection,
|
||||
|
|
|
|||
Loading…
Reference in New Issue