mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Show push URLs for remotes that have any (#5576)
In the Remotes panel, when selecting a remote that has push URLs, show them in the main view in addition to the fetch URL(s). Addresses half of #5566 (just the showing part, not the editing).
This commit is contained in:
commit
c6d73ace9a
|
|
@ -69,7 +69,7 @@ func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
|
|||
|
||||
func (self *RemoteLoader) getRemotesFromConfig() []*models.Remote {
|
||||
cmdArgs := NewGitCmd("config").
|
||||
Arg("--local", "--get-regexp", `^remote\.[^.]+\.url$`).ToArgv()
|
||||
Arg("--local", "--get-regexp", `^remote\.[^.]+\.(url|pushurl)$`).ToArgv()
|
||||
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
// exit code 1 means no matching keys (no remotes configured)
|
||||
|
|
@ -83,12 +83,29 @@ func (self *RemoteLoader) getRemotesFromConfig() []*models.Remote {
|
|||
if !found {
|
||||
continue
|
||||
}
|
||||
// key is "remote.<name>.url"; strip prefix and suffix to get the name
|
||||
remoteName := strings.TrimSuffix(strings.TrimPrefix(key, "remote."), ".url")
|
||||
// key is "remote.<name>.url" or "remote.<name>.pushurl";
|
||||
// strip prefix and suffix to get the name
|
||||
rest, ok := strings.CutPrefix(key, "remote.")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
var remoteName string
|
||||
var isPushUrl bool
|
||||
if name, ok := strings.CutSuffix(rest, ".pushurl"); ok {
|
||||
remoteName, isPushUrl = name, true
|
||||
} else if name, ok := strings.CutSuffix(rest, ".url"); ok {
|
||||
remoteName, isPushUrl = name, false
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
if _, ok := remotesByName[remoteName]; !ok {
|
||||
remotesByName[remoteName] = &models.Remote{Name: remoteName}
|
||||
}
|
||||
remotesByName[remoteName].Urls = append(remotesByName[remoteName].Urls, url)
|
||||
if isPushUrl {
|
||||
remotesByName[remoteName].PushUrls = append(remotesByName[remoteName].PushUrls, url)
|
||||
} else {
|
||||
remotesByName[remoteName].Urls = append(remotesByName[remoteName].Urls, url)
|
||||
}
|
||||
}
|
||||
|
||||
return slices.Collect(maps.Values(remotesByName))
|
||||
|
|
|
|||
108
pkg/commands/git_commands/remote_loader_test.go
Normal file
108
pkg/commands/git_commands/remote_loader_test.go
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
package git_commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetRemotesFromConfig(t *testing.T) {
|
||||
configArgs := []string{"config", "--local", "--get-regexp", `^remote\.[^.]+\.(url|pushurl)$`}
|
||||
|
||||
scenarios := []struct {
|
||||
testName string
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
expectedRemotes []*models.Remote
|
||||
}{
|
||||
{
|
||||
testName: "no remotes configured",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs(configArgs, "", errors.New("exit status 1")),
|
||||
expectedRemotes: nil,
|
||||
},
|
||||
{
|
||||
testName: "single remote with one url",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs(configArgs,
|
||||
"remote.origin.url https://github.com/foo/bar.git\n",
|
||||
nil),
|
||||
expectedRemotes: []*models.Remote{
|
||||
{Name: "origin", Urls: []string{"https://github.com/foo/bar.git"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "mirror remote with multiple urls",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs(configArgs,
|
||||
"remote.origin.url https://github.com/foo/bar.git\n"+
|
||||
"remote.origin.url git@github.com:foo/bar.git\n",
|
||||
nil),
|
||||
expectedRemotes: []*models.Remote{
|
||||
{Name: "origin", Urls: []string{
|
||||
"https://github.com/foo/bar.git",
|
||||
"git@github.com:foo/bar.git",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "remote with both url and pushurl",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs(configArgs,
|
||||
"remote.origin.url https://github.com/foo/bar.git\n"+
|
||||
"remote.origin.pushurl git@github.com:foo/bar.git\n",
|
||||
nil),
|
||||
expectedRemotes: []*models.Remote{
|
||||
{
|
||||
Name: "origin",
|
||||
Urls: []string{"https://github.com/foo/bar.git"},
|
||||
PushUrls: []string{"git@github.com:foo/bar.git"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "multiple remotes",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs(configArgs,
|
||||
"remote.origin.url https://github.com/foo/bar.git\n"+
|
||||
"remote.upstream.url https://github.com/baz/bar.git\n"+
|
||||
"remote.upstream.pushurl git@github.com:baz/bar.git\n",
|
||||
nil),
|
||||
expectedRemotes: []*models.Remote{
|
||||
{Name: "origin", Urls: []string{"https://github.com/foo/bar.git"}},
|
||||
{
|
||||
Name: "upstream",
|
||||
Urls: []string{"https://github.com/baz/bar.git"},
|
||||
PushUrls: []string{"git@github.com:baz/bar.git"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "remote name containing dots is preserved",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectGitArgs(configArgs,
|
||||
"remote.my.fork.url https://github.com/foo/bar.git\n",
|
||||
nil),
|
||||
expectedRemotes: []*models.Remote{
|
||||
{Name: "my.fork", Urls: []string{"https://github.com/foo/bar.git"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.testName, func(t *testing.T) {
|
||||
loader := &RemoteLoader{
|
||||
Common: common.NewDummyCommon(),
|
||||
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
||||
}
|
||||
|
||||
// map iteration order is non-deterministic, so compare unordered
|
||||
assert.ElementsMatch(t, scenario.expectedRemotes, loader.getRemotesFromConfig())
|
||||
|
||||
scenario.runner.CheckForMissingCalls()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,11 @@ package models
|
|||
|
||||
// Remote : A git remote
|
||||
type Remote struct {
|
||||
Name string
|
||||
Urls []string
|
||||
Name string
|
||||
Urls []string
|
||||
// PushUrls is empty unless the remote has explicit `remote.<name>.pushurl`
|
||||
// entries; when empty, pushes go to Urls.
|
||||
PushUrls []string
|
||||
Branches []*RemoteBranch
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,11 @@ func (self *RemotesController) GetOnRenderToMain() func() {
|
|||
if remote == nil {
|
||||
task = types.NewRenderStringTask("No remotes")
|
||||
} else {
|
||||
task = types.NewRenderStringTask(fmt.Sprintf("%s\nUrls:\n%s", style.FgGreen.Sprint(remote.Name), strings.Join(remote.Urls, "\n")))
|
||||
content := fmt.Sprintf("%s\nUrls:\n%s", style.FgGreen.Sprint(remote.Name), strings.Join(remote.Urls, "\n"))
|
||||
if len(remote.PushUrls) > 0 {
|
||||
content += fmt.Sprintf("\nPush Urls:\n%s", strings.Join(remote.PushUrls, "\n"))
|
||||
}
|
||||
task = types.NewRenderStringTask(content)
|
||||
}
|
||||
|
||||
self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ type RemoteBranch struct {
|
|||
type Remote struct {
|
||||
Name string
|
||||
Urls []string
|
||||
PushUrls []string
|
||||
Branches []*RemoteBranch
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,8 +116,9 @@ func remoteShimFromModelRemote(remote *models.Remote) *Remote {
|
|||
}
|
||||
|
||||
return &Remote{
|
||||
Name: remote.Name,
|
||||
Urls: remote.Urls,
|
||||
Name: remote.Name,
|
||||
Urls: remote.Urls,
|
||||
PushUrls: remote.PushUrls,
|
||||
Branches: lo.Map(remote.Branches, func(branch *models.RemoteBranch, _ int) *RemoteBranch {
|
||||
return remoteBranchShimFromModelRemoteBranch(branch)
|
||||
}),
|
||||
|
|
|
|||
Loading…
Reference in a new issue