Skip to content

Commit

Permalink
Merge branch 'ct/advise-push-default' into maint
Browse files Browse the repository at this point in the history
The cases "git push" fails due to non-ff can be broken into three
categories; each case is given a separate advise message.

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
  • Loading branch information
Junio C Hamano committed May 11, 2012
2 parents 9a10117 + 0aff719 commit 07e74b0
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 13 deletions.
19 changes: 17 additions & 2 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,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
Expand Down
6 changes: 6 additions & 0 deletions advice.c
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 },
Expand Down
3 changes: 3 additions & 0 deletions advice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
60 changes: 55 additions & 5 deletions builtin/push.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion environment.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion t/lib-httpd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
'
}
2 changes: 1 addition & 1 deletion t/t5541-http-push.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
'

Expand Down
13 changes: 11 additions & 2 deletions transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 07e74b0

Please sign in to comment.