diff --git a/internal/app/ref.go b/internal/app/ref.go index 20a8051f..ffd5ec58 100644 --- a/internal/app/ref.go +++ b/internal/app/ref.go @@ -55,6 +55,10 @@ func parseRef(raw string, kind refKind) (ref, error) { if !hashed { repo, value = "", raw } + // Only a repository before # makes a reference, so a branch such as fix#12 stays a branch. + if repo != "" && !repositoryLike(repo) { + return ref{}, errNotRef + } n, err := strconv.ParseInt(value, 10, 64) if err != nil && !hashed { return ref{}, errNotRef @@ -65,6 +69,12 @@ func parseRef(raw string, kind refKind) (ref, error) { return ref{Number: n, Repo: repo}, nil } +// repositoryLike reports whether raw has the form OWNER/REPO or HOST/OWNER/REPO. +func repositoryLike(raw string) bool { + pieces := strings.Split(raw, "/") + return len(pieces) >= 2 && settings.ValidSlug(strings.Join(pieces[len(pieces)-2:], "/")) +} + func parseRefURL(raw string, kind refKind) (ref, error) { invalid := clierr.Wrap(clierr.Usage, fmt.Errorf("%q is not %s URL; use %s", raw, kind.article(), kind.forms())) u, err := url.Parse(raw) diff --git a/internal/app/ref_test.go b/internal/app/ref_test.go index 814569e5..0d74c411 100644 --- a/internal/app/ref_test.go +++ b/internal/app/ref_test.go @@ -26,7 +26,6 @@ func TestParseRefAcceptsNumbersQualifiedNumbersAndURLs(t *testing.T) { {raw: " 12 ", want: ref{Number: 12}}, {raw: "alice/project#12", want: ref{Number: 12, Repo: "alice/project"}}, {raw: "localhost:3001/alice/project#12", want: ref{Number: 12, Repo: "localhost:3001/alice/project"}}, - {raw: "project#12", want: ref{Number: 12, Repo: "project"}}, {raw: "https://gitea.com/alice/project/issues/3", want: ref{Number: 3, Repo: "https://gitea.com/alice/project"}}, {raw: "https://gitea.com/alice/project/issues/3#issuecomment-79", want: ref{Number: 3, Repo: "https://gitea.com/alice/project"}}, {raw: "http://localhost:3001/base/alice/project/pulls/7/files?style=split", kind: pullRef, want: ref{Number: 7, Repo: "http://localhost:3001/base/alice/project"}}, @@ -52,7 +51,7 @@ func TestParseRefAcceptsNumbersQualifiedNumbersAndURLs(t *testing.T) { require.NoError(t, err, tc.raw) require.Equal(t, tc.want, got, tc.raw) } - for _, branch := range []string{"feature/login", "bob:fix", "main"} { + for _, branch := range []string{"feature/login", "bob:fix", "main", "fix#12", "project#12", "bob:fix#3"} { _, err := parseRef(branch, pullRef) require.ErrorIs(t, err, errNotRef, branch) } @@ -164,6 +163,7 @@ func TestPullSelectorAcceptsNumbersURLsAndBranches(t *testing.T) { {"bob:fix", 17, ""}, {"alice:fix", 0, `no open pull request for branch "alice:fix" in alice/project; pass a number, URL or branch`}, {"carol:dup", 21, ""}, + {"fix#12", 0, `no open pull request for branch "fix#12" in alice/project`}, {"dup", 0, "several open pull requests"}, {serverURL + "/alice/project", 0, "is not a pull request URL"}, } {