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:
Ævar Arnfjörð Bjarmason 2018-11-13 19:52:43 +00:00 коммит произвёл Junio C Hamano
Родитель 04d17287a0
Коммит dd8dd300c6
5 изменённых файлов: 75 добавлений и 0 удалений

Просмотреть файл

@ -30,6 +30,13 @@ advice.*::
tries to overwrite a remote ref that points at an
object that is not a commit-ish, or make the remote
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::
Show directions on how to proceed from the current
state in the output of linkgit:git-status[1], in

Просмотреть файл

@ -9,6 +9,7 @@ int advice_push_non_ff_matching = 1;
int advice_push_already_exists = 1;
int advice_push_fetch_first = 1;
int advice_push_needs_force = 1;
int advice_push_unqualified_ref_name = 1;
int advice_status_hints = 1;
int advice_status_u_option = 1;
int advice_commit_before_merge = 1;
@ -63,6 +64,7 @@ static struct {
{ "pushAlreadyExists", &advice_push_already_exists },
{ "pushFetchFirst", &advice_push_fetch_first },
{ "pushNeedsForce", &advice_push_needs_force },
{ "pushUnqualifiedRefName", &advice_push_unqualified_ref_name },
{ "statusHints", &advice_status_hints },
{ "statusUoption", &advice_status_u_option },
{ "commitBeforeMerge", &advice_commit_before_merge },

Просмотреть файл

@ -9,6 +9,7 @@ extern int advice_push_non_ff_matching;
extern int advice_push_already_exists;
extern int advice_push_fetch_first;
extern int advice_push_needs_force;
extern int advice_push_unqualified_ref_name;
extern int advice_status_hints;
extern int advice_status_u_option;
extern int advice_commit_before_merge;

Просмотреть файл

@ -13,6 +13,7 @@
#include "mergesort.h"
#include "argv-array.h"
#include "commit-reach.h"
#include "advice.h"
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,
const char *matched_src_name)
{
struct object_id oid;
enum object_type type;
/*
* TRANSLATORS: "matches '%s'%" is the <dst> part of "git push
* <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"
"Neither worked, so we gave up. You must fully qualify the ref."),
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,

Просмотреть файл

@ -1222,4 +1222,32 @@ test_expect_success 'add remote matching the "insteadOf" URL' '
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