push: add an advice on unqualified <dst> push
Add an advice to the recently improved error message added in
f8aae12034
("push: allow unqualified dest refspecs to DWIM",
2008-04-23).
Now with advice.pushUnqualifiedRefName=true (on by default) we show a
hint about how to proceed:
$ ./git-push avar v2.19.0^{commit}:newbranch -n
error: The destination you provided is not a full refname (i.e.,
starting with "refs/"). We tried to guess what you meant by:
- Looking for a ref that matches 'newbranch' on the remote side.
- Checking if the <src> being pushed ('v2.19.0^{commit}')
is a ref in "refs/{heads,tags}/". If so we add a corresponding
refs/{heads,tags}/ prefix on the remote side.
Neither worked, so we gave up. You must fully qualify the ref.
hint: The <src> part of the refspec is a commit object.
hint: Did you mean to create a new branch by pushing to
hint: 'v2.19.0^{commit}:refs/heads/newbranch'?
error: failed to push some refs to 'git@github.com:avar/git.git'
When trying to push a tag, tree or a blob we suggest that perhaps the
user meant to push them to refs/tags/ instead.
The if/else duplication for all of OBJ_{COMMIT,TAG,TREE,BLOB} is
unfortunate, but is required to correctly mark the messages for
translation. See the discussion in
<87r2gxebsi.fsf@evledraar.gmail.com> about that.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
04d17287a0
commit
dd8dd300c6
@ -30,6 +30,13 @@ advice.*::
|
|||||||
tries to overwrite a remote ref that points at an
|
tries to overwrite a remote ref that points at an
|
||||||
object that is not a commit-ish, or make the remote
|
object that is not a commit-ish, or make the remote
|
||||||
ref point at an object that is not a commit-ish.
|
ref point at an object that is not a commit-ish.
|
||||||
|
pushUnqualifiedRefname::
|
||||||
|
Shown when linkgit:git-push[1] gives up trying to
|
||||||
|
guess based on the source and destination refs what
|
||||||
|
remote ref namespace the source belongs in, but where
|
||||||
|
we can still suggest that the user push to either
|
||||||
|
refs/heads/* or refs/tags/* based on the type of the
|
||||||
|
source object.
|
||||||
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
|
||||||
|
2
advice.c
2
advice.c
@ -9,6 +9,7 @@ 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_fetch_first = 1;
|
||||||
int advice_push_needs_force = 1;
|
int advice_push_needs_force = 1;
|
||||||
|
int advice_push_unqualified_ref_name = 1;
|
||||||
int advice_status_hints = 1;
|
int advice_status_hints = 1;
|
||||||
int advice_status_u_option = 1;
|
int advice_status_u_option = 1;
|
||||||
int advice_commit_before_merge = 1;
|
int advice_commit_before_merge = 1;
|
||||||
@ -63,6 +64,7 @@ static struct {
|
|||||||
{ "pushAlreadyExists", &advice_push_already_exists },
|
{ "pushAlreadyExists", &advice_push_already_exists },
|
||||||
{ "pushFetchFirst", &advice_push_fetch_first },
|
{ "pushFetchFirst", &advice_push_fetch_first },
|
||||||
{ "pushNeedsForce", &advice_push_needs_force },
|
{ "pushNeedsForce", &advice_push_needs_force },
|
||||||
|
{ "pushUnqualifiedRefName", &advice_push_unqualified_ref_name },
|
||||||
{ "statusHints", &advice_status_hints },
|
{ "statusHints", &advice_status_hints },
|
||||||
{ "statusUoption", &advice_status_u_option },
|
{ "statusUoption", &advice_status_u_option },
|
||||||
{ "commitBeforeMerge", &advice_commit_before_merge },
|
{ "commitBeforeMerge", &advice_commit_before_merge },
|
||||||
|
1
advice.h
1
advice.h
@ -9,6 +9,7 @@ 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_fetch_first;
|
||||||
extern int advice_push_needs_force;
|
extern int advice_push_needs_force;
|
||||||
|
extern int advice_push_unqualified_ref_name;
|
||||||
extern int advice_status_hints;
|
extern int advice_status_hints;
|
||||||
extern int advice_status_u_option;
|
extern int advice_status_u_option;
|
||||||
extern int advice_commit_before_merge;
|
extern int advice_commit_before_merge;
|
||||||
|
37
remote.c
37
remote.c
@ -13,6 +13,7 @@
|
|||||||
#include "mergesort.h"
|
#include "mergesort.h"
|
||||||
#include "argv-array.h"
|
#include "argv-array.h"
|
||||||
#include "commit-reach.h"
|
#include "commit-reach.h"
|
||||||
|
#include "advice.h"
|
||||||
|
|
||||||
enum map_direction { FROM_SRC, FROM_DST };
|
enum map_direction { FROM_SRC, FROM_DST };
|
||||||
|
|
||||||
@ -1008,6 +1009,9 @@ static int match_explicit_lhs(struct ref *src,
|
|||||||
static void show_push_unqualified_ref_name_error(const char *dst_value,
|
static void show_push_unqualified_ref_name_error(const char *dst_value,
|
||||||
const char *matched_src_name)
|
const char *matched_src_name)
|
||||||
{
|
{
|
||||||
|
struct object_id oid;
|
||||||
|
enum object_type type;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TRANSLATORS: "matches '%s'%" is the <dst> part of "git push
|
* TRANSLATORS: "matches '%s'%" is the <dst> part of "git push
|
||||||
* <remote> <src>:<dst>" push, and "being pushed ('%s')" is
|
* <remote> <src>:<dst>" push, and "being pushed ('%s')" is
|
||||||
@ -1023,6 +1027,39 @@ static void show_push_unqualified_ref_name_error(const char *dst_value,
|
|||||||
"\n"
|
"\n"
|
||||||
"Neither worked, so we gave up. You must fully qualify the ref."),
|
"Neither worked, so we gave up. You must fully qualify the ref."),
|
||||||
dst_value, matched_src_name);
|
dst_value, matched_src_name);
|
||||||
|
|
||||||
|
if (!advice_push_unqualified_ref_name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (get_oid(matched_src_name, &oid))
|
||||||
|
BUG("'%s' is not a valid object, "
|
||||||
|
"match_explicit_lhs() should catch this!",
|
||||||
|
matched_src_name);
|
||||||
|
type = oid_object_info(the_repository, &oid, NULL);
|
||||||
|
if (type == OBJ_COMMIT) {
|
||||||
|
advise(_("The <src> part of the refspec is a commit object.\n"
|
||||||
|
"Did you mean to create a new branch by pushing to\n"
|
||||||
|
"'%s:refs/heads/%s'?"),
|
||||||
|
matched_src_name, dst_value);
|
||||||
|
} else if (type == OBJ_TAG) {
|
||||||
|
advise(_("The <src> part of the refspec is a tag object.\n"
|
||||||
|
"Did you mean to create a new tag by pushing to\n"
|
||||||
|
"'%s:refs/tags/%s'?"),
|
||||||
|
matched_src_name, dst_value);
|
||||||
|
} else if (type == OBJ_TREE) {
|
||||||
|
advise(_("The <src> part of the refspec is a tree object.\n"
|
||||||
|
"Did you mean to tag a new tree by pushing to\n"
|
||||||
|
"'%s:refs/tags/%s'?"),
|
||||||
|
matched_src_name, dst_value);
|
||||||
|
} else if (type == OBJ_BLOB) {
|
||||||
|
advise(_("The <src> part of the refspec is a blob object.\n"
|
||||||
|
"Did you mean to tag a new blob by pushing to\n"
|
||||||
|
"'%s:refs/tags/%s'?"),
|
||||||
|
matched_src_name, dst_value);
|
||||||
|
} else {
|
||||||
|
BUG("'%s' should be commit/tag/tree/blob, is '%d'",
|
||||||
|
matched_src_name, type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int match_explicit(struct ref *src, struct ref *dst,
|
static int match_explicit(struct ref *src, struct ref *dst,
|
||||||
|
@ -1222,4 +1222,32 @@ test_expect_success 'add remote matching the "insteadOf" URL' '
|
|||||||
git remote add backup xyz@example.com
|
git remote add backup xyz@example.com
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'unqualified <dst> refspec DWIM and advice' '
|
||||||
|
test_when_finished "(cd test && git tag -d some-tag)" &&
|
||||||
|
(
|
||||||
|
cd test &&
|
||||||
|
git tag -a -m "Some tag" some-tag master &&
|
||||||
|
exit_with=true &&
|
||||||
|
for type in commit tag tree blob
|
||||||
|
do
|
||||||
|
if test "$type" = "blob"
|
||||||
|
then
|
||||||
|
oid=$(git rev-parse some-tag:file)
|
||||||
|
else
|
||||||
|
oid=$(git rev-parse some-tag^{$type})
|
||||||
|
fi &&
|
||||||
|
test_must_fail git push origin $oid:dst 2>err &&
|
||||||
|
test_i18ngrep "error: The destination you" err &&
|
||||||
|
test_i18ngrep "hint: Did you mean" err &&
|
||||||
|
test_must_fail git -c advice.pushUnqualifiedRefName=false \
|
||||||
|
push origin $oid:dst 2>err &&
|
||||||
|
test_i18ngrep "error: The destination you" err &&
|
||||||
|
test_i18ngrep ! "hint: Did you mean" err ||
|
||||||
|
exit_with=false
|
||||||
|
done &&
|
||||||
|
$exit_with
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user