path: root/builtin/push.c
diff options
authorChristopher Tiwald <>2012-03-20 04:31:33 (GMT)
committerJunio C Hamano <>2012-03-20 04:42:06 (GMT)
commitf25950f3475e263ed6c8f0797bb058ba6444f85e (patch)
tree93090d1d543b82c68166593a04a50f0478172b11 /builtin/push.c
parent0e2d57fd50f61e668be3180bc8f25991ea88aa8c (diff)
push: Provide situational hints for non-fast-forward errors
Pushing a non-fast-forward update to a remote repository will result in an error, but the hint text doesn't provide the correct resolution in every case. Give better resolution advice in three push scenarios: 1) If you push your current branch and it triggers a non-fast-forward error, you should merge remote changes with 'git pull' before pushing again. 2) If you push to a shared repository others push to, and your local tracking branches are not kept up to date, the 'matching refs' default will generate non-fast-forward errors on outdated branches. If this is your workflow, the 'matching refs' default is not for you. Consider setting the 'push.default' configuration variable to 'current' or 'upstream' to ensure only your current branch is pushed. 3) If you explicitly specify a ref that is not your current branch or push matching branches with ':', you will generate a non-fast-forward error if any pushed branch tip is out of date. You should checkout the offending branch and merge remote changes before pushing again. Teach transport.c to recognize these scenarios and configure push.c to hint for them. If 'git push's default behavior changes or we discover more scenarios, extension is easy. Standardize on the advice API and add three new advice variables, 'pushNonFFCurrent', 'pushNonFFDefault', and 'pushNonFFMatching'. Setting any of these to 'false' will disable their affiliated advice. Setting 'pushNonFastForward' to false will disable all three, thus preserving the config option for users who already set it, but guaranteeing new users won't disable push advice accidentally. Based-on-patch-by: Junio C Hamano <> Signed-off-by: Christopher Tiwald <> Signed-off-by: Junio C Hamano <>
Diffstat (limited to 'builtin/push.c')
1 files changed, 55 insertions, 5 deletions
diff --git a/builtin/push.c b/builtin/push.c
index d315475..8a14e4b 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -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)
@@ -95,6 +96,9 @@ static void setup_default_push_refspecs(struct remote *remote)
switch (push_default) {
+ default_matching_used = 1;
+ /* fallthru */
@@ -114,6 +118,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;
@@ -135,14 +178,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;