Skip to content

Commit dbe4dbc

Browse files
push: suggest <remote> <branch> for a slash slip
When pushing the 'main' branch to the remote 'origin', i.e., $ git push origin main it is easy to mistakenly write $ git push origin/main That is parsed as the repository to push to, and since 'origin/main' is neither a configured remote nor a path it dies with: fatal: 'origin/main' does not appear to be a git repository Often 'origin/main' does not exist as a repository, so the command fails without doing any harm, but it gives no hint that a space was meant instead of a slash and can leave the user puzzled. When the argument is not an existing path or configured remote but its part before the first slash names one, suggest the intended '<remote> <branch>' form: $ git push origin main The suggestion is shown as advice so it can be silenced with advice.pushRepoLooksLikeRef. Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
1 parent 9883c28 commit dbe4dbc

5 files changed

Lines changed: 74 additions & 1 deletion

File tree

Documentation/config/advice.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ all advice messages.
9090
Shown when linkgit:git-push[1] rejects a forced update of
9191
a branch when its remote-tracking ref has updates that we
9292
do not have locally.
93+
pushRepoLooksLikeRef::
94+
Shown when the repository given to linkgit:git-push[1] is not
95+
a configured remote but looks like a `<remote>/<branch>` ref,
96+
suggesting that the remote and branch be given as separate
97+
arguments.
9398
pushUnqualifiedRefname::
9499
Shown when linkgit:git-push[1] gives up trying to
95100
guess based on the source and destination refs what

advice.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ static struct {
6969
[ADVICE_PUSH_NON_FF_CURRENT] = { "pushNonFFCurrent" },
7070
[ADVICE_PUSH_NON_FF_MATCHING] = { "pushNonFFMatching" },
7171
[ADVICE_PUSH_REF_NEEDS_UPDATE] = { "pushRefNeedsUpdate" },
72+
[ADVICE_PUSH_REPO_LOOKS_LIKE_REF] = { "pushRepoLooksLikeRef" },
7273
[ADVICE_PUSH_UNQUALIFIED_REF_NAME] = { "pushUnqualifiedRefName" },
7374
[ADVICE_PUSH_UPDATE_REJECTED] = { "pushUpdateRejected" },
7475
[ADVICE_PUSH_UPDATE_REJECTED_ALIAS] = { "pushNonFastForward" }, /* backwards compatibility */

advice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ enum advice_type {
3636
ADVICE_PUSH_NON_FF_CURRENT,
3737
ADVICE_PUSH_NON_FF_MATCHING,
3838
ADVICE_PUSH_REF_NEEDS_UPDATE,
39+
ADVICE_PUSH_REPO_LOOKS_LIKE_REF,
3940
ADVICE_PUSH_UNQUALIFIED_REF_NAME,
4041
ADVICE_PUSH_UPDATE_REJECTED,
4142
ADVICE_PUSH_UPDATE_REJECTED_ALIAS,

builtin/push.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "advice.h"
99
#include "branch.h"
1010
#include "config.h"
11+
#include "dir.h"
1112
#include "environment.h"
1213
#include "gettext.h"
1314
#include "hex.h"
@@ -662,6 +663,29 @@ static int push_multiple(struct string_list *list,
662663
return result;
663664
}
664665

666+
static void die_if_repo_looks_like_ref(const char *repo)
667+
{
668+
const char *slash = strchr(repo, '/');
669+
struct strbuf name = STRBUF_INIT;
670+
int code;
671+
672+
if (!slash || !slash[1] || file_exists(repo))
673+
return;
674+
675+
strbuf_add(&name, repo, slash - repo);
676+
if (!remote_is_configured(remote_get(name.buf), 0)) {
677+
strbuf_release(&name);
678+
return;
679+
}
680+
681+
code = die_message(_("'%s' is not a valid push target"), repo);
682+
advise_if_enabled(ADVICE_PUSH_REPO_LOOKS_LIKE_REF,
683+
_("Did you mean to use: git push %s %s?"),
684+
name.buf, slash + 1);
685+
strbuf_release(&name);
686+
exit(code);
687+
}
688+
665689
int cmd_push(int argc,
666690
const char **argv,
667691
const char *prefix,
@@ -744,6 +768,17 @@ int cmd_push(int argc,
744768

745769
if (repo) {
746770
if (!add_remote_or_group(repo, &remote_group)) {
771+
struct remote *r;
772+
773+
/*
774+
* Check the advice up front to avoid the remote
775+
* lookup when the hint is off. The helper still
776+
* calls advise_if_enabled() so the hint carries the
777+
* standard "disable this message" instructions.
778+
*/
779+
if (advice_enabled(ADVICE_PUSH_REPO_LOOKS_LIKE_REF))
780+
die_if_repo_looks_like_ref(repo);
781+
747782
/*
748783
* Not a configured remote name or group name.
749784
* Try treating it as a direct URL or path, e.g.
@@ -753,7 +788,7 @@ int cmd_push(int argc,
753788
* from the URL so the loop below can handle it
754789
* identically to a named remote.
755790
*/
756-
struct remote *r = pushremote_get(repo);
791+
r = pushremote_get(repo);
757792
if (!r)
758793
die(_("bad repository '%s'"), repo);
759794
string_list_append(&remote_group, r->name);

t/t5529-push-errors.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,37 @@ test_expect_success 'detect empty remote with targeted refspec' '
5454
grep "fatal: bad repository ${SQ}${SQ}" stderr
5555
'
5656

57+
test_expect_success 'suggest <remote> <branch> for a <remote>/<branch> slip' '
58+
test_must_fail git push origin/main 2>stderr &&
59+
test_grep "${SQ}origin/main${SQ} is not a valid push target" stderr &&
60+
test_grep "hint: Did you mean to use: git push origin main?" stderr &&
61+
test_must_fail git -c advice.pushRepoLooksLikeRef=false push origin/main 2>stderr &&
62+
test_grep ! "Did you mean" stderr
63+
'
64+
65+
test_expect_success 'suggest <remote> <branch> when the branch has slashes' '
66+
test_must_fail git push origin/feature/x 2>stderr &&
67+
test_grep "hint: Did you mean to use: git push origin feature/x?" stderr
68+
'
69+
70+
test_expect_success 'no suggestion when prefix is not a configured remote' '
71+
test_must_fail git push not-a-remote/main 2>stderr &&
72+
test_grep ! "Did you mean" stderr
73+
'
74+
75+
test_expect_success 'no suggestion for a trailing slash with no branch' '
76+
test_must_fail git push origin/ 2>stderr &&
77+
test_grep ! "Did you mean" stderr
78+
'
79+
80+
test_expect_success 'no suggestion when the argument is an existing path' '
81+
test_when_finished "rm -rf origin" &&
82+
git init --bare origin/main &&
83+
git push origin/main HEAD:refs/heads/pushed 2>stderr &&
84+
test_grep ! "Did you mean" stderr &&
85+
git -C origin/main rev-parse --verify refs/heads/pushed
86+
'
87+
5788
test_expect_success 'detect ambiguous refs early' '
5889
git branch foo &&
5990
git tag foo &&

0 commit comments

Comments
 (0)