Merge branch 'jc/push-reject-reasons'
Improve error and advice messages given locally when "git push" refuses when it cannot compute fast-forwardness by separating these cases from the normal "not a fast-forward; merge first and push again" case. * jc/push-reject-reasons: push: finishing touches to explain REJECT_ALREADY_EXISTS better push: introduce REJECT_FETCH_FIRST and REJECT_NEEDS_FORCE push: further simplify the logic to assign rejection reason push: further clean up fields of "struct ref"
This commit is contained in:
@ -143,7 +143,8 @@ advice.*::
|
|||||||
pushUpdateRejected::
|
pushUpdateRejected::
|
||||||
Set this variable to 'false' if you want to disable
|
Set this variable to 'false' if you want to disable
|
||||||
'pushNonFFCurrent', 'pushNonFFDefault',
|
'pushNonFFCurrent', 'pushNonFFDefault',
|
||||||
'pushNonFFMatching', and 'pushAlreadyExists'
|
'pushNonFFMatching', 'pushAlreadyExists',
|
||||||
|
'pushFetchFirst', and 'pushNeedsForce'
|
||||||
simultaneously.
|
simultaneously.
|
||||||
pushNonFFCurrent::
|
pushNonFFCurrent::
|
||||||
Advice shown when linkgit:git-push[1] fails due to a
|
Advice shown when linkgit:git-push[1] fails due to a
|
||||||
@ -162,6 +163,15 @@ advice.*::
|
|||||||
pushAlreadyExists::
|
pushAlreadyExists::
|
||||||
Shown when linkgit:git-push[1] rejects an update that
|
Shown when linkgit:git-push[1] rejects an update that
|
||||||
does not qualify for fast-forwarding (e.g., a tag.)
|
does not qualify for fast-forwarding (e.g., a tag.)
|
||||||
|
pushFetchFirst::
|
||||||
|
Shown when linkgit:git-push[1] rejects an update that
|
||||||
|
tries to overwrite a remote ref that points at an
|
||||||
|
object we do not have.
|
||||||
|
pushNeedsForce::
|
||||||
|
Shown when linkgit:git-push[1] rejects an update that
|
||||||
|
tries to overwrite a remote ref that points at an
|
||||||
|
object that is not a committish, or make the remote
|
||||||
|
ref point at an object that is not a committish.
|
||||||
statusHints::
|
statusHints::
|
||||||
Show directions on how to proceed from the current
|
Show directions on how to proceed from the current
|
||||||
state in the output of linkgit:git-status[1], in
|
state in the output of linkgit:git-status[1], in
|
||||||
|
4
advice.c
4
advice.c
@ -5,6 +5,8 @@ int advice_push_non_ff_current = 1;
|
|||||||
int advice_push_non_ff_default = 1;
|
int advice_push_non_ff_default = 1;
|
||||||
int advice_push_non_ff_matching = 1;
|
int advice_push_non_ff_matching = 1;
|
||||||
int advice_push_already_exists = 1;
|
int advice_push_already_exists = 1;
|
||||||
|
int advice_push_fetch_first = 1;
|
||||||
|
int advice_push_needs_force = 1;
|
||||||
int advice_status_hints = 1;
|
int advice_status_hints = 1;
|
||||||
int advice_commit_before_merge = 1;
|
int advice_commit_before_merge = 1;
|
||||||
int advice_resolve_conflict = 1;
|
int advice_resolve_conflict = 1;
|
||||||
@ -20,6 +22,8 @@ static struct {
|
|||||||
{ "pushnonffdefault", &advice_push_non_ff_default },
|
{ "pushnonffdefault", &advice_push_non_ff_default },
|
||||||
{ "pushnonffmatching", &advice_push_non_ff_matching },
|
{ "pushnonffmatching", &advice_push_non_ff_matching },
|
||||||
{ "pushalreadyexists", &advice_push_already_exists },
|
{ "pushalreadyexists", &advice_push_already_exists },
|
||||||
|
{ "pushfetchfirst", &advice_push_fetch_first },
|
||||||
|
{ "pushneedsforce", &advice_push_needs_force },
|
||||||
{ "statushints", &advice_status_hints },
|
{ "statushints", &advice_status_hints },
|
||||||
{ "commitbeforemerge", &advice_commit_before_merge },
|
{ "commitbeforemerge", &advice_commit_before_merge },
|
||||||
{ "resolveconflict", &advice_resolve_conflict },
|
{ "resolveconflict", &advice_resolve_conflict },
|
||||||
|
2
advice.h
2
advice.h
@ -8,6 +8,8 @@ extern int advice_push_non_ff_current;
|
|||||||
extern int advice_push_non_ff_default;
|
extern int advice_push_non_ff_default;
|
||||||
extern int advice_push_non_ff_matching;
|
extern int advice_push_non_ff_matching;
|
||||||
extern int advice_push_already_exists;
|
extern int advice_push_already_exists;
|
||||||
|
extern int advice_push_fetch_first;
|
||||||
|
extern int advice_push_needs_force;
|
||||||
extern int advice_status_hints;
|
extern int advice_status_hints;
|
||||||
extern int advice_commit_before_merge;
|
extern int advice_commit_before_merge;
|
||||||
extern int advice_resolve_conflict;
|
extern int advice_resolve_conflict;
|
||||||
|
@ -220,9 +220,20 @@ static const char message_advice_checkout_pull_push[] =
|
|||||||
"(e.g. 'git pull') before pushing again.\n"
|
"(e.g. 'git pull') before pushing again.\n"
|
||||||
"See the 'Note about fast-forwards' in 'git push --help' for details.");
|
"See the 'Note about fast-forwards' in 'git push --help' for details.");
|
||||||
|
|
||||||
|
static const char message_advice_ref_fetch_first[] =
|
||||||
|
N_("Updates were rejected because the remote contains work that you do\n"
|
||||||
|
"not have locally. This is usually caused by another repository pushing\n"
|
||||||
|
"to the same ref. You may want to first merge the remote changes (e.g.,\n"
|
||||||
|
"'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[] =
|
static const char message_advice_ref_already_exists[] =
|
||||||
N_("Updates were rejected because the destination reference already exists\n"
|
N_("Updates were rejected because the tag already exists in the remote.");
|
||||||
"in the remote.");
|
|
||||||
|
static const char message_advice_ref_needs_force[] =
|
||||||
|
N_("You cannot update a remote ref that points at a non-commit object,\n"
|
||||||
|
"or update a remote ref to make it point at a non-commit object,\n"
|
||||||
|
"without using the '--force' option.\n");
|
||||||
|
|
||||||
static void advise_pull_before_push(void)
|
static void advise_pull_before_push(void)
|
||||||
{
|
{
|
||||||
@ -252,6 +263,20 @@ static void advise_ref_already_exists(void)
|
|||||||
advise(_(message_advice_ref_already_exists));
|
advise(_(message_advice_ref_already_exists));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void advise_ref_fetch_first(void)
|
||||||
|
{
|
||||||
|
if (!advice_push_fetch_first || !advice_push_update_rejected)
|
||||||
|
return;
|
||||||
|
advise(_(message_advice_ref_fetch_first));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void advise_ref_needs_force(void)
|
||||||
|
{
|
||||||
|
if (!advice_push_needs_force || !advice_push_update_rejected)
|
||||||
|
return;
|
||||||
|
advise(_(message_advice_ref_needs_force));
|
||||||
|
}
|
||||||
|
|
||||||
static int push_with_options(struct transport *transport, int flags)
|
static int push_with_options(struct transport *transport, int flags)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -285,6 +310,10 @@ static int push_with_options(struct transport *transport, int flags)
|
|||||||
advise_checkout_pull_push();
|
advise_checkout_pull_push();
|
||||||
} else if (reject_reasons & REJECT_ALREADY_EXISTS) {
|
} else if (reject_reasons & REJECT_ALREADY_EXISTS) {
|
||||||
advise_ref_already_exists();
|
advise_ref_already_exists();
|
||||||
|
} else if (reject_reasons & REJECT_FETCH_FIRST) {
|
||||||
|
advise_ref_fetch_first();
|
||||||
|
} else if (reject_reasons & REJECT_NEEDS_FORCE) {
|
||||||
|
advise_ref_needs_force();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -44,6 +44,16 @@ static void print_helper_status(struct ref *ref)
|
|||||||
msg = "non-fast forward";
|
msg = "non-fast forward";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case REF_STATUS_REJECT_FETCH_FIRST:
|
||||||
|
res = "error";
|
||||||
|
msg = "fetch first";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REF_STATUS_REJECT_NEEDS_FORCE:
|
||||||
|
res = "error";
|
||||||
|
msg = "needs force";
|
||||||
|
break;
|
||||||
|
|
||||||
case REF_STATUS_REJECT_ALREADY_EXISTS:
|
case REF_STATUS_REJECT_ALREADY_EXISTS:
|
||||||
res = "error";
|
res = "error";
|
||||||
msg = "already exists";
|
msg = "already exists";
|
||||||
|
6
cache.h
6
cache.h
@ -1015,10 +1015,8 @@ struct ref {
|
|||||||
char *symref;
|
char *symref;
|
||||||
unsigned int
|
unsigned int
|
||||||
force:1,
|
force:1,
|
||||||
requires_force:1,
|
forced_update:1,
|
||||||
merge:1,
|
merge:1,
|
||||||
nonfastforward:1,
|
|
||||||
update:1,
|
|
||||||
deletion:1;
|
deletion:1;
|
||||||
enum {
|
enum {
|
||||||
REF_STATUS_NONE = 0,
|
REF_STATUS_NONE = 0,
|
||||||
@ -1026,6 +1024,8 @@ struct ref {
|
|||||||
REF_STATUS_REJECT_NONFASTFORWARD,
|
REF_STATUS_REJECT_NONFASTFORWARD,
|
||||||
REF_STATUS_REJECT_ALREADY_EXISTS,
|
REF_STATUS_REJECT_ALREADY_EXISTS,
|
||||||
REF_STATUS_REJECT_NODELETE,
|
REF_STATUS_REJECT_NODELETE,
|
||||||
|
REF_STATUS_REJECT_FETCH_FIRST,
|
||||||
|
REF_STATUS_REJECT_NEEDS_FORCE,
|
||||||
REF_STATUS_UPTODATE,
|
REF_STATUS_UPTODATE,
|
||||||
REF_STATUS_REMOTE_REJECT,
|
REF_STATUS_REMOTE_REJECT,
|
||||||
REF_STATUS_EXPECTING_REPORT
|
REF_STATUS_EXPECTING_REPORT
|
||||||
|
38
remote.c
38
remote.c
@ -1317,28 +1317,23 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
|||||||
* passing the --force argument
|
* passing the --force argument
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ref->update =
|
if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
|
||||||
!ref->deletion &&
|
int why = 0; /* why would this push require --force? */
|
||||||
!is_null_sha1(ref->old_sha1);
|
|
||||||
|
|
||||||
if (ref->update) {
|
if (!prefixcmp(ref->name, "refs/tags/"))
|
||||||
ref->nonfastforward =
|
why = REF_STATUS_REJECT_ALREADY_EXISTS;
|
||||||
!has_sha1_file(ref->old_sha1)
|
else if (!has_sha1_file(ref->old_sha1))
|
||||||
|| !ref_newer(ref->new_sha1, ref->old_sha1);
|
why = REF_STATUS_REJECT_FETCH_FIRST;
|
||||||
|
else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
|
||||||
|
!lookup_commit_reference_gently(ref->new_sha1, 1))
|
||||||
|
why = REF_STATUS_REJECT_NEEDS_FORCE;
|
||||||
|
else if (!ref_newer(ref->new_sha1, ref->old_sha1))
|
||||||
|
why = REF_STATUS_REJECT_NONFASTFORWARD;
|
||||||
|
|
||||||
if (!prefixcmp(ref->name, "refs/tags/")) {
|
if (!force_ref_update)
|
||||||
ref->requires_force = 1;
|
ref->status = why;
|
||||||
if (!force_ref_update) {
|
else if (why)
|
||||||
ref->status = REF_STATUS_REJECT_ALREADY_EXISTS;
|
ref->forced_update = 1;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (ref->nonfastforward) {
|
|
||||||
ref->requires_force = 1;
|
|
||||||
if (!force_ref_update) {
|
|
||||||
ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1532,7 +1527,8 @@ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
|
|||||||
struct commit_list *list, *used;
|
struct commit_list *list, *used;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
|
|
||||||
/* Both new and old must be commit-ish and new is descendant of
|
/*
|
||||||
|
* Both new and old must be commit-ish and new is descendant of
|
||||||
* old. Otherwise we require --force.
|
* old. Otherwise we require --force.
|
||||||
*/
|
*/
|
||||||
o = deref_tag(parse_object(old_sha1), NULL, 0);
|
o = deref_tag(parse_object(old_sha1), NULL, 0);
|
||||||
|
@ -230,6 +230,8 @@ int send_pack(struct send_pack_args *args,
|
|||||||
switch (ref->status) {
|
switch (ref->status) {
|
||||||
case REF_STATUS_REJECT_NONFASTFORWARD:
|
case REF_STATUS_REJECT_NONFASTFORWARD:
|
||||||
case REF_STATUS_REJECT_ALREADY_EXISTS:
|
case REF_STATUS_REJECT_ALREADY_EXISTS:
|
||||||
|
case REF_STATUS_REJECT_FETCH_FIRST:
|
||||||
|
case REF_STATUS_REJECT_NEEDS_FORCE:
|
||||||
case REF_STATUS_UPTODATE:
|
case REF_STATUS_UPTODATE:
|
||||||
continue;
|
continue;
|
||||||
default:
|
default:
|
||||||
|
@ -666,6 +666,16 @@ static void push_update_ref_status(struct strbuf *buf,
|
|||||||
free(msg);
|
free(msg);
|
||||||
msg = NULL;
|
msg = NULL;
|
||||||
}
|
}
|
||||||
|
else if (!strcmp(msg, "fetch first")) {
|
||||||
|
status = REF_STATUS_REJECT_FETCH_FIRST;
|
||||||
|
free(msg);
|
||||||
|
msg = NULL;
|
||||||
|
}
|
||||||
|
else if (!strcmp(msg, "needs force")) {
|
||||||
|
status = REF_STATUS_REJECT_NEEDS_FORCE;
|
||||||
|
free(msg);
|
||||||
|
msg = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*ref)
|
if (*ref)
|
||||||
|
14
transport.c
14
transport.c
@ -659,7 +659,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
|
|||||||
const char *msg;
|
const char *msg;
|
||||||
|
|
||||||
strcpy(quickref, status_abbrev(ref->old_sha1));
|
strcpy(quickref, status_abbrev(ref->old_sha1));
|
||||||
if (ref->requires_force) {
|
if (ref->forced_update) {
|
||||||
strcat(quickref, "...");
|
strcat(quickref, "...");
|
||||||
type = '+';
|
type = '+';
|
||||||
msg = "forced update";
|
msg = "forced update";
|
||||||
@ -699,6 +699,14 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
|
|||||||
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
||||||
"already exists", porcelain);
|
"already exists", porcelain);
|
||||||
break;
|
break;
|
||||||
|
case REF_STATUS_REJECT_FETCH_FIRST:
|
||||||
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
||||||
|
"fetch first", porcelain);
|
||||||
|
break;
|
||||||
|
case REF_STATUS_REJECT_NEEDS_FORCE:
|
||||||
|
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
|
||||||
|
"needs force", porcelain);
|
||||||
|
break;
|
||||||
case REF_STATUS_REMOTE_REJECT:
|
case REF_STATUS_REMOTE_REJECT:
|
||||||
print_ref_status('!', "[remote rejected]", ref,
|
print_ref_status('!', "[remote rejected]", ref,
|
||||||
ref->deletion ? NULL : ref->peer_ref,
|
ref->deletion ? NULL : ref->peer_ref,
|
||||||
@ -750,6 +758,10 @@ void transport_print_push_status(const char *dest, struct ref *refs,
|
|||||||
*reject_reasons |= REJECT_NON_FF_OTHER;
|
*reject_reasons |= REJECT_NON_FF_OTHER;
|
||||||
} else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {
|
} else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {
|
||||||
*reject_reasons |= REJECT_ALREADY_EXISTS;
|
*reject_reasons |= REJECT_ALREADY_EXISTS;
|
||||||
|
} else if (ref->status == REF_STATUS_REJECT_FETCH_FIRST) {
|
||||||
|
*reject_reasons |= REJECT_FETCH_FIRST;
|
||||||
|
} else if (ref->status == REF_STATUS_REJECT_NEEDS_FORCE) {
|
||||||
|
*reject_reasons |= REJECT_NEEDS_FORCE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,8 @@ void transport_set_verbosity(struct transport *transport, int verbosity,
|
|||||||
#define REJECT_NON_FF_HEAD 0x01
|
#define REJECT_NON_FF_HEAD 0x01
|
||||||
#define REJECT_NON_FF_OTHER 0x02
|
#define REJECT_NON_FF_OTHER 0x02
|
||||||
#define REJECT_ALREADY_EXISTS 0x04
|
#define REJECT_ALREADY_EXISTS 0x04
|
||||||
|
#define REJECT_FETCH_FIRST 0x08
|
||||||
|
#define REJECT_NEEDS_FORCE 0x10
|
||||||
|
|
||||||
int transport_push(struct transport *connection,
|
int transport_push(struct transport *connection,
|
||||||
int refspec_nr, const char **refspec, int flags,
|
int refspec_nr, const char **refspec, int flags,
|
||||||
|
Reference in New Issue
Block a user