From c128f5d680f59fd125cafd90f10e39eae5f3a135 Mon Sep 17 00:00:00 2001 From: Jeremy Stribling Date: Mon, 31 Jul 2017 15:34:45 -0700 Subject: plumbing: fix pack commands for the file client on Windows The default git install on Windows doesn't come with commands for receive-pack and upload-pack in the default $PATH. Instead, use --exec-path to find pack executables in that case. --- plumbing/transport/file/client.go | 55 ++++++++++++++++++++++++++++++++-- plumbing/transport/file/client_test.go | 22 ++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/plumbing/transport/file/client.go b/plumbing/transport/file/client.go index 0b42abf..af4a88b 100644 --- a/plumbing/transport/file/client.go +++ b/plumbing/transport/file/client.go @@ -2,9 +2,13 @@ package file import ( + "bufio" + "errors" "io" "os" "os/exec" + "path/filepath" + "strings" "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common" @@ -30,6 +34,45 @@ func NewClient(uploadPackBin, receivePackBin string) transport.Transport { }) } +func prefixExecPath(cmd string) (string, error) { + // Use `git --exec-path` to find the exec path. + execCmd := exec.Command("git", "--exec-path") + + stdout, err := execCmd.StdoutPipe() + if err != nil { + return "", err + } + stdoutBuf := bufio.NewReader(stdout) + + err = execCmd.Start() + if err != nil { + return "", err + } + + execPathBytes, isPrefix, err := stdoutBuf.ReadLine() + if err != nil { + return "", err + } + if isPrefix { + return "", errors.New("Couldn't read exec-path line all at once") + } + + err = execCmd.Wait() + if err != nil { + return "", err + } + execPath := string(execPathBytes) + execPath = strings.TrimSpace(execPath) + cmd = filepath.Join(execPath, cmd) + + // Make sure it actually exists. + _, err = os.Stat(cmd) + if err != nil { + return "", err + } + return cmd, nil +} + func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthMethod, ) (common.Command, error) { @@ -40,8 +83,16 @@ func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthM cmd = r.ReceivePackBin } - if _, err := exec.LookPath(cmd); err != nil { - return nil, err + _, err := exec.LookPath(cmd) + if err != nil { + if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound { + cmd, err = prefixExecPath(cmd) + if err != nil { + return nil, err + } + } else { + return nil, err + } } return &command{cmd: exec.Command(cmd, ep.Path())}, nil diff --git a/plumbing/transport/file/client_test.go b/plumbing/transport/file/client_test.go index 030175e..3532a4c 100644 --- a/plumbing/transport/file/client_test.go +++ b/plumbing/transport/file/client_test.go @@ -14,6 +14,28 @@ import ( func Test(t *testing.T) { TestingT(t) } +type ClientSuite struct { + CommonSuite +} + +var _ = Suite(&ClientSuite{}) + +func (s *ClientSuite) TestCommand(c *C) { + runner := &runner{ + UploadPackBin: transport.UploadPackServiceName, + ReceivePackBin: transport.ReceivePackServiceName, + } + ep, err := transport.NewEndpoint(filepath.Join("fake", "repo")) + c.Assert(err, IsNil) + var emptyAuth transport.AuthMethod + _, err = runner.Command("git-receive-pack", ep, emptyAuth) + c.Assert(err, IsNil) + + // Make sure we get an error for one that doesn't exist. + _, err = runner.Command("git-fake-command", ep, emptyAuth) + c.Assert(os.IsNotExist(err), Equals, true) +} + const bareConfig = `[core] repositoryformatversion = 0 filemode = true -- cgit