Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions api/queries_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,26 +524,6 @@ func ForkRepo(client *Client, repo ghrepo.Interface, org string) (*Repository, e
}, nil
}

func LastCommit(client *Client, repo ghrepo.Interface) (*Commit, error) {
var responseData struct {
Repository struct {
DefaultBranchRef struct {
Target struct {
Commit `graphql:"... on Commit"`
}
}
} `graphql:"repository(owner: $owner, name: $repo)"`
}
variables := map[string]interface{}{
"owner": githubv4.String(repo.RepoOwner()), "repo": githubv4.String(repo.RepoName()),
}
gql := graphQLClient(client.http, repo.RepoHost())
if err := gql.QueryNamed(context.Background(), "LastCommit", &responseData, variables); err != nil {
return nil, err
}
return &responseData.Repository.DefaultBranchRef.Target.Commit, nil
}

// RepoFindForks finds forks of the repo that are affiliated with the viewer
func RepoFindForks(client *Client, repo ghrepo.Interface, limit int) ([]*Repository, error) {
result := struct {
Expand Down
27 changes: 0 additions & 27 deletions git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"errors"
"fmt"
"io"
"net/url"
"os"
"os/exec"
Expand Down Expand Up @@ -230,17 +229,6 @@ func CommitBody(sha string) (string, error) {
return string(output), err
}

// Push publishes a git ref to a remote and sets up upstream configuration
func Push(remote string, ref string, cmdOut, cmdErr io.Writer) error {
pushCmd, err := GitCommand("push", "--set-upstream", remote, ref)
if err != nil {
return err
}
pushCmd.Stdout = cmdOut
pushCmd.Stderr = cmdErr
return run.PrepareCmd(pushCmd).Run()
}

type BranchConfig struct {
RemoteName string
RemoteURL *url.URL
Expand Down Expand Up @@ -398,21 +386,6 @@ func GetDirFromPath(p string) (string, error) {
return firstLine(output), err
}

func PathFromRepoRoot() string {
showCmd, err := GitCommand("rev-parse", "--show-prefix")
if err != nil {
return ""
}
output, err := run.PrepareCmd(showCmd).Output()
if err != nil {
return ""
}
if path := firstLine(output); path != "" {
return path[:len(path)-1]
}
return ""
}

func outputLines(output []byte) []string {
lines := strings.TrimSuffix(string(output), "\n")
return strings.Split(lines, "\n")
Expand Down
139 changes: 139 additions & 0 deletions git/local_repo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package git

import (
"bufio"
"bytes"
"context"
"io"
"os/exec"
"regexp"
"strings"

"github.com/cli/safeexec"
)

type LocalRepo struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer

gitCommandInit func(ctx context.Context, args ...string) (*exec.Cmd, error)
gitExePath string
gitExeErr error
}

func (c *LocalRepo) PathPrefix(ctx context.Context) (string, error) {
showCmd, err := c.gitCommand(ctx, "rev-parse", "--show-prefix")
if err != nil {
return "", err
}
output, err := showCmd.Output()
return strings.TrimRight(string(output), "\n"), err
}

func (c *LocalRepo) LastCommit(ctx context.Context) (*Commit, error) {
showCmd, err := c.gitCommand(ctx, "-c", "log.ShowSignature=false", "show", "-s", "--pretty=format:%H,%s", "HEAD")
if err != nil {
return nil, err
}
output, err := showCmd.Output()
if err != nil {
return nil, err
}
idx := bytes.IndexByte(output, ',')
return &Commit{
Sha: string(output[0:idx]),
Title: strings.TrimSpace(string(output[idx+1:])),
}, nil
}

func (c *LocalRepo) gitCommand(ctx context.Context, args ...string) (*exec.Cmd, error) {
if c.gitCommandInit != nil {
return c.gitCommandInit(ctx, args...)
}
gitExe, err := c.gitExe()
if err != nil {
return nil, err
}
cmd := exec.CommandContext(ctx, gitExe, args...)
return cmd, nil
}

func (c *LocalRepo) gitExe() (string, error) {
if c.gitExePath == "" && c.gitExeErr == nil {
c.gitExePath, c.gitExeErr = safeexec.LookPath("git")
}
return c.gitExePath, c.gitExeErr
}

type PushOptions struct {
RemoteName string
TargetBranch string
ShowProgress bool
SetUpstream bool
}

var gitPushRegexp = regexp.MustCompile(`^remote: (Create a pull request.*by visiting|[[:space:]]*https://.*/pull/new/)`)

// Push publishes a branch to a git remote and filters out "Create a pull request by visiting <URL>" lines
// before forwarding them to stderr.
func (g *LocalRepo) Push(ctx context.Context, opts PushOptions) error {
args := []string{"push"}
if opts.SetUpstream {
args = append(args, "--set-upstream")
}
if opts.ShowProgress {
args = append(args, "--progress")
}
args = append(args, opts.RemoteName, "HEAD:"+opts.TargetBranch)

pushCmd, err := g.gitCommand(ctx, args...)
if err != nil {
return err
}
pushCmd.Stdin = g.Stdin
pushCmd.Stdout = g.Stdout
r, err := pushCmd.StderrPipe()
if err != nil {
return err
}
if err = pushCmd.Start(); err != nil {
return err
}
if err = filterLines(g.Stderr, r, gitPushRegexp); err != nil {
return err
}
return pushCmd.Wait()
}

func filterLines(w io.Writer, r io.Reader, re *regexp.Regexp) error {
s := bufio.NewScanner(r)
s.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
i := bytes.IndexAny(data, "\r\n")
if i >= 0 {
// encompass both CR & LF characters if they appear together
if data[i] == '\r' && len(data) > i+1 && data[i+1] == '\n' {
return i + 2, data[0 : i+2], nil
}
return i + 1, data[0 : i+1], nil
}
if atEOF {
return len(data), data, nil
}
// Request more data.
return 0, nil, nil
})
for s.Scan() {
line := s.Bytes()
if !re.Match(line) {
_, err := w.Write(line)
if err != nil {
return err
}
}
}
return s.Err()
}
126 changes: 126 additions & 0 deletions git/local_repo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package git

import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestHelperProcess(t *testing.T) {
if os.Getenv("GH_WANT_HELPER_PROCESS") != "1" {
return
}
args := os.Args[3:]
switch strings.Join(args, " ") {
case "git push origin HEAD:no-output":
case "git push origin HEAD:some-output":
fmt.Fprintln(os.Stdout, "STDOUT")
fmt.Fprintln(os.Stderr, "STDERR")
case "git push --progress origin HEAD:feature":
fmt.Fprint(os.Stderr, "progress: 0%\r")
fmt.Fprint(os.Stderr, "progress: 33%\r")
fmt.Fprint(os.Stderr, "progress: 66%\r")
fmt.Fprint(os.Stderr, "progress: 100%, done.\n")
fmt.Fprint(os.Stderr, "remote: \n")
fmt.Fprint(os.Stderr, "remote: Create a pull request for 'feature' on GitHub by visiting: \nremote: https://github.com/owner/repo/pull/new/feature \nremote: \n")
fmt.Fprint(os.Stderr, "To https://github.com/owner/repo.git\n * [new branch] HEAD -> feature\n")
case "git push origin HEAD:crlf":
fmt.Fprint(os.Stderr, "one\r\n")
fmt.Fprint(os.Stderr, "two\r\n")
fmt.Fprint(os.Stderr, "three\r")
fmt.Fprint(os.Stderr, "four")
default:
fmt.Fprintf(os.Stderr, "unrecognized arguments: %#v\n", args)
os.Exit(1)
}
os.Exit(0)
}

func Test_gitClientExec_Push(t *testing.T) {
gitCommand := func(ctx context.Context, args ...string) (*exec.Cmd, error) {
args = append([]string{"-test.run=TestHelperProcess", "--", "git"}, args...)
cmd := exec.Command(os.Args[0], args...)
cmd.Env = []string{"GH_WANT_HELPER_PROCESS=1"}
return cmd, nil
}

tests := []struct {
name string
args PushOptions
wantStdout string
wantStderr *logWriter
wantErr bool
}{
{
name: "no output",
args: PushOptions{RemoteName: "origin", TargetBranch: "no-output"},
wantStdout: "",
wantStderr: &logWriter{},
},
{
name: "some output",
args: PushOptions{RemoteName: "origin", TargetBranch: "some-output"},
wantStdout: "STDOUT\n",
wantStderr: &logWriter{"STDERR\n"},
},
{
name: "progress output",
args: PushOptions{RemoteName: "origin", TargetBranch: "feature", ShowProgress: true},
wantStdout: "",
wantStderr: &logWriter{
"progress: 0%\r",
"progress: 33%\r",
"progress: 66%\r",
"progress: 100%, done.\n",
"remote: \n",
"remote: \n",
"To https://github.com/owner/repo.git\n",
" * [new branch] HEAD -> feature\n",
},
},
{
name: "cr-lf",
args: PushOptions{RemoteName: "origin", TargetBranch: "crlf"},
wantStdout: "",
wantStderr: &logWriter{"one\r\n", "two\r\n", "three\r", "four"},
},
{
name: "failure",
args: PushOptions{RemoteName: "nonexist", TargetBranch: "feature"},
wantErr: true,
wantStderr: &logWriter{"unrecognized arguments: []string{\"git\", \"push\", \"nonexist\", \"HEAD:feature\"}\n"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stdout := &bytes.Buffer{}
stderr := &logWriter{}
g := &LocalRepo{
gitCommandInit: gitCommand,
Stdout: stdout,
Stderr: stderr,
}
if err := g.Push(context.Background(), tt.args); (err != nil) != tt.wantErr {
t.Errorf("gitClientExec.Push() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotStdout := stdout.String(); gotStdout != tt.wantStdout {
t.Errorf("gitClientExec.Push() stdout = %q, want %q", gotStdout, tt.wantStdout)
}
assert.Equal(t, tt.wantStderr, stderr)
})
}
}

type logWriter []string

func (w *logWriter) Write(p []byte) (int, error) {
*w = append(*w, string(p))
return len(p), nil
}
Loading