Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Documentation/config/advice.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ all advice messages.
Shown when linkgit:git-push[1] rejects a forced update of
a branch when its remote-tracking ref has updates that we
do not have locally.
pushRepoLooksLikeRef::
Shown when the repository given to linkgit:git-push[1] is not
a configured remote but looks like a `<remote>/<branch>` ref,
suggesting that the remote and branch be given as separate
arguments.
pushUnqualifiedRefname::
Shown when linkgit:git-push[1] gives up trying to
guess based on the source and destination refs what
Expand Down
1 change: 1 addition & 0 deletions advice.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ static struct {
[ADVICE_PUSH_NON_FF_CURRENT] = { "pushNonFFCurrent" },
[ADVICE_PUSH_NON_FF_MATCHING] = { "pushNonFFMatching" },
[ADVICE_PUSH_REF_NEEDS_UPDATE] = { "pushRefNeedsUpdate" },
[ADVICE_PUSH_REPO_LOOKS_LIKE_REF] = { "pushRepoLooksLikeRef" },
[ADVICE_PUSH_UNQUALIFIED_REF_NAME] = { "pushUnqualifiedRefName" },
[ADVICE_PUSH_UPDATE_REJECTED] = { "pushUpdateRejected" },
[ADVICE_PUSH_UPDATE_REJECTED_ALIAS] = { "pushNonFastForward" }, /* backwards compatibility */
Expand Down
1 change: 1 addition & 0 deletions advice.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ enum advice_type {
ADVICE_PUSH_NON_FF_CURRENT,
ADVICE_PUSH_NON_FF_MATCHING,
ADVICE_PUSH_REF_NEEDS_UPDATE,
ADVICE_PUSH_REPO_LOOKS_LIKE_REF,
ADVICE_PUSH_UNQUALIFIED_REF_NAME,
ADVICE_PUSH_UPDATE_REJECTED,
ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
Expand Down
17 changes: 17 additions & 0 deletions builtin/branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,23 @@ int cmd_branch(int argc,
if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname)) {
if (!argc || branch_checked_out(branch->refname))
die(_("no commit on branch '%s' yet"), branch->name);
if (argc == 1 && !strchr(new_upstream, '/') &&
remote_is_configured(remote_get(new_upstream), 0)) {
struct strbuf remote_ref = STRBUF_INIT;

strbuf_addf(&remote_ref, "refs/remotes/%s/%s",
new_upstream, argv[0]);
if (refs_ref_exists(get_main_ref_store(the_repository),
remote_ref.buf)) {
int code = die_message(_("--set-upstream-to takes a single <remote>/<branch> argument"));
advise_if_enabled(ADVICE_SET_UPSTREAM_FAILURE,
_("Did you mean to use: git branch --set-upstream-to=%s/%s?"),
new_upstream, argv[0]);
strbuf_release(&remote_ref);
exit(code);
}
strbuf_release(&remote_ref);
}
die(_("branch '%s' does not exist"), branch->name);
}

Expand Down
26 changes: 25 additions & 1 deletion builtin/push.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "advice.h"
#include "branch.h"
#include "config.h"
#include "dir.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
Expand Down Expand Up @@ -744,6 +745,29 @@ int cmd_push(int argc,

if (repo) {
if (!add_remote_or_group(repo, &remote_group)) {
const char *slash = strchr(repo, '/');
struct remote *r;

/*
* A "<remote>/<branch>" argument that does not name
* a path is likely a slip for the separate
* "<remote> <branch>" form, so suggest that instead.
*/
if (slash && slash[1] && !file_exists(repo)) {
struct strbuf name = STRBUF_INIT;

strbuf_add(&name, repo, slash - repo);
if (remote_is_configured(remote_get(name.buf), 0)) {
int code = die_message(_("'%s' is not a valid push target"), repo);
advise_if_enabled(ADVICE_PUSH_REPO_LOOKS_LIKE_REF,
_("Did you mean to use: git push %s %s?"),
name.buf, slash + 1);
strbuf_release(&name);
exit(code);
}
strbuf_release(&name);
}

/*
* Not a configured remote name or group name.
* Try treating it as a direct URL or path, e.g.
Expand All @@ -753,7 +777,7 @@ int cmd_push(int argc,
* from the URL so the loop below can handle it
* identically to a named remote.
*/
struct remote *r = pushremote_get(repo);
r = pushremote_get(repo);
if (!r)
die(_("bad repository '%s'"), repo);
string_list_append(&remote_group, r->name);
Expand Down
38 changes: 38 additions & 0 deletions t/t3200-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,44 @@ test_expect_success '--set-upstream-to fails on a missing dst branch' '
test_cmp expect err
'

test_expect_success '--set-upstream-to suggests <remote>/<branch> on slip' '
test_when_finished "git remote remove slip-remote" &&
git remote add slip-remote . &&
git update-ref refs/remotes/slip-remote/slip-feature HEAD &&
test_must_fail git branch --set-upstream-to slip-remote slip-feature 2>err &&
test_grep "takes a single <remote>/<branch> argument" err &&
test_grep "hint: Did you mean to use: git branch --set-upstream-to=slip-remote/slip-feature?" err &&
test_must_fail git -c advice.setUpstreamFailure=false \
branch --set-upstream-to slip-remote slip-feature 2>err &&
test_grep ! "Did you mean" err
'

test_expect_success '--set-upstream-to does not suggest when no matching remote ref' '
test_when_finished "git remote remove slip-remote" &&
git remote add slip-remote . &&
test_must_fail git branch --set-upstream-to slip-remote no-such-branch 2>err &&
test_grep "branch ${SQ}no-such-branch${SQ} does not exist" err &&
test_grep ! "Did you mean" err
'

test_expect_success '--set-upstream-to to a local branch is not mistaken for a slip' '
git branch slip-local-upstream &&
git branch slip-local-target &&
git branch --set-upstream-to=slip-local-upstream slip-local-target 2>err &&
test_grep ! "Did you mean" err &&
echo refs/heads/slip-local-upstream >expect &&
git config branch.slip-local-target.merge >actual &&
test_cmp expect actual
'

test_expect_success '--set-upstream-to slip suggestion keeps a slashed branch name' '
test_when_finished "git remote remove slip-remote" &&
git remote add slip-remote . &&
git update-ref refs/remotes/slip-remote/slip/feature HEAD &&
test_must_fail git branch --set-upstream-to slip-remote slip/feature 2>err &&
test_grep "hint: Did you mean to use: git branch --set-upstream-to=slip-remote/slip/feature?" err
'

test_expect_success '--set-upstream-to fails on a missing src branch' '
test_must_fail git branch --set-upstream-to does-not-exist main 2>err &&
test_grep "the requested upstream branch '"'"'does-not-exist'"'"' does not exist" err
Expand Down
31 changes: 31 additions & 0 deletions t/t5529-push-errors.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,37 @@ test_expect_success 'detect empty remote with targeted refspec' '
grep "fatal: bad repository ${SQ}${SQ}" stderr
'

test_expect_success 'suggest <remote> <branch> for a <remote>/<branch> slip' '
test_must_fail git push origin/main 2>stderr &&
grep "${SQ}origin/main${SQ} is not a valid push target" stderr &&
grep "hint: Did you mean to use: git push origin main?" stderr &&
test_must_fail git -c advice.pushRepoLooksLikeRef=false push origin/main 2>stderr &&
! grep "Did you mean" stderr
'

test_expect_success 'suggest <remote> <branch> when the branch has slashes' '
test_must_fail git push origin/feature/x 2>stderr &&
grep "hint: Did you mean to use: git push origin feature/x?" stderr
'

test_expect_success 'no suggestion when prefix is not a configured remote' '
test_must_fail git push not-a-remote/main 2>stderr &&
! grep "Did you mean" stderr
'

test_expect_success 'no suggestion for a trailing slash with no branch' '
test_must_fail git push origin/ 2>stderr &&
! grep "Did you mean" stderr
'

test_expect_success 'no suggestion when the argument is an existing path' '
test_when_finished "rm -rf origin" &&
git init --bare origin/main &&
git push origin/main HEAD:refs/heads/pushed 2>stderr &&
! grep "Did you mean" stderr &&
git -C origin/main rev-parse --verify refs/heads/pushed
'

test_expect_success 'detect ambiguous refs early' '
git branch foo &&
git tag foo &&
Expand Down
Loading