diff --git a/docs/keybindings/Keybindings_pl.md b/docs/keybindings/Keybindings_pl.md index 95e713826..e97e1dd88 100644 --- a/docs/keybindings/Keybindings_pl.md +++ b/docs/keybindings/Keybindings_pl.md @@ -43,8 +43,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
space: przełącz
- o: utwórz żądanie pobrania
- O: utwórz opcje żądania ściągnięcia
+ o: maak of laat een pull-request zien
+ O: utwórz opcje żądania
ctrl+y: skopiuj adres URL żądania pobrania do schowka
c: przełącz używając nazwy
F: wymuś przełączenie
diff --git a/docs/keybindings/Keybindings_zh.md b/docs/keybindings/Keybindings_zh.md
index 42a4fd1c7..6c8a35c5b 100644
--- a/docs/keybindings/Keybindings_zh.md
+++ b/docs/keybindings/Keybindings_zh.md
@@ -44,7 +44,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
space: 检出
o: 创建抓取请求
- O: 创建抓取请求选项
+ O: 创建抓取请求
ctrl+y: 将抓取请求 URL 复制到剪贴板
c: 按名称检出
F: 强制检出
diff --git a/pkg/app/app.go b/pkg/app/app.go
index cb3220cee..680a62049 100644
--- a/pkg/app/app.go
+++ b/pkg/app/app.go
@@ -150,7 +150,7 @@ func NewApp(config config.AppConfigurer, filterPath string) (*App, error) {
}
func (app *App) validateGhVersion() error {
- output, err := app.OSCommand.RunCommandWithOutput("gh --version")
+ output, err := app.OSCommand.Cmd.New("gh --version").RunWithOutput()
// if we get an error anywhere here we'll show the same status
minVersionError := errors.New(app.Tr.MinGhVersionError)
if err != nil {
diff --git a/pkg/commands/git.go b/pkg/commands/git.go
index f6812e254..31ba2f42a 100644
--- a/pkg/commands/git.go
+++ b/pkg/commands/git.go
@@ -37,6 +37,7 @@ type GitCommand struct {
Tag *git_commands.TagCommands
WorkingTree *git_commands.WorkingTreeCommands
Bisect *git_commands.BisectCommands
+ Gh *git_commands.GhCommands
Loaders Loaders
}
@@ -115,6 +116,7 @@ func NewGitCommandAux(
patchManager := patch.NewPatchManager(cmn.Log, workingTreeCommands.ApplyPatch, workingTreeCommands.ShowFileDiff)
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchManager)
bisectCommands := git_commands.NewBisectCommands(gitCommon)
+ ghCommands := git_commands.NewGhCommand(gitCommon)
return &GitCommand{
Branch: branchCommands,
@@ -133,6 +135,7 @@ func NewGitCommandAux(
Tag: tagCommands,
Bisect: bisectCommands,
WorkingTree: workingTreeCommands,
+ Gh: ghCommands,
Loaders: Loaders{
Branches: loaders.NewBranchLoader(cmn, branchCommands.GetRawBranches, branchCommands.CurrentBranchName, configCommands),
CommitFiles: loaders.NewCommitFileLoader(cmn, cmd),
diff --git a/pkg/commands/git_commands/gh.go b/pkg/commands/git_commands/gh.go
new file mode 100644
index 000000000..30d580aa4
--- /dev/null
+++ b/pkg/commands/git_commands/gh.go
@@ -0,0 +1,128 @@
+package git_commands
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
+ "github.com/jesseduffield/lazygit/pkg/commands/models"
+)
+
+type GhCommands struct {
+ *GitCommon
+}
+
+func NewGhCommand(gitCommon *GitCommon) *GhCommands {
+ return &GhCommands{
+ GitCommon: gitCommon,
+ }
+}
+
+// https://github.com/cli/cli/issues/2300
+func (self *GhCommands) BaseRepo() error {
+ return self.cmd.New("git config --local --get-regexp .gh-resolved").StreamOutput().Run()
+
+}
+
+// Ex: git config --local --add "remote.origin.gh-resolved" "jesseduffield/lazygit"
+func (self *GhCommands) SetBaseRepo(repository string) (string, error) {
+ return self.cmd.NewShell(fmt.Sprintf("git config --local --add \"remote.origin.gh-resolved\" \"%s\"", repository)).RunWithOutput()
+}
+
+func (self *GhCommands) prList() (string, error) {
+ return self.cmd.NewShell("gh pr list --limit 100 --state all --json state,url,number,headRefName,headRepositoryOwner").RunWithOutput()
+}
+
+func (self *GhCommands) GithubMostRecentPRs() ([]*models.GithubPullRequest, error) {
+ commandOutput, err := self.prList()
+ if err != nil {
+ return nil, err
+ }
+
+ prs := []*models.GithubPullRequest{}
+ err = json.Unmarshal([]byte(commandOutput), &prs)
+ if err != nil {
+ return nil, err
+ }
+
+ return prs, nil
+}
+
+func GenerateGithubPullRequestMap(prs []*models.GithubPullRequest, branches []*models.Branch, remotes []*models.Remote) (map[*models.Branch]*models.GithubPullRequest, error) {
+ res := map[*models.Branch]*models.GithubPullRequest{}
+
+ if len(prs) == 0 {
+ return res, nil
+ }
+
+ remotesToOwnersMap, err := getRemotesToOwnersMap(remotes)
+
+ if len(remotesToOwnersMap) == 0 {
+ return res, err
+ }
+
+ prWithStringKey := map[string]models.GithubPullRequest{}
+
+ for _, pr := range prs {
+ prWithStringKey[pr.UserName()+":"+pr.BranchName()] = *pr
+ }
+
+ for _, branch := range branches {
+ if !branch.IsTrackingRemote() || branch.UpstreamBranch == "" {
+ continue
+ }
+
+ owner, foundRemoteOwner := remotesToOwnersMap[branch.UpstreamRemote]
+ if !foundRemoteOwner {
+ continue
+ }
+
+ pr, hasPr := prWithStringKey[owner+":"+branch.UpstreamBranch]
+
+ if !hasPr {
+ continue
+ }
+
+ res[branch] = &pr
+ }
+
+ return res, nil
+}
+
+func GetRepoInfoFromURL(url string) hosting_service.RepoInformation {
+ isHTTP := strings.HasPrefix(url, "http")
+
+ if isHTTP {
+ splits := strings.Split(url, "/")
+ owner := strings.Join(splits[3:len(splits)-1], "/")
+ repo := strings.TrimSuffix(splits[len(splits)-1], ".git")
+
+ return hosting_service.RepoInformation{
+ Owner: owner,
+ Repository: repo,
+ }
+ }
+
+ tmpSplit := strings.Split(url, ":")
+ splits := strings.Split(tmpSplit[1], "/")
+ owner := strings.Join(splits[0:len(splits)-1], "/")
+ repo := strings.TrimSuffix(splits[len(splits)-1], ".git")
+
+ return hosting_service.RepoInformation{
+ Owner: owner,
+ Repository: repo,
+ }
+}
+
+func getRemotesToOwnersMap(remotes []*models.Remote) (map[string]string, error) {
+ res := map[string]string{}
+ for _, remote := range remotes {
+ if len(remote.Urls) == 0 {
+ continue
+ }
+
+ res[remote.Name] = GetRepoInfoFromURL(remote.Urls[0]).Owner
+ }
+ return res, nil
+}
diff --git a/pkg/commands/github.go b/pkg/commands/github.go
deleted file mode 100644
index 65489143f..000000000
--- a/pkg/commands/github.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package commands
-
-import (
- "encoding/json"
-
- "github.com/jesseduffield/lazygit/pkg/commands/models"
-)
-
-func (c *GitCommand) GithubMostRecentPRs() ([]*models.GithubPullRequest, error) {
- commandOutput, err := c.OSCommand.RunCommandWithOutput("gh pr list --limit 50 --state all --json state,url,number,headRefName,headRepositoryOwner")
- if err != nil {
- return nil, err
- }
-
- prs := []*models.GithubPullRequest{}
- err = json.Unmarshal([]byte(commandOutput), &prs)
- if err != nil {
- return nil, err
- }
-
- return prs, nil
-}
-
-func (c *GitCommand) GenerateGithubPullRequestMap(prs []*models.GithubPullRequest, branches []*models.Branch, remotes []*models.Remote) (map[*models.Branch]*models.GithubPullRequest, error) {
- res := map[*models.Branch]*models.GithubPullRequest{}
-
- if len(prs) == 0 {
- return res, nil
- }
-
- remotesToOwnersMap, err := c.GetRemotesToOwnersMap(remotes)
-
- if len(remotesToOwnersMap) == 0 {
- return res, err
- }
-
- prWithStringKey := map[string]models.GithubPullRequest{}
-
- for _, pr := range prs {
- prWithStringKey[pr.UserName()+":"+pr.BranchName()] = *pr
- }
-
- for _, branch := range branches {
- if !branch.IsTrackingRemote() || branch.BranchName() == "" {
- continue
- }
-
- owner, foundRemoteOwner := remotesToOwnersMap[branch.RemoteName()]
- if branch.BranchName() == "" || !foundRemoteOwner {
- continue
- }
-
- pr, hasPr := prWithStringKey[owner+":"+branch.BranchName()]
- if !hasPr {
- continue
- }
-
- res[branch] = &pr
- }
-
- return res, nil
-}
diff --git a/pkg/commands/github_test.go b/pkg/commands/github_test.go
deleted file mode 100644
index d8c09f970..000000000
--- a/pkg/commands/github_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package commands
-
-import (
- "os/exec"
- "testing"
-
- "github.com/jesseduffield/lazygit/pkg/commands/models"
- "github.com/jesseduffield/lazygit/pkg/secureexec"
- "github.com/stretchr/testify/assert"
-)
-
-func TestGithubMostRecentPRs(t *testing.T) {
- scenarios := []struct {
- testName string
- response string
- expect []*models.GithubPullRequest
- }{
- {
- "no response",
- "",
- nil,
- },
- {
- "error response",
- "none of the git remotes configured for this repository point to a known GitHub host. To tell gh about a new GitHub host, please use `gh auth login`",
- nil,
- },
- {
- "empty response",
- "[]",
- []*models.GithubPullRequest{},
- },
- {
- "response with data",
- `[{
- "headRefName": "command-log-2",
- "number": 1249,
- "state": "MERGED",
- "url": "https://github.com/jesseduffield/lazygit/pull/1249",
- "headRepositoryOwner": {
- "id": "MDQ6VXNlcjg0NTY2MzM=",
- "name": "Jesse Duffield",
- "login": "jesseduffield"
- }
- }]`,
- []*models.GithubPullRequest{{
- HeadRefName: "command-log-2",
- Number: 1249,
- State: "MERGED",
- Url: "https://github.com/jesseduffield/lazygit/pull/1249",
- HeadRepositoryOwner: models.GithubRepositoryOwner{ID: "MDQ6VXNlcjg0NTY2MzM=", Name: "Jesse Duffield", Login: "jesseduffield"},
- }},
- },
- }
-
- for _, s := range scenarios {
- t.Run(s.testName, func(t *testing.T) {
- gitCmd := NewDummyGitCommand()
- gitCmd.OSCommand.Command = func(cmd string, args ...string) *exec.Cmd {
- assert.EqualValues(t, "gh", cmd)
- assert.EqualValues(t, []string{"pr", "list", "--limit", "50", "--state", "all", "--json", "state,url,number,headRefName,headRepositoryOwner"}, args)
- return secureexec.Command("echo", s.response)
- }
-
- res, _ := gitCmd.GithubMostRecentPRs()
- assert.Equal(t, s.expect, res)
-
- })
- }
-}
diff --git a/pkg/commands/models/branch.go b/pkg/commands/models/branch.go
index 7bd109d97..3cdf5ad6d 100644
--- a/pkg/commands/models/branch.go
+++ b/pkg/commands/models/branch.go
@@ -1,7 +1,5 @@
package models
-import "strings"
-
// Branch : A git branch
// duplicating this for now
type Branch struct {
@@ -57,15 +55,3 @@ func (b *Branch) HasCommitsToPull() bool {
func (b *Branch) IsRealBranch() bool {
return b.Pushables != "" && b.Pullables != ""
}
-func (b *Branch) RemoteName() string {
- return strings.SplitN(b.UpstreamName, "/", 2)[0]
-}
-
-func (b *Branch) BranchName() string {
- remoteAndBranch := strings.SplitN(b.UpstreamName, "/", 2)
- if len(remoteAndBranch) != 2 {
- return ""
- }
-
- return strings.SplitN(b.UpstreamName, "/", 2)[1]
-}
diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go
index 5d03daba0..9efc73d90 100644
--- a/pkg/config/user_config.go
+++ b/pkg/config/user_config.go
@@ -71,7 +71,7 @@ type GitConfig struct {
OverrideGpg bool `yaml:"overrideGpg"`
DisableForcePushing bool `yaml:"disableForcePushing"`
CommitPrefixes map[string]CommitPrefixConfig `yaml:"commitPrefixes"`
- // this shoudl really be under 'gui', not 'git'
+ // this should really be under 'gui', not 'git'
ParseEmoji bool `yaml:"parseEmoji"`
Log LogConfig `yaml:"log"`
EnableGhCommand bool `yaml:"enableGhCommand"`
@@ -376,6 +376,7 @@ func GetDefaultConfig() *UserConfig {
CommitPrefixes: map[string]CommitPrefixConfig(nil),
ParseEmoji: false,
DiffContextSize: 3,
+ EnableGhCommand: false,
},
Refresher: RefresherConfig{
RefreshInterval: 10,
diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go
index c2c44f273..7e68fdb17 100644
--- a/pkg/gui/branches_panel.go
+++ b/pkg/gui/branches_panel.go
@@ -75,7 +75,7 @@ func (gui *Gui) refreshBranches() {
}
func (gui *Gui) refreshGithubPullRequests() {
- _, err := gui.GitCommand.RunCommandWithOutput("git config --local --get-regexp .gh-resolved$")
+ err := gui.Git.Gh.BaseRepo()
if err == nil {
_ = gui.setGithubPullRequests()
return
@@ -89,8 +89,7 @@ func (gui *Gui) refreshGithubPullRequests() {
findSuggestionsFunc: gui.getRemoteRepoSuggestionsFunc(),
handleConfirm: func(repository string) error {
return gui.WithWaitingStatus(gui.Tr.LcSelectingRemote, func() error {
- // ex git config --local --add "remote.origin.gh-resolved" "jesseduffield/lazygit"
- _, err := gui.GitCommand.RunCommandWithOutput(fmt.Sprintf("git config --local --add \"remote.origin.gh-resolved\" \"%s\"", repository))
+ _, err := gui.Git.Gh.SetBaseRepo(repository)
if err != nil {
return err
}
@@ -107,7 +106,8 @@ func (gui *Gui) refreshGithubPullRequests() {
}
func (gui *Gui) setGithubPullRequests() error {
- prs, err := gui.GitCommand.GithubMostRecentPRs()
+ prs, err := gui.Git.Gh.GithubMostRecentPRs()
+
if err != nil {
return gui.surfaceError(err)
}
diff --git a/pkg/gui/find_suggestions.go b/pkg/gui/find_suggestions.go
index 5864eb178..56a27ee94 100644
--- a/pkg/gui/find_suggestions.go
+++ b/pkg/gui/find_suggestions.go
@@ -4,7 +4,7 @@ import (
"fmt"
"os"
- "github.com/jesseduffield/lazygit/pkg/commands"
+ "github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
@@ -60,7 +60,7 @@ func (gui *Gui) getRemoteRepoNames() []string {
if len(remote.Urls) == 0 {
continue
}
- info := commands.GetRepoInfoFromURL(remote.Urls[0])
+ info := git_commands.GetRepoInfoFromURL(remote.Urls[0])
result = append(result, fmt.Sprintf("%s/%s", info.Owner, info.Repository))
}
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index ed41537c2..375f7c0d4 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -313,6 +313,7 @@ type guiState struct {
Tags []*models.Tag
MenuItems []*menuItem
BisectInfo *git_commands.BisectInfo
+ GithubState *GithubState
Updating bool
Panels *panelStates
@@ -789,20 +790,20 @@ func (gui *Gui) setColorScheme() error {
return nil
}
-// func (gui *Gui) GetPr(branch *models.Branch) (*models.GithubPullRequest, bool, error) {
-// prs, err := gui.GitCommand.GenerateGithubPullRequestMap(
-// gui.State.GithubState.RecentPRs,
-// []*models.Branch{branch},
-// gui.State.Remotes,
-// )
-// if err != nil {
-// return nil, false, err
-// }
+func (gui *Gui) GetPr(branch *models.Branch) (*models.GithubPullRequest, bool, error) {
+ prs, err := git_commands.GenerateGithubPullRequestMap(
+ gui.State.GithubState.RecentPRs,
+ []*models.Branch{branch},
+ gui.State.Remotes,
+ )
+ if err != nil {
+ return nil, false, err
+ }
-// pr, hasPr := prs[branch]
+ pr, hasPr := prs[branch]
-// return pr, hasPr, nil
-// }
+ return pr, hasPr, nil
+}
func (gui *Gui) OnUIThread(f func() error) {
gui.g.Update(func(*gocui.Gui) error {
diff --git a/pkg/gui/list_context_config.go b/pkg/gui/list_context_config.go
index 7d9fb7947..3fde1edfd 100644
--- a/pkg/gui/list_context_config.go
+++ b/pkg/gui/list_context_config.go
@@ -69,7 +69,7 @@ func (gui *Gui) branchesListContext() IListContext {
OnRenderToMain: OnFocusWrapper(gui.branchesRenderToMain),
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
- prs, err := gui.GitCommand.GenerateGithubPullRequestMap(gui.State.GithubState.RecentPRs, gui.State.Branches, gui.State.Remotes)
+ prs, err := git_commands.GenerateGithubPullRequestMap(gui.State.GithubState.RecentPRs, gui.State.Branches, gui.State.Remotes)
if err != nil {
panic(err)
}
diff --git a/pkg/gui/presentation/branches.go b/pkg/gui/presentation/branches.go
index 4dac7328e..1ace50fab 100644
--- a/pkg/gui/presentation/branches.go
+++ b/pkg/gui/presentation/branches.go
@@ -2,6 +2,7 @@ package presentation
import (
"fmt"
+ "strconv"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
@@ -12,7 +13,10 @@ import (
var branchPrefixColorCache = make(map[string]style.TextStyle)
-func GetBranchListDisplayStrings(branches []*models.Branch, fullDescription bool, diffName string) [][]string {
+func GetBranchListDisplayStrings(
+ branches []*models.Branch,
+ prs map[*models.Branch]*models.GithubPullRequest,
+ fullDescription bool, diffName string) [][]string {
lines := make([][]string, len(branches))
for i := range branches {
@@ -50,11 +54,11 @@ func getBranchDisplayStrings(
res := []string{recencyColor.Sprint(b.Recency)}
pr, hasPr := prs[b]
+
res = append(res, coloredPrNumber(pr, hasPr), coloredName)
if fullDescription {
- return append(
- res,
+ res = append(res,
fmt.Sprintf("%s %s",
style.FgYellow.Sprint(b.UpstreamRemote),
style.FgYellow.Sprint(b.UpstreamBranch),
@@ -102,3 +106,18 @@ func BranchStatus(branch *models.Branch) string {
func SetCustomBranches(customBranchColors map[string]string) {
branchPrefixColorCache = utils.SetCustomColors(customBranchColors)
}
+
+func coloredPrNumber(pr *models.GithubPullRequest, hasPr bool) string {
+ if hasPr {
+ colour := style.FgMagenta // = state MERGED
+ switch pr.State {
+ case "OPEN":
+ colour = style.FgGreen
+ case "CLOSED":
+ colour = style.FgRed
+ }
+ return colour.Sprint("#" + strconv.Itoa(pr.Number))
+ }
+
+ return ("")
+}
diff --git a/pkg/gui/pull_request_menu_panel.go b/pkg/gui/pull_request_menu_panel.go
index cd39b0c6a..276074072 100644
--- a/pkg/gui/pull_request_menu_panel.go
+++ b/pkg/gui/pull_request_menu_panel.go
@@ -45,7 +45,7 @@ func (gui *Gui) createOrOpenPullRequestMenu(selectedBranch *models.Branch, check
if hasPr {
menuItems = append(menuItems, &menuItem{
- displayString: gui.GitCommand.Tr.MustSpecifyOriginError + strconv.Itoa(pr.Number),
+ displayString: gui.Git.Gh.Tr.MustSpecifyOriginError + strconv.Itoa(pr.Number),
onPress: func() error {
return gui.OSCommand.OpenLink(pr.Url)
},
diff --git a/pkg/i18n/chinese.go b/pkg/i18n/chinese.go
index 060689db7..580a08205 100644
--- a/pkg/i18n/chinese.go
+++ b/pkg/i18n/chinese.go
@@ -441,9 +441,6 @@ func chineseTranslationSet() TranslationSet {
LcSelectBranch: "选择分支",
CreatingPullRequestAtUrl: "在 URL 创建抓取请求: %s",
OpenPr: "公开公关 #",
-
- Spans: Spans{
- ConfirmRevertCommit: "您确定要撤消 {{.selectedCommit}} 吗?",
Actions: Actions{
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
CheckoutCommit: "检出提交",
diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go
index e1d7c5b26..222ad4d89 100644
--- a/pkg/i18n/english.go
+++ b/pkg/i18n/english.go
@@ -440,8 +440,6 @@ type TranslationSet struct {
DecreaseContextInDiffView string
CreatePullRequestOptions string
LcCreatePullRequestOptions string
- LcDefaultBranch string
- LcSelectBranch string
CreatePullRequest string
SelectConfigFile string
NoConfigFileFoundErr string