Skip to content

Commit

Permalink
[PATCH] Add --patch option to mr show
Browse files Browse the repository at this point in the history
Fixes #277 which requests functionality that provides the patches
for an MR in 'git show' format.  This is useful for users that want
to quickly look at a patchset without checking out a branch.

The original patch provided by Eric required users to provide the
target remote, even if the target remote was "origin".  The additional
code provided by Prarit uses the fetch remote name in 'git remote -v'
by default unless the user specifies a specific remote.

Original patch from Eric, with modifications to automatically find the
target remote, and the test from Prarit.

Add '--patch' option to 'mr show' that displays the patches for a merge
request in 'git show' format.

Co-Developed-by: Eric Engestrom <[email protected]>
Signed-off-by: Prarit Bhargava <[email protected]>
  • Loading branch information
prarit committed Sep 2, 2020
1 parent 4b64aaa commit 6ce90a2
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 1 deletion.
58 changes: 57 additions & 1 deletion cmd/mr_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ import (
"github.com/spf13/viper"
gitlab "github.com/xanzy/go-gitlab"
"github.com/zaquestion/lab/internal/action"
"github.com/zaquestion/lab/internal/git"
lab "github.com/zaquestion/lab/internal/gitlab"
)

var (
mrShowPatch bool
mrShowPatchReverse bool
)

var mrShowCmd = &cobra.Command{
Use: "show [remote] <id>",
Aliases: []string{"get"},
Expand All @@ -41,7 +47,25 @@ var mrShowCmd = &cobra.Command{
}
renderMarkdown := !noMarkdown

printMR(mr, rn, renderMarkdown)
if mrShowPatch {
var remote string

if len(args) == 1 {
remote = findLocalRemote(mr.TargetProjectID)
} else if len(args) == 2 {
remote = args[0]
} else {
log.Fatal("Too many arguments.")
}

err := git.Fetch(remote, mr.SHA)
if err != nil {
log.Fatal(err)
}
git.Show(remote+"/"+mr.TargetBranch, mr.SHA, mrShowPatchReverse)
} else {
printMR(mr, rn, renderMarkdown)
}

showComments, _ := cmd.Flags().GetBool("comments")
if showComments {
Expand All @@ -60,6 +84,36 @@ var mrShowCmd = &cobra.Command{
},
}

func findLocalRemote(ProjectID int) string {
var remote string

project, err := lab.GetProject(ProjectID)
if err != nil {
log.Fatal(err)
}
remotes_str, err := git.GetLocalRemotes()
if err != nil {
log.Fatal(err)
}
remotes := strings.Split(remotes_str, "\n")

// find the matching local remote for this project
for r := range remotes {
// The fetch and push entries can be different for a remote.
// Only the fetch entry is useful.
if strings.Contains(remotes[r], project.SSHURLToRepo+" (fetch)") {
found := strings.Split(remotes[r], "\t")
remote = found[0]
break
}
}

if remote == "" {
log.Fatal("remote for ", project.SSHURLToRepo, "not found in local remotes")
}
return remote
}

func printMR(mr *gitlab.MergeRequest, project string, renderMarkdown bool) {
assignee := "None"
milestone := "None"
Expand Down Expand Up @@ -204,6 +258,8 @@ func init() {
mrShowCmd.Flags().BoolP("no-markdown", "M", false, "Don't use markdown renderer to print the issue description")
mrShowCmd.Flags().BoolP("comments", "c", false, "Show comments for the merge request")
mrShowCmd.Flags().StringP("since", "s", "", "Show comments since specified date (format: 2020-08-21 14:57:46.808 +0000 UTC)")
mrShowCmd.Flags().BoolVarP(&mrShowPatch, "patch", "p", false, "Show MR patches")
mrShowCmd.Flags().BoolVarP(&mrShowPatchReverse, "reverse", "", false, "Reverse order when showing MR patches (chronological instead of anti-chronological)")
mrCmd.AddCommand(mrShowCmd)
carapace.Gen(mrShowCmd).PositionalCompletion(
action.Remotes(),
Expand Down
28 changes: 28 additions & 0 deletions cmd/mr_show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,31 @@ WebURL: https://gitlab.com/zaquestion/test/-/merge_requests/1
require.Contains(t, string(b), `commented at`)
require.Contains(t, string(b), `updated comment at`)
}

func Test_mrShow_patch(t *testing.T) {
t.Parallel()
repo := copyTestRepo(t)
cmd := exec.Command(labBinaryPath, "mr", "show", "origin", "1", "--patch")
cmd.Dir = repo

b, err := cmd.CombinedOutput()
if err != nil {
t.Log(string(b))
t.Error(err)
}

out := string(b)
out = stripansi.Strip(out)
// The index line below has been stripped as it is dependent on
// the git version and pretty defaults.
require.Contains(t, out, `commit 54fd49a2ac60aeeef5ddc75efecd49f85f7ba9b0
Author: Zaq? Wiedmann <[email protected]>
Date: Tue Sep 19 03:55:16 2017 +0000
Test file for MR test
diff --git a/mrtest b/mrtest
new file mode 100644
`)

}
35 changes: 35 additions & 0 deletions internal/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,38 @@ func InsideGitRepo() bool {
out, _ := cmd.CombinedOutput()
return bytes.Contains(out, []byte("true\n"))
}

// Fetch a commit from a given remote
func Fetch(remote, commit string) error {
gitcmd := []string{"fetch", remote, commit}
cmd := New(gitcmd...)
cmd.Stdout = nil
cmd.Stderr = nil
err := cmd.Run()
if err != nil {
return errors.Errorf("Can't fetch git commit %s from remote %s", commit, remote)
}
return nil
}

// Show all the commits between 2 git commits
func Show(commit1, commit2 string, reverse bool) {
gitcmd := []string{"show"}
if reverse {
gitcmd = append(gitcmd, "--reverse")
}
gitcmd = append(gitcmd, fmt.Sprintf("%s..%s", commit1, commit2))
New(gitcmd...).Run()
}

// GetLocalRemotes returns a string of local remote names and URLs
func GetLocalRemotes() (string, error) {
cmd := New("remote", "-v")
cmd.Stdout = nil
remotes, err := cmd.Output()
if err != nil {
return "", err
}

return string(remotes), nil
}

0 comments on commit 6ce90a2

Please sign in to comment.