Find push remote using branch.<name>.pushRemote and remote.pushDefault

When using a push.default = current triangular workflow, apart from
using @{push} to determine the remote branch name, we should also follow
the

1. branch.<name>.pushRemote
2. remote.pushDefault
3. branch.<name>.remote

...list to determine which remote Git pushes to.
This commit is contained in:
Frederick Zhang 2024-06-14 19:00:31 +10:00
parent 7fc35fd47d
commit 4254818dbd
No known key found for this signature in database
GPG key ID: 980A192C361BE1AE
8 changed files with 215 additions and 37 deletions

View file

@ -379,7 +379,7 @@ func (c *Client) lookupCommit(ctx context.Context, sha, format string) ([]byte,
// ReadBranchConfig parses the `branch.BRANCH.(remote|merge|gh-merge-base)` part of git config.
func (c *Client) ReadBranchConfig(ctx context.Context, branch string) (cfg BranchConfig) {
prefix := regexp.QuoteMeta(fmt.Sprintf("branch.%s.", branch))
args := []string{"config", "--get-regexp", fmt.Sprintf("^%s(remote|merge|%s)$", prefix, MergeBaseConfig)}
args := []string{"config", "--get-regexp", fmt.Sprintf("^%s(remote|merge|pushremote|%s)$", prefix, MergeBaseConfig)}
cmd, err := c.Command(ctx, args...)
if err != nil {
return
@ -397,21 +397,22 @@ func (c *Client) ReadBranchConfig(ctx context.Context, branch string) (cfg Branc
keys := strings.Split(parts[0], ".")
switch keys[len(keys)-1] {
case "remote":
if strings.Contains(parts[1], ":") {
u, err := ParseURL(parts[1])
if err != nil {
continue
}
cfg.RemoteURL = u
} else if !isFilesystemPath(parts[1]) {
cfg.RemoteName = parts[1]
}
parseRemoteURLOrName(parts[1], &cfg.RemoteURL, &cfg.RemoteName)
case "pushremote":
parseRemoteURLOrName(parts[1], &cfg.PushRemoteURL, &cfg.PushRemoteName)
case "merge":
cfg.MergeRef = parts[1]
case MergeBaseConfig:
cfg.MergeBase = parts[1]
}
}
if cfg.PushRemoteURL == nil && cfg.PushRemoteName == "" {
if conf, err := c.Config(ctx, "remote.pushDefault"); err == nil && conf != "" {
parseRemoteURLOrName(conf, &cfg.PushRemoteURL, &cfg.PushRemoteName)
} else {
cfg.PushRemoteName = cfg.RemoteName
}
}
if out, err = c.revParse(ctx, "--verify", "--quiet", "--abbrev-ref", branch+"@{push}"); err == nil {
cfg.Push = strings.TrimSuffix(string(out), "\n")
}
@ -776,6 +777,16 @@ func parseRemotes(remotesStr []string) RemoteSet {
return remotes
}
func parseRemoteURLOrName(value string, remoteURL **url.URL, remoteName *string) {
if strings.Contains(value, ":") {
if u, err := ParseURL(value); err == nil {
*remoteURL = u
}
} else if !isFilesystemPath(value) {
*remoteName = value
}
}
func populateResolvedRemotes(remotes RemoteSet, resolved []string) {
for _, l := range resolved {
parts := strings.SplitN(l, " ", 2)

View file

@ -725,31 +725,160 @@ func TestClientCommitBody(t *testing.T) {
}
func TestClientReadBranchConfig(t *testing.T) {
type cmdTest struct {
exitStatus int
stdOut string
stdErr string
wantArgs string
}
tests := []struct {
name string
cmdExitStatus []int
cmdStdout []string
cmdStderr []string
wantCmdArgs []string
cmds []cmdTest
wantBranchConfig BranchConfig
}{
{
name: "read branch config",
cmdExitStatus: []int{0, 0},
cmdStdout: []string{"branch.trunk.remote origin\nbranch.trunk.merge refs/heads/trunk\nbranch.trunk.gh-merge-base trunk", "origin/trunk"},
cmdStderr: []string{"", ""},
wantCmdArgs: []string{`path/to/git config --get-regexp ^branch\.trunk\.(remote|merge|gh-merge-base)$`, `path/to/git rev-parse --verify --quiet --abbrev-ref trunk@{push}`},
wantBranchConfig: BranchConfig{RemoteName: "origin", MergeRef: "refs/heads/trunk", MergeBase: "trunk", Push: "origin/trunk"},
name: "read branch config, central",
cmds: []cmdTest{
{
stdOut: "branch.trunk.remote origin\nbranch.trunk.merge refs/heads/trunk\nbranch.trunk.gh-merge-base trunk",
wantArgs: `path/to/git config --get-regexp ^branch\.trunk\.(remote|merge|pushremote|gh-merge-base)$`,
},
{
wantArgs: `path/to/git config remote.pushDefault`,
},
{
stdOut: "origin/trunk",
wantArgs: `path/to/git rev-parse --verify --quiet --abbrev-ref trunk@{push}`,
},
},
wantBranchConfig: BranchConfig{RemoteName: "origin", MergeRef: "refs/heads/trunk", MergeBase: "trunk", PushRemoteName: "origin", Push: "origin/trunk"},
},
{
name: "read branch config, central, push.default = upstream",
cmds: []cmdTest{
{
stdOut: "branch.trunk.remote origin\nbranch.trunk.merge refs/heads/trunk-remote",
wantArgs: `path/to/git config --get-regexp ^branch\.trunk\.(remote|merge|pushremote|gh-merge-base)$`,
},
{
wantArgs: `path/to/git config remote.pushDefault`,
},
{
stdOut: "origin/trunk-remote",
wantArgs: `path/to/git rev-parse --verify --quiet --abbrev-ref trunk@{push}`,
},
},
wantBranchConfig: BranchConfig{RemoteName: "origin", MergeRef: "refs/heads/trunk-remote", PushRemoteName: "origin", Push: "origin/trunk-remote"},
},
{
name: "read branch config, central, push.default = current",
cmds: []cmdTest{
{
stdOut: "branch.trunk.remote origin\nbranch.trunk.merge refs/heads/main",
wantArgs: `path/to/git config --get-regexp ^branch\.trunk\.(remote|merge|pushremote|gh-merge-base)$`,
},
{
wantArgs: `path/to/git config remote.pushDefault`,
},
{
stdOut: "origin/trunk",
wantArgs: `path/to/git rev-parse --verify --quiet --abbrev-ref trunk@{push}`,
},
},
wantBranchConfig: BranchConfig{RemoteName: "origin", MergeRef: "refs/heads/main", PushRemoteName: "origin", Push: "origin/trunk"},
},
{
name: "read branch config, central, push.default = current, target branch not pushed, no existing remote branch",
cmds: []cmdTest{
{
stdOut: "branch.trunk.remote .\nbranch.trunk.merge refs/heads/trunk-middle",
wantArgs: `path/to/git config --get-regexp ^branch\.trunk\.(remote|merge|pushremote|gh-merge-base)$`,
},
{
stdOut: "origin",
wantArgs: `path/to/git config remote.pushDefault`,
},
{
exitStatus: 1,
wantArgs: `path/to/git rev-parse --verify --quiet --abbrev-ref trunk@{push}`,
},
},
wantBranchConfig: BranchConfig{MergeRef: "refs/heads/trunk-middle", PushRemoteName: "origin"},
},
{
name: "read branch config, triangular, push.default = current, has existing remote branch, branch.trunk.pushremote effective",
cmds: []cmdTest{
{
stdOut: "branch.trunk.remote upstream\nbranch.trunk.merge refs/heads/main\nbranch.trunk.pushremote origin",
wantArgs: `path/to/git config --get-regexp ^branch\.trunk\.(remote|merge|pushremote|gh-merge-base)$`,
},
{
stdOut: "origin/trunk",
wantArgs: `path/to/git rev-parse --verify --quiet --abbrev-ref trunk@{push}`,
},
},
wantBranchConfig: BranchConfig{RemoteName: "upstream", MergeRef: "refs/heads/main", PushRemoteName: "origin", Push: "origin/trunk"},
},
{
name: "read branch config, triangular, push.default = current, has existing remote branch, remote.pushDefault effective",
cmds: []cmdTest{
{
stdOut: "branch.trunk.remote upstream\nbranch.trunk.merge refs/heads/main",
wantArgs: `path/to/git config --get-regexp ^branch\.trunk\.(remote|merge|pushremote|gh-merge-base)$`,
},
{
stdOut: "origin",
wantArgs: `path/to/git config remote.pushDefault`,
},
{
stdOut: "origin/trunk",
wantArgs: `path/to/git rev-parse --verify --quiet --abbrev-ref trunk@{push}`,
},
},
wantBranchConfig: BranchConfig{RemoteName: "upstream", MergeRef: "refs/heads/main", PushRemoteName: "origin", Push: "origin/trunk"},
},
{
name: "read branch config, triangular, push.default = current, no existing remote branch, branch.trunk.pushremote effective",
cmds: []cmdTest{
{
stdOut: "branch.trunk.remote upstream\nbranch.trunk.merge refs/heads/main\nbranch.trunk.pushremote origin",
wantArgs: `path/to/git config --get-regexp ^branch\.trunk\.(remote|merge|pushremote|gh-merge-base)$`,
},
{
exitStatus: 1,
wantArgs: `path/to/git rev-parse --verify --quiet --abbrev-ref trunk@{push}`,
},
},
wantBranchConfig: BranchConfig{RemoteName: "upstream", MergeRef: "refs/heads/main", PushRemoteName: "origin"},
},
{
name: "read branch config, triangular, push.default = current, no existing remote branch, remote.pushDefault effective",
cmds: []cmdTest{
{
stdOut: "branch.trunk.remote upstream\nbranch.trunk.merge refs/heads/main",
wantArgs: `path/to/git config --get-regexp ^branch\.trunk\.(remote|merge|pushremote|gh-merge-base)$`,
},
{
stdOut: "origin",
wantArgs: `path/to/git config remote.pushDefault`,
},
{
exitStatus: 1,
wantArgs: `path/to/git rev-parse --verify --quiet --abbrev-ref trunk@{push}`,
},
},
wantBranchConfig: BranchConfig{RemoteName: "upstream", MergeRef: "refs/heads/main", PushRemoteName: "origin"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var cmds []*exec.Cmd
var cmdCtxs []commandCtx
for i := 0; i < len(tt.cmdExitStatus); i++ {
cmd, cmdCtx := createCommandContext(t, tt.cmdExitStatus[i], tt.cmdStdout[i], tt.cmdStderr[i])
for _, c := range tt.cmds {
cmd, cmdCtx := createCommandContext(t, c.exitStatus, c.stdOut, c.stdErr)
cmds = append(cmds, cmd)
cmdCtxs = append(cmdCtxs, cmdCtx)
}
i := -1
client := Client{
@ -761,8 +890,8 @@ func TestClientReadBranchConfig(t *testing.T) {
},
}
branchConfig := client.ReadBranchConfig(context.Background(), "trunk")
for i := 0; i < len(tt.cmdExitStatus); i++ {
assert.Equal(t, tt.wantCmdArgs[i], strings.Join(cmds[i].Args[3:], " "))
for i := 0; i < len(tt.cmds); i++ {
assert.Equal(t, tt.cmds[i].wantArgs, strings.Join(cmds[i].Args[3:], " "))
}
assert.Equal(t, tt.wantBranchConfig, branchConfig)
})

View file

@ -64,7 +64,9 @@ type BranchConfig struct {
RemoteName string
RemoteURL *url.URL
// MergeBase is the optional base branch to target in a new PR if `--base` is not specified.
MergeBase string
MergeRef string
Push string
MergeBase string
MergeRef string
PushRemoteURL *url.URL
PushRemoteName string
Push string
}

View file

@ -354,6 +354,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "")
},
@ -385,6 +386,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
},
expectedOut: "https://github.com/OWNER/REPO/pull/12\n",
@ -402,6 +404,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
},
expectedOutputs: []string{
@ -436,6 +439,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
},
httpStubs: func(reg *httpmock.Registry, t *testing.T) {
@ -499,6 +503,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
},
expectedOutputs: []string{
@ -539,6 +544,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
},
httpStubs: func(reg *httpmock.Registry, t *testing.T) {
@ -608,6 +614,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
},
expectedOut: heredoc.Doc(`
@ -656,6 +663,7 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register(`git push --set-upstream origin HEAD:refs/heads/feature`, 0, "")
@ -720,6 +728,7 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register(`git push --set-upstream origin HEAD:refs/heads/feature`, 0, "")
@ -767,6 +776,7 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register(`git push --set-upstream origin HEAD:refs/heads/feature`, 0, "")
@ -817,6 +827,7 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register("git remote rename origin upstream", 0, "")
@ -876,6 +887,7 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register("git show-ref --verify", 0, heredoc.Doc(`
deadbeef HEAD
@ -914,6 +926,7 @@ func Test_createRun(t *testing.T) {
branch.feature.remote origin
branch.feature.merge refs/heads/my-feat2
`)) // determineTrackingBranch
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 0, "origin/my-feat2")
cs.Register("git show-ref --verify", 0, heredoc.Doc(`
deadbeef HEAD
@ -955,6 +968,7 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "d3476a1\u0000commit 0\u0000\u0000\n7a6ea13\u0000commit 1\u0000\u0000")
},
@ -997,6 +1011,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
},
httpStubs: func(reg *httpmock.Registry, t *testing.T) {
@ -1087,6 +1102,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
},
wantErr: "a pull request for branch \"feature\" into branch \"master\" already exists:\nhttps://github.com/OWNER/REPO/pull/123",
@ -1105,6 +1121,7 @@ func Test_createRun(t *testing.T) {
httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "")
@ -1138,6 +1155,7 @@ func Test_createRun(t *testing.T) {
mockRetrieveProjects(t, reg)
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "")
cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "")
@ -1186,6 +1204,7 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git -c log.ShowSignature=false log --pretty=format:%H%x00%s%x00%b%x00 --cherry origin/master...feature`, 0, "")
cs.Register(`git rev-parse --show-toplevel`, 0, "")
@ -1246,6 +1265,7 @@ func Test_createRun(t *testing.T) {
}))
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "")
},
@ -1295,6 +1315,7 @@ func Test_createRun(t *testing.T) {
{
name: "web long URL",
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "")
},
@ -1323,6 +1344,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
},
httpStubs: func(reg *httpmock.Registry, t *testing.T) {
@ -1344,6 +1366,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(
"git -c log.ShowSignature=false log --pretty=format:%H%x00%s%x00%b%x00 --cherry origin/master...feature",
@ -1420,6 +1443,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(
"git -c log.ShowSignature=false log --pretty=format:%H%x00%s%x00%b%x00 --cherry origin/master...feature",
@ -1457,6 +1481,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(
"git -c log.ShowSignature=false log --pretty=format:%H%x00%s%x00%b%x00 --cherry origin/master...feature",
@ -1508,6 +1533,7 @@ func Test_createRun(t *testing.T) {
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register("git -c log.ShowSignature=false log --pretty=format:%H%x00%s%x00%b%x00 --cherry origin/master...feature", 0, "")
},
@ -1560,7 +1586,7 @@ func Test_createRun(t *testing.T) {
},
customBranchConfig: true,
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp \^branch\\\.task1\\\.\(remote\|merge\|gh-merge-base\)\$`, 0, heredoc.Doc(`
cs.Register(`git config --get-regexp \^branch\\\.task1\\\.\(remote\|merge\|pushremote\|gh-merge-base\)\$`, 0, heredoc.Doc(`
branch.task1.remote origin
branch.task1.merge refs/heads/task1
branch.task1.gh-merge-base feature/feat2`)) // ReadBranchConfig
@ -1568,6 +1594,7 @@ func Test_createRun(t *testing.T) {
deadbeef HEAD
deadb00f refs/remotes/upstream/feature/feat2
deadbeef refs/remotes/origin/task1`)) // determineTrackingBranch
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref task1@\{push\}`, 1, "")
},
expectedOut: "https://github.com/OWNER/REPO/pull/12\n",
@ -1595,7 +1622,7 @@ func Test_createRun(t *testing.T) {
defer cmdTeardown(t)
cs.Register(`git status --porcelain`, 0, "")
if !tt.customBranchConfig {
cs.Register(`git config --get-regexp \^branch\\\..+\\\.\(remote\|merge\|gh-merge-base\)\$`, 0, "")
cs.Register(`git config --get-regexp \^branch\\\..+\\\.\(remote\|merge\|pushremote\|gh-merge-base\)\$`, 0, "")
}
if tt.cmdStubs != nil {
@ -1679,6 +1706,7 @@ func Test_tryDetermineTrackingRef(t *testing.T) {
name: "empty",
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "")
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register(`git show-ref --verify -- HEAD`, 0, "abc HEAD")
},
@ -1689,6 +1717,7 @@ func Test_tryDetermineTrackingRef(t *testing.T) {
name: "no match",
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "")
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 1, "")
cs.Register("git show-ref --verify -- HEAD refs/remotes/upstream/feature refs/remotes/origin/feature", 0, "abc HEAD\nbca refs/remotes/upstream/feature")
},
@ -1709,6 +1738,7 @@ func Test_tryDetermineTrackingRef(t *testing.T) {
name: "match",
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "")
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 0, "origin/feature")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/upstream/feature refs/remotes/origin/feature$`, 0, heredoc.Doc(`
deadbeef HEAD
@ -1739,6 +1769,7 @@ func Test_tryDetermineTrackingRef(t *testing.T) {
branch.feature.remote origin
branch.feature.merge refs/heads/great-feat
`))
cs.Register(`git config remote.pushDefault`, 1, "")
cs.Register(`git rev-parse --verify --quiet --abbrev-ref feature@\{push\}`, 0, "origin/great-feat")
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/great-feat refs/remotes/origin/feature$`, 0, heredoc.Doc(`
deadbeef HEAD

View file

@ -251,22 +251,21 @@ func (f *finder) parseCurrentBranch() (string, int, error) {
}
var gitRemoteRepo ghrepo.Interface
if branchConfig.RemoteURL != nil {
if branchConfig.PushRemoteURL != nil {
// the branch merges from a remote specified by URL
if r, err := ghrepo.FromURL(branchConfig.RemoteURL); err == nil {
gitRemoteRepo = r
}
} else if branchConfig.RemoteName != "" {
// the branch merges from a remote specified by name
} else if branchConfig.PushRemoteName != "" {
rem, _ := f.remotesFn()
if r, err := rem.FindByName(branchConfig.RemoteName); err == nil {
if r, err := rem.FindByName(branchConfig.PushRemoteName); err == nil {
gitRemoteRepo = r
}
}
if gitRemoteRepo != nil {
if branchConfig.Push != "" {
prHeadRef = strings.TrimPrefix(branchConfig.Push, branchConfig.RemoteName+"/")
prHeadRef = strings.TrimPrefix(branchConfig.Push, branchConfig.PushRemoteName+"/")
} else if pushDefault, _ := f.pushDefault(); (pushDefault == "upstream" || pushDefault == "tracking") &&
strings.HasPrefix(branchConfig.MergeRef, "refs/heads/") {
prHeadRef = strings.TrimPrefix(branchConfig.MergeRef, "refs/heads/")

View file

@ -328,6 +328,7 @@ func TestFind(t *testing.T) {
branchConfig: func(branch string) (c git.BranchConfig) {
c.MergeRef = "refs/heads/blue-upstream-berries"
c.RemoteName = "origin"
c.PushRemoteName = "origin"
c.Push = "origin/blue-upstream-berries"
return
},
@ -373,6 +374,7 @@ func TestFind(t *testing.T) {
u, _ := url.Parse("https://github.com/UPSTREAMOWNER/REPO")
c.MergeRef = "refs/heads/blue-upstream-berries"
c.RemoteURL = u
c.PushRemoteURL = u
return
},
pushDefault: func() (string, error) { return "upstream", nil },
@ -411,6 +413,7 @@ func TestFind(t *testing.T) {
branchConfig: func(branch string) (c git.BranchConfig) {
c.MergeRef = "refs/heads/blue-upstream-berries"
c.RemoteName = "origin"
c.PushRemoteName = "origin"
c.Push = "origin/blue-upstream-berries"
return
},
@ -454,6 +457,7 @@ func TestFind(t *testing.T) {
},
branchConfig: func(branch string) (c git.BranchConfig) {
c.RemoteName = "origin"
c.PushRemoteName = "origin"
c.Push = "origin/blueberries"
return
},

View file

@ -201,16 +201,16 @@ func prSelectorForCurrentBranch(gitClient *git.Client, baseRepo ghrepo.Interface
if r, err := ghrepo.FromURL(branchConfig.RemoteURL); err == nil {
branchOwner = r.RepoOwner()
}
} else if branchConfig.RemoteName != "" {
} else if branchConfig.PushRemoteName != "" {
// the branch merges from a remote specified by name
if r, err := rem.FindByName(branchConfig.RemoteName); err == nil {
if r, err := rem.FindByName(branchConfig.PushRemoteName); err == nil {
branchOwner = r.RepoOwner()
}
}
if branchOwner != "" {
if branchConfig.Push != "" {
selector = strings.TrimPrefix(branchConfig.Push, branchConfig.RemoteName+"/")
selector = strings.TrimPrefix(branchConfig.Push, branchConfig.PushRemoteName+"/")
} else if pushDefault, _ := gitClient.Config(context.Background(), "push.default"); (pushDefault == "upstream" || pushDefault == "tracking") &&
strings.HasPrefix(branchConfig.MergeRef, "refs/heads/") {
selector = strings.TrimPrefix(branchConfig.MergeRef, "refs/heads/")

View file

@ -383,6 +383,7 @@ func Test_prSelectorForCurrentBranchPushDefaultUpstream(t *testing.T) {
branch.Frederick888/main.remote git@github.com:Frederick888/playground.git
branch.Frederick888/main.merge refs/heads/main
`))
rs.Register(`git config remote.pushDefault`, 1, "")
rs.Register(`git rev-parse --verify --quiet --abbrev-ref Frederick888/main@\{push\}`, 1, "")
rs.Register(`git config push\.default`, 0, "upstream")
@ -414,6 +415,7 @@ func Test_prSelectorForCurrentBranchPushDefaultTracking(t *testing.T) {
branch.Frederick888/main.remote git@github.com:Frederick888/playground.git
branch.Frederick888/main.merge refs/heads/main
`))
rs.Register(`git config remote.pushDefault`, 1, "")
rs.Register(`git rev-parse --verify --quiet --abbrev-ref Frederick888/main@\{push\}`, 1, "")
rs.Register(`git config push\.default`, 0, "tracking")