Skip to content

Commit 59eaa50

Browse files
push: suggest <remote> <branch> when a slash slips into the repository
"git push origin/main" is treated as a repository and dies with "'origin/main' does not appear to be a git repository", with no hint that a space was meant instead of a slash. When the argument is not an existing path or configured remote but its part before the first slash names one, suggest the intended "git push <remote> <branch>" form. The suggestion is shown as advice so it can be silenced with advice.pushRepoLooksLikeRef. Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
1 parent 9455cb4 commit 59eaa50

5 files changed

Lines changed: 63 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: 25 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"
@@ -744,6 +745,29 @@ int cmd_push(int argc,
744745

745746
if (repo) {
746747
if (!add_remote_or_group(repo, &remote_group)) {
748+
const char *slash = strchr(repo, '/');
749+
struct remote *r;
750+
751+
/*
752+
* A "<remote>/<branch>" argument that does not name
753+
* a path is likely a slip for the separate
754+
* "<remote> <branch>" form, so suggest that instead.
755+
*/
756+
if (slash && slash[1] && !file_exists(repo)) {
757+
struct strbuf name = STRBUF_INIT;
758+
759+
strbuf_add(&name, repo, slash - repo);
760+
if (remote_is_configured(remote_get(name.buf), 0)) {
761+
int code = die_message(_("'%s' is not a valid push target"), repo);
762+
advise_if_enabled(ADVICE_PUSH_REPO_LOOKS_LIKE_REF,
763+
_("Did you mean to use: git push %s %s?"),
764+
name.buf, slash + 1);
765+
strbuf_release(&name);
766+
exit(code);
767+
}
768+
strbuf_release(&name);
769+
}
770+
747771
/*
748772
* Not a configured remote name or group name.
749773
* Try treating it as a direct URL or path, e.g.
@@ -753,7 +777,7 @@ int cmd_push(int argc,
753777
* from the URL so the loop below can handle it
754778
* identically to a named remote.
755779
*/
756-
struct remote *r = pushremote_get(repo);
780+
r = pushremote_get(repo);
757781
if (!r)
758782
die(_("bad repository '%s'"), repo);
759783
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+
grep "${SQ}origin/main${SQ} is not a valid push target" stderr &&
60+
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+
! 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+
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+
! 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+
! 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+
! 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)