Merge branch 'ct/advise-push-default'

Break down the cases in which "git push" fails due to non-ff into
three categories, and give separate advise messages for each case.

By Christopher Tiwald (2) and Jeff King (1)
* ct/advise-push-default:
  Fix httpd tests that broke when non-ff push advice changed
  clean up struct ref's nonfastforward field
  push: Provide situational hints for non-fast-forward errors
This commit is contained in:
Junio C Hamano 2012-04-20 15:50:37 -07:00
commit c5da24a73a
10 changed files with 99 additions and 13 deletions

View File

@ -138,8 +138,23 @@ advice.*::
+
--
pushNonFastForward::
Advice shown when linkgit:git-push[1] refuses
non-fast-forward refs.
Set this variable to 'false' if you want to disable
'pushNonFFCurrent', 'pushNonFFDefault', and
'pushNonFFMatching' simultaneously.
pushNonFFCurrent::
Advice shown when linkgit:git-push[1] fails due to a
non-fast-forward update to the current branch.
pushNonFFDefault::
Advice to set 'push.default' to 'upstream' or 'current'
when you ran linkgit:git-push[1] and pushed 'matching
refs' by default (i.e. you did not provide an explicit
refspec, and no 'push.default' configuration was set)
and it resulted in a non-fast-forward error.
pushNonFFMatching::
Advice shown when you ran linkgit:git-push[1] and pushed
'matching refs' explicitly (i.e. you used ':', or
specified a refspec that isn't your current branch) and
it resulted in a non-fast-forward error.
statusHints::
Directions on how to stage/unstage/add shown in the
output of linkgit:git-status[1] and the template shown

View File

@ -1,6 +1,9 @@
#include "cache.h"
int advice_push_nonfastforward = 1;
int advice_push_non_ff_current = 1;
int advice_push_non_ff_default = 1;
int advice_push_non_ff_matching = 1;
int advice_status_hints = 1;
int advice_commit_before_merge = 1;
int advice_resolve_conflict = 1;
@ -12,6 +15,9 @@ static struct {
int *preference;
} advice_config[] = {
{ "pushnonfastforward", &advice_push_nonfastforward },
{ "pushnonffcurrent", &advice_push_non_ff_current },
{ "pushnonffdefault", &advice_push_non_ff_default },
{ "pushnonffmatching", &advice_push_non_ff_matching },
{ "statushints", &advice_status_hints },
{ "commitbeforemerge", &advice_commit_before_merge },
{ "resolveconflict", &advice_resolve_conflict },

View File

@ -4,6 +4,9 @@
#include "git-compat-util.h"
extern int advice_push_nonfastforward;
extern int advice_push_non_ff_current;
extern int advice_push_non_ff_default;
extern int advice_push_non_ff_matching;
extern int advice_status_hints;
extern int advice_commit_before_merge;
extern int advice_resolve_conflict;

View File

@ -24,6 +24,7 @@ static int progress = -1;
static const char **refspec;
static int refspec_nr;
static int refspec_alloc;
static int default_matching_used;
static void add_refspec(const char *ref)
{
@ -111,6 +112,9 @@ static void setup_default_push_refspecs(struct remote *remote)
{
switch (push_default) {
default:
case PUSH_DEFAULT_UNSPECIFIED:
default_matching_used = 1;
/* fallthru */
case PUSH_DEFAULT_MATCHING:
add_refspec(":");
break;
@ -130,6 +134,45 @@ static void setup_default_push_refspecs(struct remote *remote)
}
}
static const char message_advice_pull_before_push[] =
N_("Updates were rejected because the tip of your current branch is behind\n"
"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
"before pushing again.\n"
"See the 'Note about fast-forwards' in 'git push --help' for details.");
static const char message_advice_use_upstream[] =
N_("Updates were rejected because a pushed branch tip is behind its remote\n"
"counterpart. If you did not intend to push that branch, you may want to\n"
"specify branches to push or set the 'push.default' configuration\n"
"variable to 'current' or 'upstream' to push only the current branch.");
static const char message_advice_checkout_pull_push[] =
N_("Updates were rejected because a pushed branch tip is behind its remote\n"
"counterpart. Check out this branch and merge the remote changes\n"
"(e.g. 'git pull') before pushing again.\n"
"See the 'Note about fast-forwards' in 'git push --help' for details.");
static void advise_pull_before_push(void)
{
if (!advice_push_non_ff_current || !advice_push_nonfastforward)
return;
advise(_(message_advice_pull_before_push));
}
static void advise_use_upstream(void)
{
if (!advice_push_non_ff_default || !advice_push_nonfastforward)
return;
advise(_(message_advice_use_upstream));
}
static void advise_checkout_pull_push(void)
{
if (!advice_push_non_ff_matching || !advice_push_nonfastforward)
return;
advise(_(message_advice_checkout_pull_push));
}
static int push_with_options(struct transport *transport, int flags)
{
int err;
@ -151,14 +194,21 @@ static int push_with_options(struct transport *transport, int flags)
error(_("failed to push some refs to '%s'"), transport->url);
err |= transport_disconnect(transport);
if (!err)
return 0;
if (nonfastforward && advice_push_nonfastforward) {
fprintf(stderr, _("To prevent you from losing history, non-fast-forward updates were rejected\n"
"Merge the remote changes (e.g. 'git pull') before pushing again. See the\n"
"'Note about fast-forwards' section of 'git push --help' for details.\n"));
switch (nonfastforward) {
default:
break;
case NON_FF_HEAD:
advise_pull_before_push();
break;
case NON_FF_OTHER:
if (default_matching_used)
advise_use_upstream();
else
advise_checkout_pull_push();
break;
}
return 1;

View File

@ -625,7 +625,8 @@ enum push_default_type {
PUSH_DEFAULT_NOTHING = 0,
PUSH_DEFAULT_MATCHING,
PUSH_DEFAULT_UPSTREAM,
PUSH_DEFAULT_CURRENT
PUSH_DEFAULT_CURRENT,
PUSH_DEFAULT_UNSPECIFIED
};
extern enum branch_track git_branch_track;

View File

@ -52,7 +52,7 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
enum push_default_type push_default = PUSH_DEFAULT_MATCHING;
enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
#ifndef OBJECT_CREATION_MODE
#define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS
#endif

View File

@ -160,6 +160,6 @@ test_http_push_nonff() {
'
test_expect_success 'non-fast-forward push shows help message' '
test_i18ngrep "To prevent you from losing history, non-fast-forward updates were rejected" output
test_i18ngrep "Updates were rejected because" output
'
}

View File

@ -168,7 +168,7 @@ test_expect_success 'push fails for non-fast-forward refs unmatched by remote he
'
test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
test_i18ngrep "To prevent you from losing history, non-fast-forward updates were rejected" \
test_i18ngrep "Updates were rejected because" \
output
'

View File

@ -721,6 +721,10 @@ void transport_print_push_status(const char *dest, struct ref *refs,
{
struct ref *ref;
int n = 0;
unsigned char head_sha1[20];
char *head;
head = resolve_refdup("HEAD", head_sha1, 1, NULL);
if (verbose) {
for (ref = refs; ref; ref = ref->next)
@ -738,8 +742,13 @@ void transport_print_push_status(const char *dest, struct ref *refs,
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 = 1;
if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD &&
*nonfastforward != NON_FF_HEAD) {
if (!strcmp(head, ref->name))
*nonfastforward = NON_FF_HEAD;
else
*nonfastforward = NON_FF_OTHER;
}
}
}

View File

@ -138,6 +138,8 @@ 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
int transport_push(struct transport *connection,
int refspec_nr, const char **refspec, int flags,
int * nonfastforward);