Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit beab77a

Browse files
authored
Merge pull request #418 from smola/ssh-issue-310
fix push on git and ssh
2 parents 2a00316 + cbbd2a9 commit beab77a

File tree

6 files changed

+189
-39
lines changed

6 files changed

+189
-39
lines changed

plumbing/transport/file/client.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package file
33

44
import (
55
"io"
6+
"os"
67
"os/exec"
78

89
"gopkg.in/src-d/go-git.v4/plumbing/transport"
@@ -71,10 +72,16 @@ func (c *command) Close() error {
7172
return nil
7273
}
7374

74-
return c.cmd.Process.Kill()
75-
}
76-
77-
func (c *command) Wait() error {
7875
defer func() { c.closed = true }()
79-
return c.cmd.Wait()
76+
err := c.cmd.Wait()
77+
if _, ok := err.(*os.PathError); ok {
78+
return nil
79+
}
80+
81+
// When a repository does not exist, the command exits with code 128.
82+
if _, ok := err.(*exec.ExitError); ok {
83+
return nil
84+
}
85+
86+
return err
8087
}

plumbing/transport/git/common.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,12 @@ func (c *command) StdoutPipe() (io.Reader, error) {
9090
}
9191

9292
func endpointToCommand(cmd string, ep transport.Endpoint) string {
93-
return fmt.Sprintf("%s %s%chost=%s%c", cmd, ep.Path(), 0, ep.Host(), 0)
94-
}
93+
host := ep.Host()
94+
if ep.Port() != DefaultPort {
95+
host = fmt.Sprintf("%s:%d", ep.Host(), ep.Port())
96+
}
9597

96-
// Wait no-op function, required by the interface
97-
func (c *command) Wait() error {
98-
return nil
98+
return fmt.Sprintf("%s %s%chost=%s%c", cmd, ep.Path(), 0, host, 0)
9999
}
100100

101101
// Close closes the TCP connection and connection.
+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package git
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"io/ioutil"
7+
"net"
8+
"os"
9+
"os/exec"
10+
"path/filepath"
11+
"strings"
12+
"time"
13+
14+
"github.com/src-d/go-git-fixtures"
15+
"gopkg.in/src-d/go-git.v4/plumbing/transport"
16+
"gopkg.in/src-d/go-git.v4/plumbing/transport/test"
17+
18+
. "gopkg.in/check.v1"
19+
)
20+
21+
type ReceivePackSuite struct {
22+
test.ReceivePackSuite
23+
fixtures.Suite
24+
25+
base string
26+
daemon *exec.Cmd
27+
}
28+
29+
var _ = Suite(&ReceivePackSuite{})
30+
31+
func (s *ReceivePackSuite) SetUpTest(c *C) {
32+
s.ReceivePackSuite.Client = DefaultClient
33+
34+
port, err := freePort()
35+
c.Assert(err, IsNil)
36+
37+
base, err := ioutil.TempDir(os.TempDir(), "go-git-daemon-test")
38+
c.Assert(err, IsNil)
39+
s.base = base
40+
41+
host := fmt.Sprintf("localhost_%d", port)
42+
interpolatedBase := filepath.Join(base, host)
43+
err = os.MkdirAll(interpolatedBase, 0755)
44+
c.Assert(err, IsNil)
45+
46+
dotgit := fixtures.Basic().One().DotGit().Base()
47+
prepareRepo(c, dotgit)
48+
err = os.Rename(dotgit, filepath.Join(interpolatedBase, "basic.git"))
49+
c.Assert(err, IsNil)
50+
51+
ep, err := transport.NewEndpoint(fmt.Sprintf("git://localhost:%d/basic.git", port))
52+
c.Assert(err, IsNil)
53+
s.ReceivePackSuite.Endpoint = ep
54+
55+
dotgit = fixtures.ByTag("empty").One().DotGit().Base()
56+
prepareRepo(c, dotgit)
57+
err = os.Rename(dotgit, filepath.Join(interpolatedBase, "empty.git"))
58+
c.Assert(err, IsNil)
59+
60+
ep, err = transport.NewEndpoint(fmt.Sprintf("git://localhost:%d/empty.git", port))
61+
c.Assert(err, IsNil)
62+
s.ReceivePackSuite.EmptyEndpoint = ep
63+
64+
ep, err = transport.NewEndpoint(fmt.Sprintf("git://localhost:%d/non-existent.git", port))
65+
c.Assert(err, IsNil)
66+
s.ReceivePackSuite.NonExistentEndpoint = ep
67+
68+
s.daemon = exec.Command(
69+
"git",
70+
"daemon",
71+
fmt.Sprintf("--base-path=%s", base),
72+
"--export-all",
73+
"--enable=receive-pack",
74+
"--reuseaddr",
75+
fmt.Sprintf("--port=%d", port),
76+
// Use interpolated paths to validate that clients are specifying
77+
// host and port properly.
78+
// Note that some git versions (e.g. v2.11.0) had a bug that prevented
79+
// the use of repository paths containing colons (:), so we use
80+
// underscore (_) instead of colon in the interpolation.
81+
// See https://github.com/git/git/commit/fe050334074c5132d01e1df2c1b9a82c9b8d394c
82+
fmt.Sprintf("--interpolated-path=%s/%%H_%%P%%D", base),
83+
// Unless max-connections is limited to 1, a git-receive-pack
84+
// might not be seen by a subsequent operation.
85+
"--max-connections=1",
86+
// Whitelist required for interpolated paths.
87+
fmt.Sprintf("%s/%s", interpolatedBase, "basic.git"),
88+
fmt.Sprintf("%s/%s", interpolatedBase, "empty.git"),
89+
)
90+
91+
// Environment must be inherited in order to acknowledge GIT_EXEC_PATH if set.
92+
s.daemon.Env = os.Environ()
93+
94+
err = s.daemon.Start()
95+
c.Assert(err, IsNil)
96+
97+
// Connections might be refused if we start sending request too early.
98+
time.Sleep(time.Millisecond * 500)
99+
}
100+
101+
func (s *ReceivePackSuite) TearDownTest(c *C) {
102+
err := s.daemon.Process.Signal(os.Interrupt)
103+
c.Assert(err, IsNil)
104+
_ = s.daemon.Wait()
105+
err = os.RemoveAll(s.base)
106+
c.Assert(err, IsNil)
107+
}
108+
109+
func freePort() (int, error) {
110+
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
111+
if err != nil {
112+
return 0, err
113+
}
114+
115+
l, err := net.ListenTCP("tcp", addr)
116+
if err != nil {
117+
return 0, err
118+
}
119+
120+
return l.Addr().(*net.TCPAddr).Port, l.Close()
121+
}
122+
123+
const bareConfig = `[core]
124+
repositoryformatversion = 0
125+
filemode = true
126+
bare = true`
127+
128+
func prepareRepo(c *C, path string) {
129+
// git-receive-pack refuses to update refs/heads/master on non-bare repo
130+
// so we ensure bare repo config.
131+
config := filepath.Join(path, "config")
132+
if _, err := os.Stat(config); err == nil {
133+
f, err := os.OpenFile(config, os.O_TRUNC|os.O_WRONLY, 0)
134+
c.Assert(err, IsNil)
135+
content := strings.NewReader(bareConfig)
136+
_, err = io.Copy(f, content)
137+
c.Assert(err, IsNil)
138+
c.Assert(f.Close(), IsNil)
139+
}
140+
}

plumbing/transport/internal/common/common.go

+24-26
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,8 @@ type Command interface {
5959
// Start starts the specified command. It does not wait for it to
6060
// complete.
6161
Start() error
62-
// Wait waits for the command to exit. It must have been started by
63-
// Start. The returned error is nil if the command runs, has no
64-
// problems copying stdin, stdout, and stderr, and exits with a zero
65-
// exit status.
66-
Wait() error
6762
// Close closes the command and releases any resources used by it. It
68-
// can be called to forcibly finish the command without calling to Wait
69-
// or to release resources after calling Wait.
63+
// will block until the command exits.
7064
Close() error
7165
}
7266

@@ -178,6 +172,7 @@ func (s *session) handleAdvRefDecodeError(err error) error {
178172
// If repository is not found, we get empty stdout and server writes an
179173
// error to stderr.
180174
if err == packp.ErrEmptyInput {
175+
s.finished = true
181176
if err := s.checkNotFoundError(); err != nil {
182177
return err
183178
}
@@ -246,9 +241,7 @@ func (s *session) UploadPack(req *packp.UploadPackRequest) (*packp.UploadPackRes
246241
return nil, err
247242
}
248243

249-
wc := &waitCloser{s.Command}
250-
rc := ioutil.NewReadCloser(r, wc)
251-
244+
rc := ioutil.NewReadCloser(r, s.Command)
252245
return DecodeUploadPackResponse(rc, req)
253246
}
254247

@@ -263,10 +256,14 @@ func (s *session) ReceivePack(req *packp.ReferenceUpdateRequest) (*packp.ReportS
263256
return nil, err
264257
}
265258

259+
if err := s.Stdin.Close(); err != nil {
260+
return nil, err
261+
}
262+
266263
if !req.Capabilities.Supports(capability.ReportStatus) {
267264
// If we have neither report-status or sideband, we can only
268265
// check return value error.
269-
return nil, s.Command.Wait()
266+
return nil, s.Command.Close()
270267
}
271268

272269
report := packp.NewReportStatus()
@@ -278,7 +275,7 @@ func (s *session) ReceivePack(req *packp.ReferenceUpdateRequest) (*packp.ReportS
278275
return report, err
279276
}
280277

281-
return report, s.Command.Wait()
278+
return report, s.Command.Close()
282279
}
283280

284281
func (s *session) finish() error {
@@ -302,7 +299,7 @@ func (s *session) finish() error {
302299
func (s *session) Close() error {
303300
if err := s.finish(); err != nil {
304301
_ = s.Command.Close()
305-
return nil
302+
return err
306303
}
307304

308305
return s.Command.Close()
@@ -329,10 +326,12 @@ func (s *session) checkNotFoundError() error {
329326
}
330327

331328
var (
332-
githubRepoNotFoundErr = "ERROR: Repository not found."
333-
bitbucketRepoNotFoundErr = "conq: repository does not exist."
334-
localRepoNotFoundErr = "does not appear to be a git repository"
335-
gitProtocolNotFoundErr = "ERR \n Repository not found."
329+
githubRepoNotFoundErr = "ERROR: Repository not found."
330+
bitbucketRepoNotFoundErr = "conq: repository does not exist."
331+
localRepoNotFoundErr = "does not appear to be a git repository"
332+
gitProtocolNotFoundErr = "ERR \n Repository not found."
333+
gitProtocolNoSuchErr = "ERR no such repository"
334+
gitProtocolAccessDeniedErr = "ERR access denied"
336335
)
337336

338337
func isRepoNotFoundError(s string) bool {
@@ -352,6 +351,14 @@ func isRepoNotFoundError(s string) bool {
352351
return true
353352
}
354353

354+
if strings.HasPrefix(s, gitProtocolNoSuchErr) {
355+
return true
356+
}
357+
358+
if strings.HasPrefix(s, gitProtocolAccessDeniedErr) {
359+
return true
360+
}
361+
355362
return false
356363
}
357364

@@ -403,12 +410,3 @@ func DecodeUploadPackResponse(r io.ReadCloser, req *packp.UploadPackRequest) (
403410

404411
return res, nil
405412
}
406-
407-
type waitCloser struct {
408-
Command Command
409-
}
410-
411-
// Close waits until the command exits and returns error, if any.
412-
func (c *waitCloser) Close() error {
413-
return c.Command.Wait()
414-
}

plumbing/transport/test/receive_pack.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ func (s *ReceivePackSuite) TestAdvertisedReferencesEmpty(c *C) {
3939
func (s *ReceivePackSuite) TestAdvertisedReferencesNotExists(c *C) {
4040
r, err := s.Client.NewReceivePackSession(s.NonExistentEndpoint, s.EmptyAuth)
4141
c.Assert(err, IsNil)
42-
defer func() { c.Assert(r.Close(), IsNil) }()
4342
ar, err := r.AdvertisedReferences()
4443
c.Assert(err, Equals, transport.ErrRepositoryNotFound)
4544
c.Assert(ar, IsNil)
45+
c.Assert(r.Close(), IsNil)
4646

4747
r, err = s.Client.NewReceivePackSession(s.NonExistentEndpoint, s.EmptyAuth)
4848
c.Assert(err, IsNil)
@@ -54,6 +54,7 @@ func (s *ReceivePackSuite) TestAdvertisedReferencesNotExists(c *C) {
5454
writer, err := r.ReceivePack(req)
5555
c.Assert(err, Equals, transport.ErrRepositoryNotFound)
5656
c.Assert(writer, IsNil)
57+
c.Assert(r.Close(), IsNil)
5758
}
5859

5960
func (s *ReceivePackSuite) TestCallAdvertisedReferenceTwice(c *C) {
@@ -270,7 +271,6 @@ func (s *ReceivePackSuite) TestSendPackAddDeleteReference(c *C) {
270271
func (s *ReceivePackSuite) testSendPackAddReference(c *C) {
271272
r, err := s.Client.NewReceivePackSession(s.Endpoint, s.EmptyAuth)
272273
c.Assert(err, IsNil)
273-
defer func() { c.Assert(r.Close(), IsNil) }()
274274

275275
fixture := fixtures.Basic().ByTag("packfile").One()
276276

@@ -285,14 +285,15 @@ func (s *ReceivePackSuite) testSendPackAddReference(c *C) {
285285
req.Capabilities.Set(capability.ReportStatus)
286286
}
287287

288+
c.Assert(r.Close(), IsNil)
289+
288290
s.receivePack(c, s.Endpoint, req, nil, false)
289291
s.checkRemoteReference(c, s.Endpoint, "refs/heads/newbranch", fixture.Head)
290292
}
291293

292294
func (s *ReceivePackSuite) testSendPackDeleteReference(c *C) {
293295
r, err := s.Client.NewReceivePackSession(s.Endpoint, s.EmptyAuth)
294296
c.Assert(err, IsNil)
295-
defer func() { c.Assert(r.Close(), IsNil) }()
296297

297298
fixture := fixtures.Basic().ByTag("packfile").One()
298299

@@ -307,6 +308,8 @@ func (s *ReceivePackSuite) testSendPackDeleteReference(c *C) {
307308
req.Capabilities.Set(capability.ReportStatus)
308309
}
309310

311+
c.Assert(r.Close(), IsNil)
312+
310313
s.receivePack(c, s.Endpoint, req, nil, false)
311314
s.checkRemoteReference(c, s.Endpoint, "refs/heads/newbranch", plumbing.ZeroHash)
312315
}

remote.go

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ func (r *Remote) Push(o *PushOptions) (err error) {
7676
return err
7777
}
7878

79+
defer ioutil.CheckClose(s, &err)
80+
7981
ar, err := s.AdvertisedReferences()
8082
if err != nil {
8183
return err

0 commit comments

Comments
 (0)