mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-02-03 13:21:56 +02:00
Add option to create pull request form branches panel.
This commit is contained in:
parent
d5f64602a8
commit
df0e3e52fe
@ -555,6 +555,12 @@ func (c *GitCommand) Show(sha string) (string, error) {
|
|||||||
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git show --color %s", sha))
|
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git show --color %s", sha))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRemoteURL returns current repo remote url
|
||||||
|
func (c *GitCommand) GetRemoteURL() string {
|
||||||
|
url, _ := c.OSCommand.RunCommandWithOutput("git config --get remote.origin.url")
|
||||||
|
return utils.TrimTrailingNewline(url)
|
||||||
|
}
|
||||||
|
|
||||||
// Diff returns the diff of a file
|
// Diff returns the diff of a file
|
||||||
func (c *GitCommand) Diff(file *File) string {
|
func (c *GitCommand) Diff(file *File) string {
|
||||||
cachedArg := ""
|
cachedArg := ""
|
||||||
|
101
pkg/commands/pull_request.go
Normal file
101
pkg/commands/pull_request.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Service is a service that repository is on (Github, Bitbucket, ...)
|
||||||
|
type Service struct {
|
||||||
|
Name string
|
||||||
|
PullRequestURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullRequest opens a link in browser to create new pull request
|
||||||
|
// with selected branch
|
||||||
|
type PullRequest struct {
|
||||||
|
GitServices []*Service
|
||||||
|
GitCommand *GitCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepoInformation holds some basic information about the repo
|
||||||
|
type RepoInformation struct {
|
||||||
|
Owner string
|
||||||
|
Repository string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServices() []*Service {
|
||||||
|
return []*Service{
|
||||||
|
{
|
||||||
|
Name: "github.com",
|
||||||
|
PullRequestURL: "https://github.com/%s/%s/compare/%s?expand=1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bitbucket.org",
|
||||||
|
PullRequestURL: "https://bitbucket.org/%s/%s/pull-requests/new?t=%s",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "gitlab.com",
|
||||||
|
PullRequestURL: "https://gitlab.com/%s/%s/merge_requests/new?merge_request[source_branch]=%s",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPullRequest creates new instance of PullRequest
|
||||||
|
func NewPullRequest(gitCommand *GitCommand) (*PullRequest, error) {
|
||||||
|
return &PullRequest{
|
||||||
|
GitServices: getServices(),
|
||||||
|
GitCommand: gitCommand,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create opens link to new pull request in browser
|
||||||
|
func (pr *PullRequest) Create(branch *Branch) error {
|
||||||
|
repoURL := pr.GitCommand.GetRemoteURL()
|
||||||
|
var gitService *Service
|
||||||
|
|
||||||
|
for _, service := range pr.GitServices {
|
||||||
|
if strings.Contains(repoURL, service.Name) {
|
||||||
|
gitService = service
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gitService == nil {
|
||||||
|
return errors.New(pr.GitCommand.Tr.SLocalize("UnsupportedGitService"))
|
||||||
|
}
|
||||||
|
|
||||||
|
repoInfo := getRepoInfoFromURL(repoURL)
|
||||||
|
|
||||||
|
return pr.GitCommand.OSCommand.OpenFile(fmt.Sprintf(
|
||||||
|
gitService.PullRequestURL, repoInfo.Owner, repoInfo.Repository, branch.Name,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRepoInfoFromURL(url string) *RepoInformation {
|
||||||
|
isHTTP := strings.HasPrefix(url, "http")
|
||||||
|
removeGitExtension := regexp.MustCompile(`\.git$`)
|
||||||
|
|
||||||
|
if isHTTP {
|
||||||
|
splits := strings.Split(url, "/")
|
||||||
|
owner := splits[len(splits)-2]
|
||||||
|
repo := removeGitExtension.ReplaceAllString(splits[len(splits)-1], "")
|
||||||
|
|
||||||
|
return &RepoInformation{
|
||||||
|
Owner: owner,
|
||||||
|
Repository: repo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpSplit := strings.Split(url, ":")
|
||||||
|
splits := strings.Split(tmpSplit[1], "/")
|
||||||
|
owner := splits[0]
|
||||||
|
repo := removeGitExtension.ReplaceAllString(splits[1], "")
|
||||||
|
|
||||||
|
return &RepoInformation{
|
||||||
|
Owner: owner,
|
||||||
|
Repository: repo,
|
||||||
|
}
|
||||||
|
}
|
151
pkg/commands/pull_request_test.go
Normal file
151
pkg/commands/pull_request_test.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetRepoInfoFromURL(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
repoURL string
|
||||||
|
test func(*RepoInformation)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"Returns repository information for git remote url",
|
||||||
|
"git@github.com:petersmith/super_calculator",
|
||||||
|
func(repoInfo *RepoInformation) {
|
||||||
|
assert.EqualValues(t, repoInfo.Owner, "petersmith")
|
||||||
|
assert.EqualValues(t, repoInfo.Repository, "super_calculator")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Returns repository information for http remote url",
|
||||||
|
"https://my_username@bitbucket.org/johndoe/social_network.git",
|
||||||
|
func(repoInfo *RepoInformation) {
|
||||||
|
assert.EqualValues(t, repoInfo.Owner, "johndoe")
|
||||||
|
assert.EqualValues(t, repoInfo.Repository, "social_network")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
s.test(getRepoInfoFromURL(s.repoURL))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreatePullRequest(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
branch *Branch
|
||||||
|
command func(string, ...string) *exec.Cmd
|
||||||
|
test func(err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"Opens a link to new pull request on bitbucket",
|
||||||
|
&Branch{
|
||||||
|
Name: "feature/profile-page",
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
// Handle git remote url call
|
||||||
|
if strings.HasPrefix(cmd, "git") {
|
||||||
|
return exec.Command("echo", "git@bitbucket.org:johndoe/social_network.git")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, cmd, "open")
|
||||||
|
assert.Equal(t, args, []string{"https://bitbucket.org/johndoe/social_network/pull-requests/new?t=feature/profile-page"})
|
||||||
|
return exec.Command("echo")
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Opens a link to new pull request on bitbucket with http remote url",
|
||||||
|
&Branch{
|
||||||
|
Name: "feature/events",
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
// Handle git remote url call
|
||||||
|
if strings.HasPrefix(cmd, "git") {
|
||||||
|
return exec.Command("echo", "https://my_username@bitbucket.org/johndoe/social_network.git")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, cmd, "open")
|
||||||
|
assert.Equal(t, args, []string{"https://bitbucket.org/johndoe/social_network/pull-requests/new?t=feature/events"})
|
||||||
|
return exec.Command("echo")
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Opens a link to new pull request on github",
|
||||||
|
&Branch{
|
||||||
|
Name: "feature/sum-operation",
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
// Handle git remote url call
|
||||||
|
if strings.HasPrefix(cmd, "git") {
|
||||||
|
return exec.Command("echo", "git@github.com:peter/calculator.git")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, cmd, "open")
|
||||||
|
assert.Equal(t, args, []string{"https://github.com/peter/calculator/compare/feature/sum-operation?expand=1"})
|
||||||
|
return exec.Command("echo")
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Opens a link to new pull request on gitlab",
|
||||||
|
&Branch{
|
||||||
|
Name: "feature/ui",
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
// Handle git remote url call
|
||||||
|
if strings.HasPrefix(cmd, "git") {
|
||||||
|
return exec.Command("echo", "git@gitlab.com:peter/calculator.git")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, cmd, "open")
|
||||||
|
assert.Equal(t, args, []string{"https://gitlab.com/peter/calculator/merge_requests/new?merge_request[source_branch]=feature/ui"})
|
||||||
|
return exec.Command("echo")
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Throws an error if git service is unsupported",
|
||||||
|
&Branch{
|
||||||
|
Name: "feature/divide-operation",
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
return exec.Command("echo", "git@something.com:peter/calculator.git")
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.Error(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
gitCommand := newDummyGitCommand()
|
||||||
|
gitCommand.OSCommand.command = s.command
|
||||||
|
gitCommand.OSCommand.Config.GetUserConfig().Set("os.openCommand", "open {{filename}}")
|
||||||
|
dummyPullRequest, _ := NewPullRequest(gitCommand)
|
||||||
|
s.test(dummyPullRequest.Create(s.branch))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,17 @@ func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.refreshSidePanels(g)
|
return gui.refreshSidePanels(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
branch := gui.getSelectedBranch(gui.getBranchesView(g))
|
||||||
|
pullRequest, _ := commands.NewPullRequest(gui.GitCommand)
|
||||||
|
|
||||||
|
if err := pullRequest.Create(branch); err != nil {
|
||||||
|
return gui.createErrorPanel(g, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
|
||||||
branch := gui.getSelectedBranch(v)
|
branch := gui.getSelectedBranch(v)
|
||||||
message := gui.Tr.SLocalize("SureForceCheckout")
|
message := gui.Tr.SLocalize("SureForceCheckout")
|
||||||
|
@ -277,6 +277,12 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
|||||||
Handler: gui.handleBranchPress,
|
Handler: gui.handleBranchPress,
|
||||||
KeyReadable: "space",
|
KeyReadable: "space",
|
||||||
Description: gui.Tr.SLocalize("checkout"),
|
Description: gui.Tr.SLocalize("checkout"),
|
||||||
|
}, {
|
||||||
|
ViewName: "branches",
|
||||||
|
Key: 'o',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCreatePullRequestPress,
|
||||||
|
Description: gui.Tr.SLocalize("createPullRequest"),
|
||||||
}, {
|
}, {
|
||||||
ViewName: "branches",
|
ViewName: "branches",
|
||||||
Key: 'c',
|
Key: 'c',
|
||||||
|
@ -379,6 +379,12 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "ConfirmQuit",
|
ID: "ConfirmQuit",
|
||||||
Other: `Weet je zeker dat je dit programma wil sluiten?`,
|
Other: `Weet je zeker dat je dit programma wil sluiten?`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "UnsupportedGitService",
|
||||||
|
Other: `Niet-ondersteunde git-service`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "createPullRequest",
|
||||||
|
Other: `maak een pull-aanvraag`,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -402,6 +402,12 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SwitchRepo",
|
ID: "SwitchRepo",
|
||||||
Other: `switch to a recent repo`,
|
Other: `switch to a recent repo`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "UnsupportedGitService",
|
||||||
|
Other: `Unsupported git service`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "createPullRequest",
|
||||||
|
Other: `create pull request`,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -377,6 +377,12 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "ConfirmQuit",
|
ID: "ConfirmQuit",
|
||||||
Other: `Na pewno chcesz wyjść z programu?`,
|
Other: `Na pewno chcesz wyjść z programu?`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "UnsupportedGitService",
|
||||||
|
Other: `Nieobsługiwana usługa git`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "createPullRequest",
|
||||||
|
Other: `utwórz żądanie wyciągnięcia`,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user