path: root/builtin
diff options
authorJunio C Hamano <>2013-01-23 21:55:30 (GMT)
committerJunio C Hamano <>2013-01-24 22:37:23 (GMT)
commit75e5c0dc5529aed42122b3a774e6b17383e51b66 (patch)
tree78600fde190fede594ec314d81bbc817220a8bf6 /builtin
parent0f4d498dbecbc1b6da66f926df3bc12446bd44dd (diff)
When we push to update an existing ref, if: * the object at the tip of the remote is not a commit; or * the object we are pushing is not a commit, it won't be correct to suggest to fetch, integrate and push again, as the old and new objects will not "merge". We should explain that the push must be forced when there is a non-committish object is involved in such a case. If we do not have the current object at the tip of the remote, we do not even know that object, when fetched, is something that can be merged. In such a case, suggesting to pull first just like non-fast-forward case may not be technically correct, but in practice, most such failures are seen when you try to push your work to a branch without knowing that somebody else already pushed to update the same branch since you forked, so "pull first" would work as a suggestion most of the time. And if the object at the tip is not a commit, "pull first" will fail, without making any permanent damage. As a side effect, it also makes the error message the user will get during the next "push" attempt easier to understand, now the user is aware that a non-commit object is involved. In these cases, the current code already rejects such a push on the client end, but we used the same error and advice messages as the ones used when rejecting a non-fast-forward push, i.e. pull from there and integrate before pushing again. Introduce new rejection reasons and reword the messages appropriately. [jc: with help by Peff on message details] Signed-off-by: Junio C Hamano <>
Diffstat (limited to 'builtin')
2 files changed, 40 insertions, 0 deletions
diff --git a/builtin/push.c b/builtin/push.c
index 8491e43..a2b3fbe 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -220,10 +220,22 @@ static const char message_advice_checkout_pull_push[] =
"(e.g. 'git pull') before pushing again.\n"
"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[] =
N_("Updates were rejected because the destination reference already exists\n"
"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)
if (!advice_push_non_ff_current || !advice_push_update_rejected)
@@ -252,6 +264,20 @@ static void advise_ref_already_exists(void)
+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)
int err;
@@ -285,6 +311,10 @@ static int push_with_options(struct transport *transport, int flags)
} else if (reject_reasons & REJECT_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;
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index f849e0a..57a46b2 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -44,6 +44,16 @@ static void print_helper_status(struct ref *ref)
msg = "non-fast forward";
+ res = "error";
+ msg = "fetch first";
+ break;
+ res = "error";
+ msg = "needs force";
+ break;
res = "error";
msg = "already exists";