aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSantiago M. Mola <santi@mola.io>2017-01-04 11:18:41 +0100
committerGitHub <noreply@github.com>2017-01-04 11:18:41 +0100
commit841abfb7dc640755c443432064252907e3e55c95 (patch)
tree8af69dcd3b301a10a3e493e2cd805cdec6dcaecd
parent90d67bb648ae32d5b1a0f7b1af011da6dfb24315 (diff)
downloadgo-git-841abfb7dc640755c443432064252907e3e55c95.tar.gz
server: add git server implementation (#190)
* server: add generic server implementation (transport-independent), both for git-upload-pack and git-receive-pack. * server: move internal functions to internal/common. * cli: add git-receive-pack and git-upload-pack implementations. * format/packfile: add UpdateObjectStorage function, extracted from Remote. * transport: implement tranport RPC-like, only with git-upload-pack and git-receive-pack methods. Client renamed to Transport. * storer: add storer.Storer interface. * protocol/packp: add UploadPackResponse constructor with packfile. * protocol/packp: fix UploadPackResponse encoding, add tests. * protocol/packp/capability: implement All.
-rw-r--r--cli/go-git/main.go41
-rw-r--r--cli/go-git/receive_pack.go36
-rw-r--r--cli/go-git/upload_pack.go37
-rw-r--r--cli/go-git/version.go14
-rw-r--r--plumbing/format/packfile/common.go30
-rw-r--r--plumbing/protocol/packp/capability/list.go10
-rw-r--r--plumbing/protocol/packp/capability/list_test.go11
-rw-r--r--plumbing/protocol/packp/shallowupd.go19
-rw-r--r--plumbing/protocol/packp/shallowupd_test.go87
-rw-r--r--plumbing/protocol/packp/srvresp.go14
-rw-r--r--plumbing/protocol/packp/updreq_decode.go2
-rw-r--r--plumbing/protocol/packp/uppackresp.go30
-rw-r--r--plumbing/protocol/packp/uppackresp_test.go44
-rw-r--r--plumbing/storer/storer.go7
-rw-r--r--plumbing/transport/client/client.go6
-rw-r--r--plumbing/transport/client/client_test.go8
-rw-r--r--plumbing/transport/common.go50
-rw-r--r--plumbing/transport/file/client.go (renamed from plumbing/transport/file/common.go)2
-rw-r--r--plumbing/transport/file/client_test.go40
-rw-r--r--plumbing/transport/file/common_test.go49
-rw-r--r--plumbing/transport/file/receive_pack_test.go (renamed from plumbing/transport/file/send_pack_test.go)36
-rw-r--r--plumbing/transport/file/server.go51
-rw-r--r--plumbing/transport/file/server_test.go105
-rw-r--r--plumbing/transport/file/upload_pack_test.go (renamed from plumbing/transport/file/fetch_pack_test.go)31
-rw-r--r--plumbing/transport/git/upload_pack_test.go (renamed from plumbing/transport/git/fetch_pack_test.go)16
-rw-r--r--plumbing/transport/http/common.go14
-rw-r--r--plumbing/transport/http/common_test.go8
-rw-r--r--plumbing/transport/http/receive_pack.go30
-rw-r--r--plumbing/transport/http/send_pack.go30
-rw-r--r--plumbing/transport/http/upload_pack.go (renamed from plumbing/transport/http/fetch_pack.go)18
-rw-r--r--plumbing/transport/http/upload_pack_test.go (renamed from plumbing/transport/http/fetch_pack_test.go)23
-rw-r--r--plumbing/transport/internal/common/common.go28
-rw-r--r--plumbing/transport/internal/common/server.go72
-rw-r--r--plumbing/transport/server/loader.go59
-rw-r--r--plumbing/transport/server/loader_test.go57
-rw-r--r--plumbing/transport/server/receive_pack_test.go40
-rw-r--r--plumbing/transport/server/server.go448
-rw-r--r--plumbing/transport/server/server_test.go70
-rw-r--r--plumbing/transport/server/upload_pack_test.go40
-rw-r--r--plumbing/transport/ssh/upload_pack_test.go (renamed from plumbing/transport/ssh/fetch_pack_test.go)16
-rw-r--r--plumbing/transport/test/receive_pack.go (renamed from plumbing/transport/test/send_pack.go)96
-rw-r--r--plumbing/transport/test/upload_pack.go (renamed from plumbing/transport/test/fetch_pack.go)80
-rw-r--r--remote.go44
43 files changed, 1641 insertions, 308 deletions
diff --git a/cli/go-git/main.go b/cli/go-git/main.go
new file mode 100644
index 0000000..97b8c3e
--- /dev/null
+++ b/cli/go-git/main.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/jessevdk/go-flags"
+)
+
+const (
+ bin = "go-git"
+ receivePackBin = "git-receive-pack"
+ uploadPackBin = "git-upload-pack"
+)
+
+func main() {
+ switch filepath.Base(os.Args[0]) {
+ case receivePackBin:
+ os.Args = append([]string{"git", "receive-pack"}, os.Args[1:]...)
+ case uploadPackBin:
+ os.Args = append([]string{"git", "upload-pack"}, os.Args[1:]...)
+ }
+
+ parser := flags.NewNamedParser(bin, flags.Default)
+ parser.AddCommand("receive-pack", "", "", &CmdReceivePack{})
+ parser.AddCommand("upload-pack", "", "", &CmdUploadPack{})
+ parser.AddCommand("version", "Show the version information.", "", &CmdVersion{})
+
+ _, err := parser.Parse()
+ if err != nil {
+ if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrCommandRequired {
+ parser.WriteHelp(os.Stdout)
+ }
+
+ os.Exit(1)
+ }
+}
+
+type cmd struct {
+ Verbose bool `short:"v" description:"Activates the verbose mode"`
+}
diff --git a/cli/go-git/receive_pack.go b/cli/go-git/receive_pack.go
new file mode 100644
index 0000000..0f96a28
--- /dev/null
+++ b/cli/go-git/receive_pack.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/file"
+)
+
+type CmdReceivePack struct {
+ cmd
+
+ Args struct {
+ GitDir string `positional-arg-name:"git-dir" required:"true"`
+ } `positional-args:"yes"`
+}
+
+func (CmdReceivePack) Usage() string {
+ //TODO: git-receive-pack returns error code 129 if arguments are invalid.
+ return fmt.Sprintf("usage: %s <git-dir>", os.Args[0])
+}
+
+func (c *CmdReceivePack) Execute(args []string) error {
+ gitDir, err := filepath.Abs(c.Args.GitDir)
+ if err != nil {
+ return err
+ }
+
+ if err := file.ServeReceivePack(gitDir); err != nil {
+ fmt.Fprintln(os.Stderr, "ERR:", err)
+ os.Exit(128)
+ }
+
+ return nil
+}
diff --git a/cli/go-git/upload_pack.go b/cli/go-git/upload_pack.go
new file mode 100644
index 0000000..c45fe48
--- /dev/null
+++ b/cli/go-git/upload_pack.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/file"
+)
+
+//TODO: usage: git upload-pack [--strict] [--timeout=<n>] <dir>
+type CmdUploadPack struct {
+ cmd
+
+ Args struct {
+ GitDir string `positional-arg-name:"git-dir" required:"true"`
+ } `positional-args:"yes"`
+}
+
+func (CmdUploadPack) Usage() string {
+ //TODO: git-upload-pack returns error code 129 if arguments are invalid.
+ return fmt.Sprintf("usage: %s <git-dir>", os.Args[0])
+}
+
+func (c *CmdUploadPack) Execute(args []string) error {
+ gitDir, err := filepath.Abs(c.Args.GitDir)
+ if err != nil {
+ return err
+ }
+
+ if err := file.ServeUploadPack(gitDir); err != nil {
+ fmt.Fprintln(os.Stderr, "ERR:", err)
+ os.Exit(128)
+ }
+
+ return nil
+}
diff --git a/cli/go-git/version.go b/cli/go-git/version.go
new file mode 100644
index 0000000..b0e773d
--- /dev/null
+++ b/cli/go-git/version.go
@@ -0,0 +1,14 @@
+package main
+
+import "fmt"
+
+var version string
+var build string
+
+type CmdVersion struct{}
+
+func (c *CmdVersion) Execute(args []string) error {
+ fmt.Printf("%s (%s) - build %s\n", bin, version, build)
+
+ return nil
+}
diff --git a/plumbing/format/packfile/common.go b/plumbing/format/packfile/common.go
index 1656551..7da2b33 100644
--- a/plumbing/format/packfile/common.go
+++ b/plumbing/format/packfile/common.go
@@ -1,5 +1,11 @@
package packfile
+import (
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+)
+
var signature = []byte{'P', 'A', 'C', 'K'}
const (
@@ -13,3 +19,27 @@ const (
maskLength = uint8(127) // 0111 1111
maskType = uint8(112) // 0111 0000
)
+
+// UpdateObjectStorage updates the given ObjectStorer with the contents of the
+// packfile.
+func UpdateObjectStorage(s storer.EncodedObjectStorer, packfile io.Reader) error {
+ if sw, ok := s.(storer.PackfileWriter); ok {
+ w, err := sw.PackfileWriter()
+ if err != nil {
+ return err
+ }
+
+ defer w.Close()
+ _, err = io.Copy(w, packfile)
+ return err
+ }
+
+ stream := NewScanner(packfile)
+ d, err := NewDecoder(stream, s)
+ if err != nil {
+ return err
+ }
+
+ _, err = d.Decode()
+ return err
+}
diff --git a/plumbing/protocol/packp/capability/list.go b/plumbing/protocol/packp/capability/list.go
index 2847580..69fdb51 100644
--- a/plumbing/protocol/packp/capability/list.go
+++ b/plumbing/protocol/packp/capability/list.go
@@ -167,6 +167,16 @@ func (l *List) Delete(capability Capability) {
}
}
+// All returns a slice with all defined capabilities.
+func (l *List) All() []Capability {
+ var cs []Capability
+ for _, key := range l.sort {
+ cs = append(cs, Capability(key))
+ }
+
+ return cs
+}
+
// String generates the capabilities strings, the capabilities are sorted in
// insertion order
func (l *List) String() string {
diff --git a/plumbing/protocol/packp/capability/list_test.go b/plumbing/protocol/packp/capability/list_test.go
index 42f0179..0a0ad26 100644
--- a/plumbing/protocol/packp/capability/list_test.go
+++ b/plumbing/protocol/packp/capability/list_test.go
@@ -171,3 +171,14 @@ func (s *SuiteCapabilities) TestAddErrMultipleArgumentsAtTheSameTime(c *check.C)
err := cap.Add(Agent, "foo", "bar")
c.Assert(err, check.Equals, ErrMultipleArguments)
}
+
+func (s *SuiteCapabilities) TestAll(c *check.C) {
+ cap := NewList()
+ c.Assert(NewList().All(), check.IsNil)
+
+ cap.Add(Agent, "foo")
+ c.Assert(cap.All(), check.DeepEquals, []Capability{Agent})
+
+ cap.Add(OFSDelta)
+ c.Assert(cap.All(), check.DeepEquals, []Capability{Agent, OFSDelta})
+}
diff --git a/plumbing/protocol/packp/shallowupd.go b/plumbing/protocol/packp/shallowupd.go
index 89063de..40f58e8 100644
--- a/plumbing/protocol/packp/shallowupd.go
+++ b/plumbing/protocol/packp/shallowupd.go
@@ -24,6 +24,7 @@ func (r *ShallowUpdate) Decode(reader io.Reader) error {
for s.Scan() {
line := s.Bytes()
+ line = bytes.TrimSpace(line)
var err error
switch {
@@ -71,3 +72,21 @@ func (r *ShallowUpdate) decodeLine(line, prefix []byte, expLen int) (plumbing.Ha
raw := string(line[expLen-40 : expLen])
return plumbing.NewHash(raw), nil
}
+
+func (r *ShallowUpdate) Encode(w io.Writer) error {
+ e := pktline.NewEncoder(w)
+
+ for _, h := range r.Shallows {
+ if err := e.Encodef("%s%s\n", shallow, h.String()); err != nil {
+ return err
+ }
+ }
+
+ for _, h := range r.Unshallows {
+ if err := e.Encodef("%s%s\n", unshallow, h.String()); err != nil {
+ return err
+ }
+ }
+
+ return e.Flush()
+}
diff --git a/plumbing/protocol/packp/shallowupd_test.go b/plumbing/protocol/packp/shallowupd_test.go
index d64fb5d..97a13fc 100644
--- a/plumbing/protocol/packp/shallowupd_test.go
+++ b/plumbing/protocol/packp/shallowupd_test.go
@@ -12,6 +12,26 @@ type ShallowUpdateSuite struct{}
var _ = Suite(&ShallowUpdateSuite{})
+func (s *ShallowUpdateSuite) TestDecodeWithLF(c *C) {
+ raw := "" +
+ "0035shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
+ "0035shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" +
+ "0000"
+
+ su := &ShallowUpdate{}
+ err := su.Decode(bytes.NewBufferString(raw))
+ c.Assert(err, IsNil)
+
+ plumbing.HashesSort(su.Shallows)
+
+ c.Assert(su.Unshallows, HasLen, 0)
+ c.Assert(su.Shallows, HasLen, 2)
+ c.Assert(su.Shallows, DeepEquals, []plumbing.Hash{
+ plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+ plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
+ })
+}
+
func (s *ShallowUpdateSuite) TestDecode(c *C) {
raw := "" +
"0034shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
@@ -61,3 +81,70 @@ func (s *ShallowUpdateSuite) TestDecodeMalformed(c *C) {
err := su.Decode(bytes.NewBufferString(raw))
c.Assert(err, NotNil)
}
+
+func (s *ShallowUpdateSuite) TestEncodeEmpty(c *C) {
+ su := &ShallowUpdate{}
+ buf := bytes.NewBuffer(nil)
+ c.Assert(su.Encode(buf), IsNil)
+ c.Assert(buf.String(), Equals, "0000")
+}
+
+func (s *ShallowUpdateSuite) TestEncode(c *C) {
+ su := &ShallowUpdate{
+ Shallows: []plumbing.Hash{
+ plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+ plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
+ },
+ Unshallows: []plumbing.Hash{
+ plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+ plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
+ },
+ }
+ buf := bytes.NewBuffer(nil)
+ c.Assert(su.Encode(buf), IsNil)
+
+ expected := "" +
+ "0035shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
+ "0035shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" +
+ "0037unshallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
+ "0037unshallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" +
+ "0000"
+
+ c.Assert(buf.String(), Equals, expected)
+}
+
+func (s *ShallowUpdateSuite) TestEncodeShallow(c *C) {
+ su := &ShallowUpdate{
+ Shallows: []plumbing.Hash{
+ plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+ plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
+ },
+ }
+ buf := bytes.NewBuffer(nil)
+ c.Assert(su.Encode(buf), IsNil)
+
+ expected := "" +
+ "0035shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
+ "0035shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" +
+ "0000"
+
+ c.Assert(buf.String(), Equals, expected)
+}
+
+func (s *ShallowUpdateSuite) TestEncodeUnshallow(c *C) {
+ su := &ShallowUpdate{
+ Unshallows: []plumbing.Hash{
+ plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+ plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
+ },
+ }
+ buf := bytes.NewBuffer(nil)
+ c.Assert(su.Encode(buf), IsNil)
+
+ expected := "" +
+ "0037unshallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
+ "0037unshallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" +
+ "0000"
+
+ c.Assert(buf.String(), Equals, expected)
+}
diff --git a/plumbing/protocol/packp/srvresp.go b/plumbing/protocol/packp/srvresp.go
index 3284fa2..d41b50d 100644
--- a/plumbing/protocol/packp/srvresp.go
+++ b/plumbing/protocol/packp/srvresp.go
@@ -68,3 +68,17 @@ func (r *ServerResponse) decodeACKLine(line []byte) error {
r.ACKs = append(r.ACKs, h)
return nil
}
+
+// Encode encodes the ServerResponse into a writer.
+func (r *ServerResponse) Encode(w io.Writer) error {
+ if len(r.ACKs) > 1 {
+ return errors.New("multi_ack and multi_ack_detailed are not supported")
+ }
+
+ e := pktline.NewEncoder(w)
+ if len(r.ACKs) == 0 {
+ return e.Encodef("%s\n", nak)
+ }
+
+ return e.Encodef("%s %s\n", ack, r.ACKs[0].String())
+}
diff --git a/plumbing/protocol/packp/updreq_decode.go b/plumbing/protocol/packp/updreq_decode.go
index 51e8183..c15d49c 100644
--- a/plumbing/protocol/packp/updreq_decode.go
+++ b/plumbing/protocol/packp/updreq_decode.go
@@ -225,7 +225,7 @@ func parseCommand(b []byte) (*Command, error) {
return nil, errInvalidNewObjId(err)
}
- return &Command{Old: oh, New: nh, Name: n}, nil
+ return &Command{Old: oh, New: nh, Name: plumbing.ReferenceName(n)}, nil
}
func parseHash(s string) (plumbing.Hash, error) {
diff --git a/plumbing/protocol/packp/uppackresp.go b/plumbing/protocol/packp/uppackresp.go
index a117956..ac456f3 100644
--- a/plumbing/protocol/packp/uppackresp.go
+++ b/plumbing/protocol/packp/uppackresp.go
@@ -5,6 +5,7 @@ import (
"io"
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
+ "gopkg.in/src-d/go-git.v4/utils/ioutil"
)
// ErrUploadPackResponseNotDecoded is returned if Read is called without
@@ -17,8 +18,8 @@ var ErrUploadPackResponseNotDecoded = errors.New("upload-pack-response should be
type UploadPackResponse struct {
ShallowUpdate
ServerResponse
- r io.ReadCloser
+ r io.ReadCloser
isShallow bool
isMultiACK bool
isOk bool
@@ -37,6 +38,16 @@ func NewUploadPackResponse(req *UploadPackRequest) *UploadPackResponse {
}
}
+// NewUploadPackResponseWithPackfile creates a new UploadPackResponse instance,
+// and sets its packfile reader.
+func NewUploadPackResponseWithPackfile(req *UploadPackRequest,
+ pf io.ReadCloser) *UploadPackResponse {
+
+ r := NewUploadPackResponse(req)
+ r.r = pf
+ return r
+}
+
// Decode decodes all the responses sent by upload-pack service into the struct
// and prepares it to read the packfile using the Read method
func (r *UploadPackResponse) Decode(reader io.ReadCloser) error {
@@ -56,6 +67,23 @@ func (r *UploadPackResponse) Decode(reader io.ReadCloser) error {
return nil
}
+// Encode encodes an UploadPackResponse.
+func (r *UploadPackResponse) Encode(w io.Writer) (err error) {
+ if r.isShallow {
+ if err := r.ShallowUpdate.Encode(w); err != nil {
+ return err
+ }
+ }
+
+ if err := r.ServerResponse.Encode(w); err != nil {
+ return err
+ }
+
+ defer ioutil.CheckClose(r.r, &err)
+ _, err = io.Copy(w, r.r)
+ return err
+}
+
// Read reads the packfile data, if the request was done with any Sideband
// capability the content read should be demultiplexed. If the methods wasn't
// called before the ErrUploadPackResponseNotDecoded will be return
diff --git a/plumbing/protocol/packp/uppackresp_test.go b/plumbing/protocol/packp/uppackresp_test.go
index c81bb76..c27fdda 100644
--- a/plumbing/protocol/packp/uppackresp_test.go
+++ b/plumbing/protocol/packp/uppackresp_test.go
@@ -7,6 +7,7 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
. "gopkg.in/check.v1"
+ "gopkg.in/src-d/go-git.v4/plumbing"
)
type UploadPackResponseSuite struct{}
@@ -80,3 +81,46 @@ func (s *UploadPackResponseSuite) TestReadNoDecode(c *C) {
c.Assert(err, Equals, ErrUploadPackResponseNotDecoded)
c.Assert(n, Equals, 0)
}
+
+func (s *UploadPackResponseSuite) TestEncodeNAK(c *C) {
+ pf := ioutil.NopCloser(bytes.NewBuffer([]byte("[PACK]")))
+ req := NewUploadPackRequest()
+ res := NewUploadPackResponseWithPackfile(req, pf)
+ defer func() { c.Assert(res.Close(), IsNil) }()
+
+ b := bytes.NewBuffer(nil)
+ c.Assert(res.Encode(b), IsNil)
+
+ expected := "0008NAK\n[PACK]"
+ c.Assert(string(b.Bytes()), Equals, expected)
+}
+
+func (s *UploadPackResponseSuite) TestEncodeDepth(c *C) {
+ pf := ioutil.NopCloser(bytes.NewBuffer([]byte("[PACK]")))
+ req := NewUploadPackRequest()
+ req.Depth = DepthCommits(1)
+
+ res := NewUploadPackResponseWithPackfile(req, pf)
+ defer func() { c.Assert(res.Close(), IsNil) }()
+
+ b := bytes.NewBuffer(nil)
+ c.Assert(res.Encode(b), IsNil)
+
+ expected := "00000008NAK\n[PACK]"
+ c.Assert(string(b.Bytes()), Equals, expected)
+}
+
+func (s *UploadPackResponseSuite) TestEncodeMultiACK(c *C) {
+ pf := ioutil.NopCloser(bytes.NewBuffer([]byte("[PACK]")))
+ req := NewUploadPackRequest()
+
+ res := NewUploadPackResponseWithPackfile(req, pf)
+ defer func() { c.Assert(res.Close(), IsNil) }()
+ res.ACKs = []plumbing.Hash{
+ plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f81"),
+ plumbing.NewHash("5dc01c595e6c6ec9ccda4f6f69c131c0dd945f82"),
+ }
+
+ b := bytes.NewBuffer(nil)
+ c.Assert(res.Encode(b), NotNil)
+}
diff --git a/plumbing/storer/storer.go b/plumbing/storer/storer.go
new file mode 100644
index 0000000..0b96c0e
--- /dev/null
+++ b/plumbing/storer/storer.go
@@ -0,0 +1,7 @@
+package storer
+
+// Storer is a basic storer for encoded objects and references.
+type Storer interface {
+ EncodedObjectStorer
+ ReferenceStorer
+}
diff --git a/plumbing/transport/client/client.go b/plumbing/transport/client/client.go
index 095c51d..4615368 100644
--- a/plumbing/transport/client/client.go
+++ b/plumbing/transport/client/client.go
@@ -11,7 +11,7 @@ import (
)
// Protocols are the protocols supported by default.
-var Protocols = map[string]transport.Client{
+var Protocols = map[string]transport.Transport{
"http": http.DefaultClient,
"https": http.DefaultClient,
"ssh": ssh.DefaultClient,
@@ -20,14 +20,14 @@ var Protocols = map[string]transport.Client{
}
// InstallProtocol adds or modifies an existing protocol.
-func InstallProtocol(scheme string, c transport.Client) {
+func InstallProtocol(scheme string, c transport.Transport) {
Protocols[scheme] = c
}
// NewClient returns the appropriate client among of the set of known protocols:
// http://, https://, ssh:// and file://.
// See `InstallProtocol` to add or modify protocols.
-func NewClient(endpoint transport.Endpoint) (transport.Client, error) {
+func NewClient(endpoint transport.Endpoint) (transport.Transport, error) {
f, ok := Protocols[endpoint.Scheme]
if !ok {
return nil, fmt.Errorf("unsupported scheme %q", endpoint.Scheme)
diff --git a/plumbing/transport/client/client_test.go b/plumbing/transport/client/client_test.go
index a0c8208..9af70b1 100644
--- a/plumbing/transport/client/client_test.go
+++ b/plumbing/transport/client/client_test.go
@@ -58,13 +58,13 @@ type dummyClient struct {
*http.Client
}
-func (*dummyClient) NewFetchPackSession(transport.Endpoint) (
- transport.FetchPackSession, error) {
+func (*dummyClient) NewUploadPackSession(transport.Endpoint) (
+ transport.UploadPackSession, error) {
return nil, nil
}
-func (*dummyClient) NewSendPackSession(transport.Endpoint) (
- transport.SendPackSession, error) {
+func (*dummyClient) NewReceivePackSession(transport.Endpoint) (
+ transport.ReceivePackSession, error) {
return nil, nil
}
diff --git a/plumbing/transport/common.go b/plumbing/transport/common.go
index 83fd5a3..00e8a30 100644
--- a/plumbing/transport/common.go
+++ b/plumbing/transport/common.go
@@ -37,21 +37,23 @@ const (
ReceivePackServiceName = "git-receive-pack"
)
-// Client can initiate git-fetch-pack and git-send-pack processes.
-type Client interface {
- // NewFetchPackSession starts a git-fetch-pack session for an endpoint.
- NewFetchPackSession(Endpoint) (FetchPackSession, error)
- // NewSendPackSession starts a git-send-pack session for an endpoint.
- NewSendPackSession(Endpoint) (SendPackSession, error)
+// Transport can initiate git-upload-pack and git-receive-pack processes.
+// It is implemented both by the client and the server, making this a RPC.
+type Transport interface {
+ // NewUploadPackSession starts a git-upload-pack session for an endpoint.
+ NewUploadPackSession(Endpoint) (UploadPackSession, error)
+ // NewReceivePackSession starts a git-receive-pack session for an endpoint.
+ NewReceivePackSession(Endpoint) (ReceivePackSession, error)
}
type Session interface {
- SetAuth(auth AuthMethod) error
// AdvertisedReferences retrieves the advertised references for a
// repository.
// If the repository does not exist, returns ErrRepositoryNotFound.
// If the repository exists, but is empty, returns ErrEmptyRemoteRepository.
AdvertisedReferences() (*packp.AdvRefs, error)
+ //TODO: Move to Client level.
+ SetAuth(auth AuthMethod) error
io.Closer
}
@@ -60,26 +62,30 @@ type AuthMethod interface {
Name() string
}
-// FetchPackSession represents a git-fetch-pack session.
-// A git-fetch-pack session has two steps: reference discovery
-// (`AdvertisedReferences` function) and fetching pack (`FetchPack` function).
-// In that order.
-type FetchPackSession interface {
+// UploadPackSession represents a git-upload-pack session.
+// A git-upload-pack session has two steps: reference discovery
+// (AdvertisedReferences) and uploading pack (UploadPack).
+type UploadPackSession interface {
Session
- // FetchPack takes a request and returns a reader for the packfile
- // received from the server.
- FetchPack(*packp.UploadPackRequest) (*packp.UploadPackResponse, error)
+ // UploadPack takes a git-upload-pack request and returns a response,
+ // including a packfile. Don't be confused by terminology, the client
+ // side of a git-upload-pack is called git-fetch-pack, although here
+ // the same interface is used to make it RPC-like.
+ UploadPack(*packp.UploadPackRequest) (*packp.UploadPackResponse, error)
}
-// SendPackSession represents a git-send-pack session.
-// A git-send-pack session has two steps: reference discovery
-// (`AdvertisedReferences` function) and sending pack (`SendPack` function).
+// ReceivePackSession represents a git-receive-pack session.
+// A git-receive-pack session has two steps: reference discovery
+// (AdvertisedReferences) and receiving pack (ReceivePack).
// In that order.
-type SendPackSession interface {
+type ReceivePackSession interface {
Session
- // UpdateReferences sends an update references request and a packfile
- // reader and returns a ReportStatus and error.
- SendPack(*packp.ReferenceUpdateRequest) (*packp.ReportStatus, error)
+ // ReceivePack sends an update references request and a packfile
+ // reader and returns a ReportStatus and error. Don't be confused by
+ // terminology, the client side of a git-receive-pack is called
+ // git-send-pack, although here the same interface is used to make it
+ // RPC-like.
+ ReceivePack(*packp.ReferenceUpdateRequest) (*packp.ReportStatus, error)
}
type Endpoint url.URL
diff --git a/plumbing/transport/file/common.go b/plumbing/transport/file/client.go
index e7d18b2..5484009 100644
--- a/plumbing/transport/file/common.go
+++ b/plumbing/transport/file/client.go
@@ -21,7 +21,7 @@ type runner struct {
// NewClient returns a new local client using the given git-upload-pack and
// git-receive-pack binaries.
-func NewClient(uploadPackBin, receivePackBin string) transport.Client {
+func NewClient(uploadPackBin, receivePackBin string) transport.Transport {
return common.NewClient(&runner{
UploadPackBin: uploadPackBin,
ReceivePackBin: receivePackBin,
diff --git a/plumbing/transport/file/client_test.go b/plumbing/transport/file/client_test.go
new file mode 100644
index 0000000..220df3d
--- /dev/null
+++ b/plumbing/transport/file/client_test.go
@@ -0,0 +1,40 @@
+package file
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "testing"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+
+ . "gopkg.in/check.v1"
+)
+
+func Test(t *testing.T) { TestingT(t) }
+
+const bareConfig = `[core]
+repositoryformatversion = 0
+filemode = true
+bare = true`
+
+func prepareRepo(c *C, path string) transport.Endpoint {
+ url := fmt.Sprintf("file://%s", path)
+ ep, err := transport.NewEndpoint(url)
+ c.Assert(err, IsNil)
+
+ // git-receive-pack refuses to update refs/heads/master on non-bare repo
+ // so we ensure bare repo config.
+ config := fmt.Sprintf("%s/config", path)
+ if _, err := os.Stat(config); err == nil {
+ f, err := os.OpenFile(config, os.O_TRUNC|os.O_WRONLY, 0)
+ c.Assert(err, IsNil)
+ content := strings.NewReader(bareConfig)
+ _, err = io.Copy(f, content)
+ c.Assert(err, IsNil)
+ c.Assert(f.Close(), IsNil)
+ }
+
+ return ep
+}
diff --git a/plumbing/transport/file/common_test.go b/plumbing/transport/file/common_test.go
index 220df3d..cd7c400 100644
--- a/plumbing/transport/file/common_test.go
+++ b/plumbing/transport/file/common_test.go
@@ -1,40 +1,39 @@
package file
import (
- "fmt"
- "io"
"os"
- "strings"
- "testing"
+ "os/exec"
+ "path/filepath"
- "gopkg.in/src-d/go-git.v4/plumbing/transport"
+ "gopkg.in/src-d/go-git.v4/fixtures"
. "gopkg.in/check.v1"
+ "io/ioutil"
)
-func Test(t *testing.T) { TestingT(t) }
+type CommonSuite struct {
+ fixtures.Suite
+ ReceivePackBin string
+ UploadPackBin string
+}
-const bareConfig = `[core]
-repositoryformatversion = 0
-filemode = true
-bare = true`
+var _ = Suite(&CommonSuite{})
-func prepareRepo(c *C, path string) transport.Endpoint {
- url := fmt.Sprintf("file://%s", path)
- ep, err := transport.NewEndpoint(url)
- c.Assert(err, IsNil)
+func (s *CommonSuite) SetUpSuite(c *C) {
+ s.Suite.SetUpSuite(c)
- // git-receive-pack refuses to update refs/heads/master on non-bare repo
- // so we ensure bare repo config.
- config := fmt.Sprintf("%s/config", path)
- if _, err := os.Stat(config); err == nil {
- f, err := os.OpenFile(config, os.O_TRUNC|os.O_WRONLY, 0)
- c.Assert(err, IsNil)
- content := strings.NewReader(bareConfig)
- _, err = io.Copy(f, content)
- c.Assert(err, IsNil)
- c.Assert(f.Close(), IsNil)
+ if err := exec.Command("git", "--version").Run(); err != nil {
+ c.Skip("git command not found")
}
- return ep
+ binDir, err := ioutil.TempDir(os.TempDir(), "")
+ c.Assert(err, IsNil)
+ s.ReceivePackBin = filepath.Join(binDir, "git-receive-pack")
+ s.UploadPackBin = filepath.Join(binDir, "git-upload-pack")
+ bin := filepath.Join(binDir, "go-git")
+ cmd := exec.Command("go", "build", "-o", bin,
+ "../../../cli/go-git/...")
+ c.Assert(cmd.Run(), IsNil)
+ c.Assert(os.Symlink(bin, s.ReceivePackBin), IsNil)
+ c.Assert(os.Symlink(bin, s.UploadPackBin), IsNil)
}
diff --git a/plumbing/transport/file/send_pack_test.go b/plumbing/transport/file/receive_pack_test.go
index fc7ea35..c07d4ed 100644
--- a/plumbing/transport/file/send_pack_test.go
+++ b/plumbing/transport/file/receive_pack_test.go
@@ -2,7 +2,6 @@ package file
import (
"os"
- "os/exec"
"gopkg.in/src-d/go-git.v4/fixtures"
"gopkg.in/src-d/go-git.v4/plumbing/transport/test"
@@ -10,24 +9,19 @@ import (
. "gopkg.in/check.v1"
)
-type SendPackSuite struct {
- fixtures.Suite
- test.SendPackSuite
+type ReceivePackSuite struct {
+ CommonSuite
+ test.ReceivePackSuite
}
-var _ = Suite(&SendPackSuite{})
+var _ = Suite(&ReceivePackSuite{})
-func (s *SendPackSuite) SetUpSuite(c *C) {
- s.Suite.SetUpSuite(c)
-
- if err := exec.Command("git", "--version").Run(); err != nil {
- c.Skip("git command not found")
- }
-
- s.SendPackSuite.Client = DefaultClient
+func (s *ReceivePackSuite) SetUpSuite(c *C) {
+ s.CommonSuite.SetUpSuite(c)
+ s.ReceivePackSuite.Client = DefaultClient
}
-func (s *SendPackSuite) SetUpTest(c *C) {
+func (s *ReceivePackSuite) SetUpTest(c *C) {
fixture := fixtures.Basic().One()
path := fixture.DotGit().Base()
s.Endpoint = prepareRepo(c, path)
@@ -39,12 +33,12 @@ func (s *SendPackSuite) SetUpTest(c *C) {
s.NonExistentEndpoint = prepareRepo(c, "/non-existent")
}
-func (s *SendPackSuite) TearDownTest(c *C) {
+func (s *ReceivePackSuite) TearDownTest(c *C) {
s.Suite.TearDownSuite(c)
}
// TODO: fix test
-func (s *SendPackSuite) TestCommandNoOutput(c *C) {
+func (s *ReceivePackSuite) TestCommandNoOutput(c *C) {
c.Skip("failing test")
if _, err := os.Stat("/bin/true"); os.IsNotExist(err) {
@@ -52,30 +46,30 @@ func (s *SendPackSuite) TestCommandNoOutput(c *C) {
}
client := NewClient("true", "true")
- session, err := client.NewSendPackSession(s.Endpoint)
+ session, err := client.NewReceivePackSession(s.Endpoint)
c.Assert(err, IsNil)
ar, err := session.AdvertisedReferences()
c.Assert(err, IsNil)
c.Assert(ar, IsNil)
}
-func (s *SendPackSuite) TestMalformedInputNoErrors(c *C) {
+func (s *ReceivePackSuite) TestMalformedInputNoErrors(c *C) {
if _, err := os.Stat("/usr/bin/yes"); os.IsNotExist(err) {
c.Skip("/usr/bin/yes not found")
}
client := NewClient("yes", "yes")
- session, err := client.NewSendPackSession(s.Endpoint)
+ session, err := client.NewReceivePackSession(s.Endpoint)
c.Assert(err, IsNil)
ar, err := session.AdvertisedReferences()
c.Assert(err, NotNil)
c.Assert(ar, IsNil)
}
-func (s *SendPackSuite) TestNonExistentCommand(c *C) {
+func (s *ReceivePackSuite) TestNonExistentCommand(c *C) {
cmd := "/non-existent-git"
client := NewClient(cmd, cmd)
- session, err := client.NewSendPackSession(s.Endpoint)
+ session, err := client.NewReceivePackSession(s.Endpoint)
c.Assert(err, ErrorMatches, ".*no such file or directory.*")
c.Assert(session, IsNil)
}
diff --git a/plumbing/transport/file/server.go b/plumbing/transport/file/server.go
new file mode 100644
index 0000000..d83d5d9
--- /dev/null
+++ b/plumbing/transport/file/server.go
@@ -0,0 +1,51 @@
+package file
+
+import (
+ "fmt"
+ "os"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/server"
+ "gopkg.in/src-d/go-git.v4/utils/ioutil"
+)
+
+// ServeUploadPack serves a git-upload-pack request using standard output, input
+// and error. This is meant to be used when implementing a git-upload-pack
+// command.
+func ServeUploadPack(path string) error {
+ ep, err := transport.NewEndpoint(fmt.Sprintf("file://%s", path))
+ if err != nil {
+ return err
+ }
+
+ s, err := server.DefaultServer.NewUploadPackSession(ep)
+ if err != nil {
+ return fmt.Errorf("error creating session: %s", err)
+ }
+
+ return common.ServeUploadPack(srvCmd, s)
+}
+
+// ServeReceivePack serves a git-receive-pack request using standard output,
+// input and error. This is meant to be used when implementing a
+// git-receive-pack command.
+func ServeReceivePack(path string) error {
+ ep, err := transport.NewEndpoint(fmt.Sprintf("file://%s", path))
+ if err != nil {
+ return err
+ }
+
+ s, err := server.DefaultServer.NewReceivePackSession(ep)
+ if err != nil {
+ return fmt.Errorf("error creating session: %s", err)
+ }
+
+ return common.ServeReceivePack(srvCmd, s)
+}
+
+var srvCmd = common.ServerCommand{
+ Stdin: os.Stdin,
+ Stdout: ioutil.WriteNopCloser(os.Stdout),
+ Stderr: os.Stderr,
+}
diff --git a/plumbing/transport/file/server_test.go b/plumbing/transport/file/server_test.go
new file mode 100644
index 0000000..ff462e2
--- /dev/null
+++ b/plumbing/transport/file/server_test.go
@@ -0,0 +1,105 @@
+package file
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+
+ "gopkg.in/src-d/go-git.v4/fixtures"
+
+ . "gopkg.in/check.v1"
+)
+
+type ServerSuite struct {
+ CommonSuite
+ RemoteName string
+ SrcPath string
+ DstPath string
+ DstURL string
+}
+
+var _ = Suite(&ServerSuite{})
+
+func (s *ServerSuite) SetUpSuite(c *C) {
+ s.CommonSuite.SetUpSuite(c)
+
+ s.RemoteName = "test"
+
+ fixture := fixtures.Basic().One()
+ s.SrcPath = fixture.DotGit().Base()
+
+ fixture = fixtures.ByTag("empty").One()
+ s.DstPath = fixture.DotGit().Base()
+ s.DstURL = fmt.Sprintf("file://%s", s.DstPath)
+
+ cmd := exec.Command("git", "remote", "add", s.RemoteName, s.DstURL)
+ cmd.Dir = s.SrcPath
+ c.Assert(cmd.Run(), IsNil)
+}
+
+func (s *ServerSuite) TestPush(c *C) {
+ // git <2.0 cannot push to an empty repository without a refspec.
+ cmd := exec.Command("git", "push",
+ "--receive-pack", s.ReceivePackBin,
+ s.RemoteName, "refs/heads/*:refs/heads/*",
+ )
+ cmd.Dir = s.SrcPath
+ cmd.Env = os.Environ()
+ cmd.Env = append(cmd.Env, "GIT_TRACE=true", "GIT_TRACE_PACKET=true")
+ stdout, stderr, err := execAndGetOutput(c, cmd)
+ c.Assert(err, IsNil, Commentf("STDOUT:\n%s\nSTDERR:\n%s\n", stdout, stderr))
+}
+
+func (s *ServerSuite) TestClone(c *C) {
+ pathToClone := c.MkDir()
+
+ cmd := exec.Command("git", "clone",
+ "--upload-pack", s.UploadPackBin,
+ s.SrcPath, pathToClone,
+ )
+ cmd.Env = os.Environ()
+ cmd.Env = append(cmd.Env, "GIT_TRACE=true", "GIT_TRACE_PACKET=true")
+ stdout, stderr, err := execAndGetOutput(c, cmd)
+ c.Assert(err, IsNil, Commentf("STDOUT:\n%s\nSTDERR:\n%s\n", stdout, stderr))
+}
+
+func execAndGetOutput(c *C, cmd *exec.Cmd) (stdout, stderr string, err error) {
+ sout, err := cmd.StdoutPipe()
+ c.Assert(err, IsNil)
+ serr, err := cmd.StderrPipe()
+ c.Assert(err, IsNil)
+
+ outChan, outErr := readAllAsync(sout)
+ errChan, errErr := readAllAsync(serr)
+
+ c.Assert(cmd.Start(), IsNil)
+
+ if err = cmd.Wait(); err != nil {
+ return <-outChan, <-errChan, err
+ }
+
+ if err := <-outErr; err != nil {
+ return <-outChan, <-errChan, err
+ }
+
+ return <-outChan, <-errChan, <-errErr
+}
+
+func readAllAsync(r io.Reader) (out chan string, err chan error) {
+ out = make(chan string, 1)
+ err = make(chan error, 1)
+ go func() {
+ b, e := ioutil.ReadAll(r)
+ if e != nil {
+ err <- e
+ } else {
+ err <- nil
+ }
+
+ out <- string(b)
+ }()
+
+ return out, err
+}
diff --git a/plumbing/transport/file/fetch_pack_test.go b/plumbing/transport/file/upload_pack_test.go
index 25e3fef..de232c4 100644
--- a/plumbing/transport/file/fetch_pack_test.go
+++ b/plumbing/transport/file/upload_pack_test.go
@@ -3,7 +3,6 @@ package file
import (
"fmt"
"os"
- "os/exec"
"gopkg.in/src-d/go-git.v4/fixtures"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
@@ -12,21 +11,17 @@ import (
. "gopkg.in/check.v1"
)
-type FetchPackSuite struct {
- fixtures.Suite
- test.FetchPackSuite
+type UploadPackSuite struct {
+ CommonSuite
+ test.UploadPackSuite
}
-var _ = Suite(&FetchPackSuite{})
+var _ = Suite(&UploadPackSuite{})
-func (s *FetchPackSuite) SetUpSuite(c *C) {
- s.Suite.SetUpSuite(c)
+func (s *UploadPackSuite) SetUpSuite(c *C) {
+ s.CommonSuite.SetUpSuite(c)
- if err := exec.Command("git", "--version").Run(); err != nil {
- c.Skip("git command not found")
- }
-
- s.FetchPackSuite.Client = DefaultClient
+ s.UploadPackSuite.Client = DefaultClient
fixture := fixtures.Basic().One()
path := fixture.DotGit().Base()
@@ -49,7 +44,7 @@ func (s *FetchPackSuite) SetUpSuite(c *C) {
}
// TODO: fix test
-func (s *FetchPackSuite) TestCommandNoOutput(c *C) {
+func (s *UploadPackSuite) TestCommandNoOutput(c *C) {
c.Skip("failing test")
if _, err := os.Stat("/bin/true"); os.IsNotExist(err) {
@@ -57,30 +52,30 @@ func (s *FetchPackSuite) TestCommandNoOutput(c *C) {
}
client := NewClient("true", "true")
- session, err := client.NewFetchPackSession(s.Endpoint)
+ session, err := client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
ar, err := session.AdvertisedReferences()
c.Assert(err, IsNil)
c.Assert(ar, IsNil)
}
-func (s *FetchPackSuite) TestMalformedInputNoErrors(c *C) {
+func (s *UploadPackSuite) TestMalformedInputNoErrors(c *C) {
if _, err := os.Stat("/usr/bin/yes"); os.IsNotExist(err) {
c.Skip("/usr/bin/yes not found")
}
client := NewClient("yes", "yes")
- session, err := client.NewFetchPackSession(s.Endpoint)
+ session, err := client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
ar, err := session.AdvertisedReferences()
c.Assert(err, NotNil)
c.Assert(ar, IsNil)
}
-func (s *FetchPackSuite) TestNonExistentCommand(c *C) {
+func (s *UploadPackSuite) TestNonExistentCommand(c *C) {
cmd := "/non-existent-git"
client := NewClient(cmd, cmd)
- session, err := client.NewFetchPackSession(s.Endpoint)
+ session, err := client.NewUploadPackSession(s.Endpoint)
c.Assert(err, ErrorMatches, ".*no such file or directory.*")
c.Assert(session, IsNil)
}
diff --git a/plumbing/transport/git/fetch_pack_test.go b/plumbing/transport/git/upload_pack_test.go
index dc40240..d506075 100644
--- a/plumbing/transport/git/fetch_pack_test.go
+++ b/plumbing/transport/git/upload_pack_test.go
@@ -8,28 +8,28 @@ import (
. "gopkg.in/check.v1"
)
-type FetchPackSuite struct {
- test.FetchPackSuite
+type UploadPackSuite struct {
+ test.UploadPackSuite
fixtures.Suite
}
-var _ = Suite(&FetchPackSuite{})
+var _ = Suite(&UploadPackSuite{})
-func (s *FetchPackSuite) SetUpSuite(c *C) {
+func (s *UploadPackSuite) SetUpSuite(c *C) {
s.Suite.SetUpSuite(c)
- s.FetchPackSuite.Client = DefaultClient
+ s.UploadPackSuite.Client = DefaultClient
ep, err := transport.NewEndpoint("git://github.com/git-fixtures/basic.git")
c.Assert(err, IsNil)
- s.FetchPackSuite.Endpoint = ep
+ s.UploadPackSuite.Endpoint = ep
ep, err = transport.NewEndpoint("git://github.com/git-fixtures/empty.git")
c.Assert(err, IsNil)
- s.FetchPackSuite.EmptyEndpoint = ep
+ s.UploadPackSuite.EmptyEndpoint = ep
ep, err = transport.NewEndpoint("git://github.com/git-fixtures/non-existent.git")
c.Assert(err, IsNil)
- s.FetchPackSuite.NonExistentEndpoint = ep
+ s.UploadPackSuite.NonExistentEndpoint = ep
}
diff --git a/plumbing/transport/http/common.go b/plumbing/transport/http/common.go
index aa3425e..957fd07 100644
--- a/plumbing/transport/http/common.go
+++ b/plumbing/transport/http/common.go
@@ -25,7 +25,7 @@ var DefaultClient = NewClient(nil)
// Note that for HTTP client cannot distinguist between private repositories and
// unexistent repositories on GitHub. So it returns `ErrAuthorizationRequired`
// for both.
-func NewClient(c *http.Client) transport.Client {
+func NewClient(c *http.Client) transport.Transport {
if c == nil {
return &client{http.DefaultClient}
}
@@ -35,16 +35,16 @@ func NewClient(c *http.Client) transport.Client {
}
}
-func (c *client) NewFetchPackSession(ep transport.Endpoint) (
- transport.FetchPackSession, error) {
+func (c *client) NewUploadPackSession(ep transport.Endpoint) (
+ transport.UploadPackSession, error) {
- return newFetchPackSession(c.c, ep), nil
+ return newUploadPackSession(c.c, ep), nil
}
-func (c *client) NewSendPackSession(ep transport.Endpoint) (
- transport.SendPackSession, error) {
+func (c *client) NewReceivePackSession(ep transport.Endpoint) (
+ transport.ReceivePackSession, error) {
- return newSendPackSession(c.c, ep), nil
+ return newReceivePackSession(c.c, ep), nil
}
type session struct {
diff --git a/plumbing/transport/http/common_test.go b/plumbing/transport/http/common_test.go
index 432bd07..217999d 100644
--- a/plumbing/transport/http/common_test.go
+++ b/plumbing/transport/http/common_test.go
@@ -26,7 +26,7 @@ func (s *ClientSuite) SetUpSuite(c *C) {
c.Assert(err, IsNil)
}
-func (s *FetchPackSuite) TestNewClient(c *C) {
+func (s *UploadPackSuite) TestNewClient(c *C) {
roundTripper := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
@@ -76,10 +76,10 @@ func (s *ClientSuite) testNewHTTPError(c *C, code int, msg string) {
func (s *ClientSuite) TestSetAuth(c *C) {
auth := &BasicAuth{}
- r, err := DefaultClient.NewFetchPackSession(s.Endpoint)
+ r, err := DefaultClient.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
r.SetAuth(auth)
- c.Assert(auth, Equals, r.(*fetchPackSession).auth)
+ c.Assert(auth, Equals, r.(*upSession).auth)
}
type mockAuth struct{}
@@ -88,7 +88,7 @@ func (*mockAuth) Name() string { return "" }
func (*mockAuth) String() string { return "" }
func (s *ClientSuite) TestSetAuthWrongType(c *C) {
- r, err := DefaultClient.NewFetchPackSession(s.Endpoint)
+ r, err := DefaultClient.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
c.Assert(r.SetAuth(&mockAuth{}), Equals, transport.ErrInvalidAuthMethod)
}
diff --git a/plumbing/transport/http/receive_pack.go b/plumbing/transport/http/receive_pack.go
new file mode 100644
index 0000000..a8384c7
--- /dev/null
+++ b/plumbing/transport/http/receive_pack.go
@@ -0,0 +1,30 @@
+package http
+
+import (
+ "errors"
+ "net/http"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+)
+
+var errReceivePackNotSupported = errors.New("receive-pack not supported yet")
+
+type rpSession struct {
+ *session
+}
+
+func newReceivePackSession(c *http.Client, ep transport.Endpoint) transport.ReceivePackSession {
+ return &rpSession{&session{}}
+}
+
+func (s *rpSession) AdvertisedReferences() (*packp.AdvRefs, error) {
+
+ return nil, errReceivePackNotSupported
+}
+
+func (s *rpSession) ReceivePack(*packp.ReferenceUpdateRequest) (
+ *packp.ReportStatus, error) {
+
+ return nil, errReceivePackNotSupported
+}
diff --git a/plumbing/transport/http/send_pack.go b/plumbing/transport/http/send_pack.go
deleted file mode 100644
index 43464ae..0000000
--- a/plumbing/transport/http/send_pack.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package http
-
-import (
- "errors"
- "net/http"
-
- "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
- "gopkg.in/src-d/go-git.v4/plumbing/transport"
-)
-
-var errSendPackNotSupported = errors.New("send-pack not supported yet")
-
-type sendPackSession struct {
- *session
-}
-
-func newSendPackSession(c *http.Client, ep transport.Endpoint) transport.SendPackSession {
- return &sendPackSession{&session{}}
-}
-
-func (s *sendPackSession) AdvertisedReferences() (*packp.AdvRefs, error) {
-
- return nil, errSendPackNotSupported
-}
-
-func (s *sendPackSession) SendPack(*packp.ReferenceUpdateRequest) (
- *packp.ReportStatus, error) {
-
- return nil, errSendPackNotSupported
-}
diff --git a/plumbing/transport/http/fetch_pack.go b/plumbing/transport/http/upload_pack.go
index 0c85be4..26257f5 100644
--- a/plumbing/transport/http/fetch_pack.go
+++ b/plumbing/transport/http/upload_pack.go
@@ -15,14 +15,14 @@ import (
"gopkg.in/src-d/go-git.v4/utils/ioutil"
)
-type fetchPackSession struct {
+type upSession struct {
*session
}
-func newFetchPackSession(c *http.Client,
- ep transport.Endpoint) transport.FetchPackSession {
+func newUploadPackSession(c *http.Client,
+ ep transport.Endpoint) transport.UploadPackSession {
- return &fetchPackSession{
+ return &upSession{
session: &session{
auth: basicAuthFromEndpoint(ep),
client: c,
@@ -31,7 +31,7 @@ func newFetchPackSession(c *http.Client,
}
}
-func (s *fetchPackSession) AdvertisedReferences() (*packp.AdvRefs, error) {
+func (s *upSession) AdvertisedReferences() (*packp.AdvRefs, error) {
if s.advRefs != nil {
return s.advRefs, nil
}
@@ -73,7 +73,7 @@ func (s *fetchPackSession) AdvertisedReferences() (*packp.AdvRefs, error) {
return ar, nil
}
-func (s *fetchPackSession) FetchPack(req *packp.UploadPackRequest) (*packp.UploadPackResponse, error) {
+func (s *upSession) UploadPack(req *packp.UploadPackRequest) (*packp.UploadPackResponse, error) {
if req.IsEmpty() {
return nil, transport.ErrEmptyUploadPackRequest
}
@@ -111,11 +111,11 @@ func (s *fetchPackSession) FetchPack(req *packp.UploadPackRequest) (*packp.Uploa
}
// Close does nothing.
-func (s *fetchPackSession) Close() error {
+func (s *upSession) Close() error {
return nil
}
-func (s *fetchPackSession) doRequest(method, url string, content *bytes.Buffer) (*http.Response, error) {
+func (s *upSession) doRequest(method, url string, content *bytes.Buffer) (*http.Response, error) {
var body io.Reader
if content != nil {
body = content
@@ -143,7 +143,7 @@ func (s *fetchPackSession) doRequest(method, url string, content *bytes.Buffer)
}
// it requires a bytes.Buffer, because we need to know the length
-func (s *fetchPackSession) applyHeadersToRequest(req *http.Request, content *bytes.Buffer) {
+func (s *upSession) applyHeadersToRequest(req *http.Request, content *bytes.Buffer) {
req.Header.Add("User-Agent", "git/1.0")
req.Header.Add("Host", s.endpoint.Host)
diff --git a/plumbing/transport/http/fetch_pack_test.go b/plumbing/transport/http/upload_pack_test.go
index 6c40e60..d3e4989 100644
--- a/plumbing/transport/http/fetch_pack_test.go
+++ b/plumbing/transport/http/upload_pack_test.go
@@ -11,37 +11,38 @@ import (
. "gopkg.in/check.v1"
)
-type FetchPackSuite struct {
- test.FetchPackSuite
+type UploadPackSuite struct {
+ test.UploadPackSuite
}
-var _ = Suite(&FetchPackSuite{})
+var _ = Suite(&UploadPackSuite{})
-func (s *FetchPackSuite) SetUpSuite(c *C) {
- s.FetchPackSuite.Client = DefaultClient
+func (s *UploadPackSuite) SetUpSuite(c *C) {
+ s.UploadPackSuite.Client = DefaultClient
ep, err := transport.NewEndpoint("https://github.com/git-fixtures/basic.git")
c.Assert(err, IsNil)
- s.FetchPackSuite.Endpoint = ep
+ s.UploadPackSuite.Endpoint = ep
ep, err = transport.NewEndpoint("https://github.com/git-fixtures/empty.git")
c.Assert(err, IsNil)
- s.FetchPackSuite.EmptyEndpoint = ep
+ s.UploadPackSuite.EmptyEndpoint = ep
ep, err = transport.NewEndpoint("https://github.com/git-fixtures/non-existent.git")
c.Assert(err, IsNil)
- s.FetchPackSuite.NonExistentEndpoint = ep
+ s.UploadPackSuite.NonExistentEndpoint = ep
}
-func (s *FetchPackSuite) TestInfoNotExists(c *C) {
- r, err := s.Client.NewFetchPackSession(s.NonExistentEndpoint)
+// Overwritten, different behaviour for HTTP.
+func (s *UploadPackSuite) TestAdvertisedReferencesNotExists(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.NonExistentEndpoint)
c.Assert(err, IsNil)
info, err := r.AdvertisedReferences()
c.Assert(err, Equals, transport.ErrAuthorizationRequired)
c.Assert(info, IsNil)
}
-func (s *FetchPackSuite) TestuploadPackRequestToReader(c *C) {
+func (s *UploadPackSuite) TestuploadPackRequestToReader(c *C) {
r := packp.NewUploadPackRequest()
r.Wants = append(r.Wants, plumbing.NewHash("d82f291cde9987322c8a0c81a325e1ba6159684c"))
r.Wants = append(r.Wants, plumbing.NewHash("2b41ef280fdb67a9b250678686a0c3e03b0a9989"))
diff --git a/plumbing/transport/internal/common/common.go b/plumbing/transport/internal/common/common.go
index 17c473e..831c4d7 100644
--- a/plumbing/transport/internal/common/common.go
+++ b/plumbing/transport/internal/common/common.go
@@ -77,20 +77,20 @@ type client struct {
}
// NewClient creates a new client using the given Commander.
-func NewClient(runner Commander) transport.Client {
+func NewClient(runner Commander) transport.Transport {
return &client{runner}
}
-// NewFetchPackSession creates a new FetchPackSession.
-func (c *client) NewFetchPackSession(ep transport.Endpoint) (
- transport.FetchPackSession, error) {
+// NewUploadPackSession creates a new UploadPackSession.
+func (c *client) NewUploadPackSession(ep transport.Endpoint) (
+ transport.UploadPackSession, error) {
return c.newSession(transport.UploadPackServiceName, ep)
}
-// NewSendPackSession creates a new SendPackSession.
-func (c *client) NewSendPackSession(ep transport.Endpoint) (
- transport.SendPackSession, error) {
+// NewReceivePackSession creates a new ReceivePackSession.
+func (c *client) NewReceivePackSession(ep transport.Endpoint) (
+ transport.ReceivePackSession, error) {
return c.newSession(transport.ReceivePackServiceName, ep)
}
@@ -219,9 +219,9 @@ func (s *session) handleAdvRefDecodeError(err error) error {
return err
}
-// FetchPack performs a request to the server to fetch a packfile. A reader is
+// UploadPack performs a request to the server to fetch a packfile. A reader is
// returned with the packfile content. The reader must be closed after reading.
-func (s *session) FetchPack(req *packp.UploadPackRequest) (*packp.UploadPackResponse, error) {
+func (s *session) UploadPack(req *packp.UploadPackRequest) (*packp.UploadPackResponse, error) {
if req.IsEmpty() {
return nil, transport.ErrEmptyUploadPackRequest
}
@@ -236,7 +236,7 @@ func (s *session) FetchPack(req *packp.UploadPackRequest) (*packp.UploadPackResp
s.packRun = true
- if err := fetchPack(s.Stdin, s.Stdout, req); err != nil {
+ if err := uploadPack(s.Stdin, s.Stdout, req); err != nil {
return nil, err
}
@@ -259,7 +259,7 @@ func (s *session) FetchPack(req *packp.UploadPackRequest) (*packp.UploadPackResp
return DecodeUploadPackResponse(rc, req)
}
-func (s *session) SendPack(req *packp.ReferenceUpdateRequest) (*packp.ReportStatus, error) {
+func (s *session) ReceivePack(req *packp.ReferenceUpdateRequest) (*packp.ReportStatus, error) {
if _, err := s.AdvertisedReferences(); err != nil {
return nil, err
}
@@ -295,7 +295,7 @@ func (s *session) finish() error {
s.finished = true
- // If we did not run fetch-pack or send-pack, we close the connection
+ // If we did not run a upload/receive-pack, we close the connection
// gracefully by sending a flush packet to the server. If the server
// operates correctly, it will exit with status 0.
if !s.packRun {
@@ -367,13 +367,13 @@ var (
eol = []byte("\n")
)
-// fetchPack implements the git-fetch-pack protocol.
+// uploadPack implements the git-upload-pack protocol.
//
// TODO support multi_ack mode
// TODO support multi_ack_detailed mode
// TODO support acks for common objects
// TODO build a proper state machine for all these processing options
-func fetchPack(w io.WriteCloser, r io.Reader, req *packp.UploadPackRequest) error {
+func uploadPack(w io.WriteCloser, r io.Reader, req *packp.UploadPackRequest) error {
if err := req.UploadRequest.Encode(w); err != nil {
return fmt.Errorf("sending upload-req message: %s", err)
}
diff --git a/plumbing/transport/internal/common/server.go b/plumbing/transport/internal/common/server.go
new file mode 100644
index 0000000..dd6cfbe
--- /dev/null
+++ b/plumbing/transport/internal/common/server.go
@@ -0,0 +1,72 @@
+package common
+
+import (
+ "fmt"
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+ "gopkg.in/src-d/go-git.v4/utils/ioutil"
+)
+
+// ServerCommand is used for a single server command execution.
+type ServerCommand struct {
+ Stderr io.Writer
+ Stdout io.WriteCloser
+ Stdin io.Reader
+}
+
+func ServeUploadPack(cmd ServerCommand, s transport.UploadPackSession) (err error) {
+ ioutil.CheckClose(cmd.Stdout, &err)
+
+ ar, err := s.AdvertisedReferences()
+ if err != nil {
+ return err
+ }
+
+ if err := ar.Encode(cmd.Stdout); err != nil {
+ return err
+ }
+
+ req := packp.NewUploadPackRequest()
+ if err := req.Decode(cmd.Stdin); err != nil {
+ return err
+ }
+
+ var resp *packp.UploadPackResponse
+ resp, err = s.UploadPack(req)
+ if err != nil {
+ return err
+ }
+
+ return resp.Encode(cmd.Stdout)
+}
+
+func ServeReceivePack(cmd ServerCommand, s transport.ReceivePackSession) error {
+ ar, err := s.AdvertisedReferences()
+ if err != nil {
+ return fmt.Errorf("internal error in advertised references: %s", err)
+ }
+
+ if err := ar.Encode(cmd.Stdout); err != nil {
+ return fmt.Errorf("error in advertised references encoding: %s", err)
+ }
+
+ req := packp.NewReferenceUpdateRequest()
+ if err := req.Decode(cmd.Stdin); err != nil {
+ return fmt.Errorf("error decoding: %s", err)
+ }
+
+ rs, err := s.ReceivePack(req)
+ if rs != nil {
+ if err := rs.Encode(cmd.Stdout); err != nil {
+ return fmt.Errorf("error in encoding report status %s", err)
+ }
+ }
+
+ if err != nil {
+ return fmt.Errorf("error in receive pack: %s", err)
+ }
+
+ return nil
+}
diff --git a/plumbing/transport/server/loader.go b/plumbing/transport/server/loader.go
new file mode 100644
index 0000000..55bcf1d
--- /dev/null
+++ b/plumbing/transport/server/loader.go
@@ -0,0 +1,59 @@
+package server
+
+import (
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
+
+ "srcd.works/go-billy.v1"
+ "srcd.works/go-billy.v1/os"
+)
+
+// DefaultLoader is a filesystem loader ignoring host and resolving paths to /.
+var DefaultLoader = NewFilesystemLoader(os.New("/"))
+
+// Loader loads repository's storer.Storer based on an optional host and a path.
+type Loader interface {
+ // Load loads a storer.Storer given a transport.Endpoint.
+ // Returns transport.ErrRepositoryNotFound if the repository does not
+ // exist.
+ Load(ep transport.Endpoint) (storer.Storer, error)
+}
+
+type fsLoader struct {
+ base billy.Filesystem
+}
+
+// NewFilesystemLoader creates a Loader that ignores host and resolves paths
+// with a given base filesystem.
+func NewFilesystemLoader(base billy.Filesystem) Loader {
+ return &fsLoader{base}
+}
+
+// Load looks up the endpoint's path in the base file system and returns a
+// storer for it. Returns transport.ErrRepositoryNotFound if a repository does
+// not exist in the given path.
+func (l *fsLoader) Load(ep transport.Endpoint) (storer.Storer, error) {
+ fs := l.base.Dir(ep.Path)
+ if _, err := fs.Stat("config"); err != nil {
+ return nil, transport.ErrRepositoryNotFound
+ }
+
+ return filesystem.NewStorage(fs)
+}
+
+// MapLoader is a Loader that uses a lookup map of storer.Storer by
+// transport.Endpoint.
+type MapLoader map[transport.Endpoint]storer.Storer
+
+// Load returns a storer.Storer for given a transport.Endpoint by looking it up
+// in the map. Returns transport.ErrRepositoryNotFound if the endpoint does not
+// exist.
+func (l MapLoader) Load(ep transport.Endpoint) (storer.Storer, error) {
+ s, ok := l[ep]
+ if !ok {
+ return nil, transport.ErrRepositoryNotFound
+ }
+
+ return s, nil
+}
diff --git a/plumbing/transport/server/loader_test.go b/plumbing/transport/server/loader_test.go
new file mode 100644
index 0000000..b4a8c37
--- /dev/null
+++ b/plumbing/transport/server/loader_test.go
@@ -0,0 +1,57 @@
+package server
+
+import (
+ "fmt"
+ "os/exec"
+ "path/filepath"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+
+ . "gopkg.in/check.v1"
+)
+
+type LoaderSuite struct {
+ RepoPath string
+}
+
+var _ = Suite(&LoaderSuite{})
+
+func (s *LoaderSuite) SetUpSuite(c *C) {
+ if err := exec.Command("git", "--version").Run(); err != nil {
+ c.Skip("git command not found")
+ }
+
+ dir := c.MkDir()
+ s.RepoPath = filepath.Join(dir, "repo.git")
+ c.Assert(exec.Command("git", "init", "--bare", s.RepoPath).Run(), IsNil)
+}
+
+func (s *LoaderSuite) endpoint(c *C, url string) transport.Endpoint {
+ ep, err := transport.NewEndpoint(url)
+ c.Assert(err, IsNil)
+ return ep
+}
+
+func (s *LoaderSuite) TestLoadNonExistent(c *C) {
+ sto, err := DefaultLoader.Load(s.endpoint(c, "file:///does-not-exist"))
+ c.Assert(err, Equals, transport.ErrRepositoryNotFound)
+ c.Assert(sto, IsNil)
+}
+
+func (s *LoaderSuite) TestLoadNonExistentIgnoreHost(c *C) {
+ sto, err := DefaultLoader.Load(s.endpoint(c, "https://github.com/does-not-exist"))
+ c.Assert(err, Equals, transport.ErrRepositoryNotFound)
+ c.Assert(sto, IsNil)
+}
+
+func (s *LoaderSuite) TestLoad(c *C) {
+ sto, err := DefaultLoader.Load(s.endpoint(c, fmt.Sprintf("file://%s", s.RepoPath)))
+ c.Assert(err, IsNil)
+ c.Assert(sto, NotNil)
+}
+
+func (s *LoaderSuite) TestLoadIgnoreHost(c *C) {
+ sto, err := DefaultLoader.Load(s.endpoint(c, fmt.Sprintf("file://%s", s.RepoPath)))
+ c.Assert(err, IsNil)
+ c.Assert(sto, NotNil)
+}
diff --git a/plumbing/transport/server/receive_pack_test.go b/plumbing/transport/server/receive_pack_test.go
new file mode 100644
index 0000000..2c4036a
--- /dev/null
+++ b/plumbing/transport/server/receive_pack_test.go
@@ -0,0 +1,40 @@
+package server_test
+
+import (
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/test"
+
+ . "gopkg.in/check.v1"
+)
+
+type ReceivePackSuite struct {
+ BaseSuite
+ test.ReceivePackSuite
+}
+
+var _ = Suite(&ReceivePackSuite{})
+
+func (s *ReceivePackSuite) SetUpSuite(c *C) {
+ s.BaseSuite.SetUpSuite(c)
+ s.ReceivePackSuite.Client = s.client
+}
+
+func (s *ReceivePackSuite) SetUpTest(c *C) {
+ s.prepareRepositories(c, &s.Endpoint, &s.EmptyEndpoint, &s.NonExistentEndpoint)
+}
+
+func (s *ReceivePackSuite) TearDownTest(c *C) {
+ s.Suite.TearDownSuite(c)
+}
+
+// TODO
+func (s *ReceivePackSuite) TestSendPackAddDeleteReference(c *C) {
+ c.Skip("delete reference not supported yet")
+}
+
+// Overwritten, server returns error earlier.
+func (s *ReceivePackSuite) TestAdvertisedReferencesNotExists(c *C) {
+ r, err := s.Client.NewReceivePackSession(s.NonExistentEndpoint)
+ c.Assert(err, Equals, transport.ErrRepositoryNotFound)
+ c.Assert(r, IsNil)
+}
diff --git a/plumbing/transport/server/server.go b/plumbing/transport/server/server.go
new file mode 100644
index 0000000..6787e9d
--- /dev/null
+++ b/plumbing/transport/server/server.go
@@ -0,0 +1,448 @@
+// Package server implements the git server protocol. For most use cases, the
+// transport-specific implementations should be used.
+package server
+
+import (
+ "errors"
+ "fmt"
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
+ "gopkg.in/src-d/go-git.v4/plumbing/object"
+ "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
+ "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
+ "gopkg.in/src-d/go-git.v4/plumbing/revlist"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+)
+
+var DefaultServer = NewServer(DefaultLoader)
+
+type server struct {
+ loader Loader
+ handler *handler
+}
+
+// NewServer returns a transport.Transport implementing a git server,
+// independent of transport. Each transport must wrap this.
+func NewServer(loader Loader) transport.Transport {
+ return &server{loader, &handler{}}
+}
+
+func (s *server) NewUploadPackSession(ep transport.Endpoint) (transport.UploadPackSession, error) {
+ sto, err := s.loader.Load(ep)
+ if err != nil {
+ return nil, err
+ }
+
+ return s.handler.NewUploadPackSession(sto)
+}
+
+func (s *server) NewReceivePackSession(ep transport.Endpoint) (transport.ReceivePackSession, error) {
+ sto, err := s.loader.Load(ep)
+ if err != nil {
+ return nil, err
+ }
+
+ return s.handler.NewReceivePackSession(sto)
+}
+
+type handler struct{}
+
+func (h *handler) NewUploadPackSession(s storer.Storer) (transport.UploadPackSession, error) {
+ return &upSession{
+ session: session{storer: s},
+ }, nil
+}
+
+func (h *handler) NewReceivePackSession(s storer.Storer) (transport.ReceivePackSession, error) {
+ return &rpSession{
+ session: session{storer: s},
+ cmdStatus: map[plumbing.ReferenceName]error{},
+ }, nil
+}
+
+type session struct {
+ storer storer.Storer
+ caps *capability.List
+}
+
+func (s *session) Close() error {
+ return nil
+}
+
+//TODO: deprecate
+func (s *session) SetAuth(transport.AuthMethod) error {
+ return nil
+}
+
+func (s *session) checkSupportedCapabilities(cl *capability.List) error {
+ for _, c := range cl.All() {
+ if !s.caps.Supports(c) {
+ return fmt.Errorf("unsupported capability: %s", c)
+ }
+ }
+
+ return nil
+}
+
+type upSession struct {
+ session
+}
+
+func (s *upSession) AdvertisedReferences() (*packp.AdvRefs, error) {
+ ar := packp.NewAdvRefs()
+
+ if err := s.setSupportedCapabilities(ar.Capabilities); err != nil {
+ return nil, err
+ }
+
+ s.caps = ar.Capabilities
+
+ if err := setReferences(s.storer, ar); err != nil {
+ return nil, err
+ }
+
+ if err := setHEAD(s.storer, ar); err != nil {
+ return nil, err
+ }
+
+ return ar, nil
+}
+
+func (s *upSession) UploadPack(req *packp.UploadPackRequest) (*packp.UploadPackResponse, error) {
+ if req.IsEmpty() {
+ return nil, transport.ErrEmptyUploadPackRequest
+ }
+
+ if err := req.Validate(); err != nil {
+ return nil, err
+ }
+
+ if s.caps == nil {
+ s.caps = capability.NewList()
+ if err := s.setSupportedCapabilities(s.caps); err != nil {
+ return nil, err
+ }
+ }
+
+ if err := s.checkSupportedCapabilities(req.Capabilities); err != nil {
+ return nil, err
+ }
+
+ s.caps = req.Capabilities
+
+ if len(req.Shallows) > 0 {
+ return nil, fmt.Errorf("shallow not supported")
+ }
+
+ objs, err := s.objectsToUpload(req)
+ if err != nil {
+ return nil, err
+ }
+
+ pr, pw := io.Pipe()
+ e := packfile.NewEncoder(pw, s.storer, false)
+ go func() {
+ _, err := e.Encode(objs)
+ pw.CloseWithError(err)
+ }()
+
+ return packp.NewUploadPackResponseWithPackfile(req, pr), nil
+}
+
+func (s *upSession) objectsToUpload(req *packp.UploadPackRequest) ([]plumbing.Hash, error) {
+ commits, err := s.commitsToUpload(req.Wants)
+ if err != nil {
+ return nil, err
+ }
+
+ return revlist.Objects(s.storer, commits, req.Haves)
+}
+
+func (s *upSession) commitsToUpload(wants []plumbing.Hash) ([]*object.Commit, error) {
+ var commits []*object.Commit
+ for _, h := range wants {
+ c, err := object.GetCommit(s.storer, h)
+ if err != nil {
+ return nil, err
+ }
+
+ commits = append(commits, c)
+ }
+
+ return commits, nil
+}
+
+func (*upSession) setSupportedCapabilities(c *capability.List) error {
+ if err := c.Set(capability.Agent, capability.DefaultAgent); err != nil {
+ return err
+ }
+
+ if err := c.Set(capability.OFSDelta); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+type rpSession struct {
+ session
+ cmdStatus map[plumbing.ReferenceName]error
+ firstErr error
+ unpackErr error
+}
+
+func (s *rpSession) AdvertisedReferences() (*packp.AdvRefs, error) {
+ ar := packp.NewAdvRefs()
+
+ if err := s.setSupportedCapabilities(ar.Capabilities); err != nil {
+ return nil, err
+ }
+
+ s.caps = ar.Capabilities
+
+ if err := setReferences(s.storer, ar); err != nil {
+ return nil, err
+ }
+
+ if err := setHEAD(s.storer, ar); err != nil {
+ return nil, err
+ }
+
+ return ar, nil
+}
+
+var (
+ ErrUpdateReference = errors.New("failed to update ref")
+)
+
+func (s *rpSession) ReceivePack(req *packp.ReferenceUpdateRequest) (*packp.ReportStatus, error) {
+ if s.caps == nil {
+ s.caps = capability.NewList()
+ if err := s.setSupportedCapabilities(s.caps); err != nil {
+ return nil, err
+ }
+ }
+
+ if err := s.checkSupportedCapabilities(req.Capabilities); err != nil {
+ return nil, err
+ }
+
+ s.caps = req.Capabilities
+
+ //TODO: Implement 'atomic' update of references.
+
+ if err := s.writePackfile(req.Packfile); err != nil {
+ s.unpackErr = err
+ s.firstErr = err
+ return s.reportStatus(), err
+ }
+
+ updatedRefs := s.updatedReferences(req)
+
+ if s.caps.Supports(capability.Atomic) && s.firstErr != nil {
+ //TODO: add support for 'atomic' once we have reference
+ // transactions, currently we do not announce it.
+ rs := s.reportStatus()
+ for _, cs := range rs.CommandStatuses {
+ if cs.Error() == nil {
+ cs.Status = ""
+ }
+ }
+ }
+
+ for name, ref := range updatedRefs {
+ //TODO: add support for 'delete-refs' once we can delete
+ // references, currently we do not announce it.
+ err := s.storer.SetReference(ref)
+ s.setStatus(name, err)
+ }
+
+ return s.reportStatus(), s.firstErr
+}
+
+func (s *rpSession) updatedReferences(req *packp.ReferenceUpdateRequest) map[plumbing.ReferenceName]*plumbing.Reference {
+ refs := map[plumbing.ReferenceName]*plumbing.Reference{}
+ for _, cmd := range req.Commands {
+ exists, err := referenceExists(s.storer, cmd.Name)
+ if err != nil {
+ s.setStatus(cmd.Name, err)
+ continue
+ }
+
+ switch cmd.Action() {
+ case packp.Create:
+ if exists {
+ s.setStatus(cmd.Name, ErrUpdateReference)
+ continue
+ }
+
+ ref := plumbing.NewHashReference(cmd.Name, cmd.New)
+ refs[ref.Name()] = ref
+ case packp.Delete:
+ if !exists {
+ s.setStatus(cmd.Name, ErrUpdateReference)
+ continue
+ }
+
+ if !s.caps.Supports(capability.DeleteRefs) {
+ s.setStatus(cmd.Name, fmt.Errorf("delete not supported"))
+ continue
+ }
+
+ refs[cmd.Name] = nil
+ case packp.Update:
+ if !exists {
+ s.setStatus(cmd.Name, ErrUpdateReference)
+ continue
+ }
+
+ if err != nil {
+ s.setStatus(cmd.Name, err)
+ continue
+ }
+
+ ref := plumbing.NewHashReference(cmd.Name, cmd.New)
+ refs[ref.Name()] = ref
+ }
+ }
+
+ return refs
+}
+
+func (s *rpSession) failAtomicUpdate() (*packp.ReportStatus, error) {
+ rs := s.reportStatus()
+ for _, cs := range rs.CommandStatuses {
+ if cs.Error() == nil {
+ cs.Status = "atomic updated"
+ }
+ }
+
+ return rs, s.firstErr
+}
+
+func (s *rpSession) writePackfile(r io.ReadCloser) error {
+ if r == nil {
+ return nil
+ }
+
+ if err := packfile.UpdateObjectStorage(s.storer, r); err != nil {
+ _ = r.Close()
+ return err
+ }
+
+ return r.Close()
+}
+
+func (s *rpSession) setStatus(ref plumbing.ReferenceName, err error) {
+ s.cmdStatus[ref] = err
+ if s.firstErr == nil && err != nil {
+ s.firstErr = err
+ }
+}
+
+func (s *rpSession) reportStatus() *packp.ReportStatus {
+ if !s.caps.Supports(capability.ReportStatus) {
+ return nil
+ }
+
+ rs := packp.NewReportStatus()
+ rs.UnpackStatus = "ok"
+
+ if s.unpackErr != nil {
+ rs.UnpackStatus = s.unpackErr.Error()
+ }
+
+ if s.cmdStatus == nil {
+ return rs
+ }
+
+ for ref, err := range s.cmdStatus {
+ msg := "ok"
+ if err != nil {
+ msg = err.Error()
+ }
+ status := &packp.CommandStatus{
+ ReferenceName: ref,
+ Status: msg,
+ }
+ rs.CommandStatuses = append(rs.CommandStatuses, status)
+ }
+
+ return rs
+}
+
+func (*rpSession) setSupportedCapabilities(c *capability.List) error {
+ if err := c.Set(capability.Agent, capability.DefaultAgent); err != nil {
+ return err
+ }
+
+ if err := c.Set(capability.OFSDelta); err != nil {
+ return err
+ }
+
+ return c.Set(capability.ReportStatus)
+}
+
+func setHEAD(s storer.Storer, ar *packp.AdvRefs) error {
+ ref, err := s.Reference(plumbing.HEAD)
+ if err == plumbing.ErrReferenceNotFound {
+ return nil
+ }
+
+ if err != nil {
+ return err
+ }
+
+ if ref.Type() == plumbing.SymbolicReference {
+ if err := ar.AddReference(ref); err != nil {
+ return nil
+ }
+
+ ref, err = storer.ResolveReference(s, ref.Target())
+ if err == plumbing.ErrReferenceNotFound {
+ return nil
+ }
+
+ if err != nil {
+ return err
+ }
+ }
+
+ if ref.Type() != plumbing.HashReference {
+ return plumbing.ErrInvalidType
+ }
+
+ h := ref.Hash()
+ ar.Head = &h
+
+ return nil
+}
+
+//TODO: add peeled references.
+func setReferences(s storer.Storer, ar *packp.AdvRefs) error {
+ iter, err := s.IterReferences()
+ if err != nil {
+ return err
+ }
+
+ return iter.ForEach(func(ref *plumbing.Reference) error {
+ if ref.Type() != plumbing.HashReference {
+ return nil
+ }
+
+ ar.References[ref.Name().String()] = ref.Hash()
+ return nil
+ })
+}
+
+func referenceExists(s storer.ReferenceStorer, n plumbing.ReferenceName) (bool, error) {
+ _, err := s.Reference(n)
+ if err == plumbing.ErrReferenceNotFound {
+ return false, nil
+ }
+
+ return err == nil, err
+}
diff --git a/plumbing/transport/server/server_test.go b/plumbing/transport/server/server_test.go
new file mode 100644
index 0000000..2020fe3
--- /dev/null
+++ b/plumbing/transport/server/server_test.go
@@ -0,0 +1,70 @@
+package server_test
+
+import (
+ "fmt"
+ "testing"
+
+ "gopkg.in/src-d/go-git.v4/fixtures"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/client"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/server"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
+ "gopkg.in/src-d/go-git.v4/storage/memory"
+
+ . "gopkg.in/check.v1"
+)
+
+func Test(t *testing.T) { TestingT(t) }
+
+const inprocScheme = "inproc"
+
+type BaseSuite struct {
+ fixtures.Suite
+ loader server.MapLoader
+ client transport.Transport
+ clientBackup transport.Transport
+}
+
+func (s *BaseSuite) SetUpSuite(c *C) {
+ s.Suite.SetUpSuite(c)
+ s.loader = server.MapLoader{}
+ s.client = server.NewServer(s.loader)
+ s.clientBackup = client.Protocols[inprocScheme]
+ client.Protocols[inprocScheme] = s.client
+}
+
+func (s *BaseSuite) TearDownSuite(c *C) {
+ if s.clientBackup == nil {
+ delete(client.Protocols, inprocScheme)
+ } else {
+ client.Protocols[inprocScheme] = s.clientBackup
+ }
+}
+
+func (s *BaseSuite) prepareRepositories(c *C, basic *transport.Endpoint,
+ empty *transport.Endpoint, nonExistent *transport.Endpoint) {
+
+ f := fixtures.Basic().One()
+ fs := f.DotGit()
+ path := fs.Base()
+ url := fmt.Sprintf("%s://%s", inprocScheme, path)
+ ep, err := transport.NewEndpoint(url)
+ c.Assert(err, IsNil)
+ *basic = ep
+ sto, err := filesystem.NewStorage(fs)
+ c.Assert(err, IsNil)
+ s.loader[ep] = sto
+
+ path = "/empty.git"
+ url = fmt.Sprintf("%s://%s", inprocScheme, path)
+ ep, err = transport.NewEndpoint(url)
+ c.Assert(err, IsNil)
+ *empty = ep
+ s.loader[ep] = memory.NewStorage()
+
+ path = "/non-existent.git"
+ url = fmt.Sprintf("%s://%s", inprocScheme, path)
+ ep, err = transport.NewEndpoint(url)
+ c.Assert(err, IsNil)
+ *nonExistent = ep
+}
diff --git a/plumbing/transport/server/upload_pack_test.go b/plumbing/transport/server/upload_pack_test.go
new file mode 100644
index 0000000..8919e8e
--- /dev/null
+++ b/plumbing/transport/server/upload_pack_test.go
@@ -0,0 +1,40 @@
+package server_test
+
+import (
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/test"
+
+ . "gopkg.in/check.v1"
+)
+
+type UploadPackSuite struct {
+ BaseSuite
+ test.UploadPackSuite
+}
+
+var _ = Suite(&UploadPackSuite{})
+
+func (s *UploadPackSuite) SetUpSuite(c *C) {
+ s.BaseSuite.SetUpSuite(c)
+ s.UploadPackSuite.Client = s.client
+}
+
+func (s *UploadPackSuite) SetUpTest(c *C) {
+ s.prepareRepositories(c, &s.Endpoint, &s.EmptyEndpoint, &s.NonExistentEndpoint)
+}
+
+// Overwritten, it's not an error in server-side.
+func (s *UploadPackSuite) TestAdvertisedReferencesEmpty(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.EmptyEndpoint)
+ c.Assert(err, IsNil)
+ ar, err := r.AdvertisedReferences()
+ c.Assert(err, IsNil)
+ c.Assert(len(ar.References), Equals, 0)
+}
+
+// Overwritten, server returns error earlier.
+func (s *UploadPackSuite) TestAdvertisedReferencesNotExists(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.NonExistentEndpoint)
+ c.Assert(err, Equals, transport.ErrRepositoryNotFound)
+ c.Assert(r, IsNil)
+}
diff --git a/plumbing/transport/ssh/fetch_pack_test.go b/plumbing/transport/ssh/upload_pack_test.go
index 927e9a8..8194770 100644
--- a/plumbing/transport/ssh/fetch_pack_test.go
+++ b/plumbing/transport/ssh/upload_pack_test.go
@@ -9,29 +9,29 @@ import (
. "gopkg.in/check.v1"
)
-type FetchPackSuite struct {
- test.FetchPackSuite
+type UploadPackSuite struct {
+ test.UploadPackSuite
}
-var _ = Suite(&FetchPackSuite{})
+var _ = Suite(&UploadPackSuite{})
-func (s *FetchPackSuite) SetUpSuite(c *C) {
+func (s *UploadPackSuite) SetUpSuite(c *C) {
if os.Getenv("SSH_AUTH_SOCK") == "" {
c.Skip("SSH_AUTH_SOCK is not set")
}
- s.FetchPackSuite.Client = DefaultClient
+ s.UploadPackSuite.Client = DefaultClient
ep, err := transport.NewEndpoint("git@github.com:git-fixtures/basic.git")
c.Assert(err, IsNil)
- s.FetchPackSuite.Endpoint = ep
+ s.UploadPackSuite.Endpoint = ep
ep, err = transport.NewEndpoint("git@github.com:git-fixtures/empty.git")
c.Assert(err, IsNil)
- s.FetchPackSuite.EmptyEndpoint = ep
+ s.UploadPackSuite.EmptyEndpoint = ep
ep, err = transport.NewEndpoint("git@github.com:git-fixtures/non-existent.git")
c.Assert(err, IsNil)
- s.FetchPackSuite.NonExistentEndpoint = ep
+ s.UploadPackSuite.NonExistentEndpoint = ep
}
diff --git a/plumbing/transport/test/send_pack.go b/plumbing/transport/test/receive_pack.go
index f880588..e798154 100644
--- a/plumbing/transport/test/send_pack.go
+++ b/plumbing/transport/test/receive_pack.go
@@ -19,44 +19,44 @@ import (
. "gopkg.in/check.v1"
)
-type SendPackSuite struct {
+type ReceivePackSuite struct {
Endpoint transport.Endpoint
EmptyEndpoint transport.Endpoint
NonExistentEndpoint transport.Endpoint
- Client transport.Client
+ Client transport.Transport
}
-func (s *SendPackSuite) TestInfoEmpty(c *C) {
- r, err := s.Client.NewSendPackSession(s.EmptyEndpoint)
+func (s *ReceivePackSuite) TestAdvertisedReferencesEmpty(c *C) {
+ r, err := s.Client.NewReceivePackSession(s.EmptyEndpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
- info, err := r.AdvertisedReferences()
+ ar, err := r.AdvertisedReferences()
c.Assert(err, IsNil)
- c.Assert(info.Head, IsNil)
+ c.Assert(ar.Head, IsNil)
}
-func (s *SendPackSuite) TestInfoNotExists(c *C) {
- r, err := s.Client.NewSendPackSession(s.NonExistentEndpoint)
+func (s *ReceivePackSuite) TestAdvertisedReferencesNotExists(c *C) {
+ r, err := s.Client.NewReceivePackSession(s.NonExistentEndpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
- info, err := r.AdvertisedReferences()
+ ar, err := r.AdvertisedReferences()
c.Assert(err, Equals, transport.ErrRepositoryNotFound)
- c.Assert(info, IsNil)
+ c.Assert(ar, IsNil)
- r, err = s.Client.NewSendPackSession(s.NonExistentEndpoint)
+ r, err = s.Client.NewReceivePackSession(s.NonExistentEndpoint)
c.Assert(err, IsNil)
req := packp.NewReferenceUpdateRequest()
req.Commands = []*packp.Command{
{"master", plumbing.ZeroHash, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")},
}
- writer, err := r.SendPack(req)
+ writer, err := r.ReceivePack(req)
c.Assert(err, Equals, transport.ErrRepositoryNotFound)
c.Assert(writer, IsNil)
}
-func (s *SendPackSuite) TestCallAdvertisedReferenceTwice(c *C) {
- r, err := s.Client.NewSendPackSession(s.Endpoint)
+func (s *ReceivePackSuite) TestCallAdvertisedReferenceTwice(c *C) {
+ r, err := s.Client.NewReceivePackSession(s.Endpoint)
c.Assert(err, IsNil)
ar1, err := r.AdvertisedReferences()
c.Assert(err, IsNil)
@@ -66,8 +66,8 @@ func (s *SendPackSuite) TestCallAdvertisedReferenceTwice(c *C) {
c.Assert(ar2, DeepEquals, ar1)
}
-func (s *SendPackSuite) TestDefaultBranch(c *C) {
- r, err := s.Client.NewSendPackSession(s.Endpoint)
+func (s *ReceivePackSuite) TestDefaultBranch(c *C) {
+ r, err := s.Client.NewReceivePackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -78,8 +78,8 @@ func (s *SendPackSuite) TestDefaultBranch(c *C) {
c.Assert(ref, Equals, fixtures.Basic().One().Head)
}
-func (s *SendPackSuite) TestCapabilities(c *C) {
- r, err := s.Client.NewSendPackSession(s.Endpoint)
+func (s *ReceivePackSuite) TestCapabilities(c *C) {
+ r, err := s.Client.NewReceivePackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -88,7 +88,7 @@ func (s *SendPackSuite) TestCapabilities(c *C) {
c.Assert(info.Capabilities.Get("agent"), HasLen, 1)
}
-func (s *SendPackSuite) TestFullSendPackOnEmpty(c *C) {
+func (s *ReceivePackSuite) TestFullSendPackOnEmpty(c *C) {
endpoint := s.EmptyEndpoint
full := true
fixture := fixtures.Basic().ByTag("packfile").One()
@@ -96,11 +96,11 @@ func (s *SendPackSuite) TestFullSendPackOnEmpty(c *C) {
req.Commands = []*packp.Command{
{"refs/heads/master", plumbing.ZeroHash, fixture.Head},
}
- s.sendPack(c, endpoint, req, fixture, full)
+ s.receivePack(c, endpoint, req, fixture, full)
s.checkRemoteHead(c, endpoint, fixture.Head)
}
-func (s *SendPackSuite) TestSendPackOnEmpty(c *C) {
+func (s *ReceivePackSuite) TestSendPackOnEmpty(c *C) {
endpoint := s.EmptyEndpoint
full := false
fixture := fixtures.Basic().ByTag("packfile").One()
@@ -108,11 +108,11 @@ func (s *SendPackSuite) TestSendPackOnEmpty(c *C) {
req.Commands = []*packp.Command{
{"refs/heads/master", plumbing.ZeroHash, fixture.Head},
}
- s.sendPack(c, endpoint, req, fixture, full)
+ s.receivePack(c, endpoint, req, fixture, full)
s.checkRemoteHead(c, endpoint, fixture.Head)
}
-func (s *SendPackSuite) TestSendPackOnEmptyWithReportStatus(c *C) {
+func (s *ReceivePackSuite) TestSendPackOnEmptyWithReportStatus(c *C) {
endpoint := s.EmptyEndpoint
full := false
fixture := fixtures.Basic().ByTag("packfile").One()
@@ -121,11 +121,11 @@ func (s *SendPackSuite) TestSendPackOnEmptyWithReportStatus(c *C) {
{"refs/heads/master", plumbing.ZeroHash, fixture.Head},
}
req.Capabilities.Set(capability.ReportStatus)
- s.sendPack(c, endpoint, req, fixture, full)
+ s.receivePack(c, endpoint, req, fixture, full)
s.checkRemoteHead(c, endpoint, fixture.Head)
}
-func (s *SendPackSuite) TestFullSendPackOnNonEmpty(c *C) {
+func (s *ReceivePackSuite) TestFullSendPackOnNonEmpty(c *C) {
endpoint := s.Endpoint
full := true
fixture := fixtures.Basic().ByTag("packfile").One()
@@ -133,11 +133,11 @@ func (s *SendPackSuite) TestFullSendPackOnNonEmpty(c *C) {
req.Commands = []*packp.Command{
{"refs/heads/master", fixture.Head, fixture.Head},
}
- s.sendPack(c, endpoint, req, fixture, full)
+ s.receivePack(c, endpoint, req, fixture, full)
s.checkRemoteHead(c, endpoint, fixture.Head)
}
-func (s *SendPackSuite) TestSendPackOnNonEmpty(c *C) {
+func (s *ReceivePackSuite) TestSendPackOnNonEmpty(c *C) {
endpoint := s.Endpoint
full := false
fixture := fixtures.Basic().ByTag("packfile").One()
@@ -145,11 +145,11 @@ func (s *SendPackSuite) TestSendPackOnNonEmpty(c *C) {
req.Commands = []*packp.Command{
{"refs/heads/master", fixture.Head, fixture.Head},
}
- s.sendPack(c, endpoint, req, fixture, full)
+ s.receivePack(c, endpoint, req, fixture, full)
s.checkRemoteHead(c, endpoint, fixture.Head)
}
-func (s *SendPackSuite) TestSendPackOnNonEmptyWithReportStatus(c *C) {
+func (s *ReceivePackSuite) TestSendPackOnNonEmptyWithReportStatus(c *C) {
endpoint := s.Endpoint
full := false
fixture := fixtures.Basic().ByTag("packfile").One()
@@ -159,11 +159,11 @@ func (s *SendPackSuite) TestSendPackOnNonEmptyWithReportStatus(c *C) {
}
req.Capabilities.Set(capability.ReportStatus)
- s.sendPack(c, endpoint, req, fixture, full)
+ s.receivePack(c, endpoint, req, fixture, full)
s.checkRemoteHead(c, endpoint, fixture.Head)
}
-func (s *SendPackSuite) TestSendPackOnNonEmptyWithReportStatusWithError(c *C) {
+func (s *ReceivePackSuite) TestSendPackOnNonEmptyWithReportStatusWithError(c *C) {
endpoint := s.Endpoint
full := false
fixture := fixtures.Basic().ByTag("packfile").One()
@@ -173,7 +173,7 @@ func (s *SendPackSuite) TestSendPackOnNonEmptyWithReportStatusWithError(c *C) {
}
req.Capabilities.Set(capability.ReportStatus)
- report, err := s.sendPackNoCheck(c, endpoint, req, fixture, full)
+ report, err := s.receivePackNoCheck(c, endpoint, req, fixture, full)
//XXX: Recent git versions return "failed to update ref", while older
// (>=1.9) return "failed to lock".
c.Assert(err, ErrorMatches, ".*(failed to update ref|failed to lock).*")
@@ -184,7 +184,7 @@ func (s *SendPackSuite) TestSendPackOnNonEmptyWithReportStatusWithError(c *C) {
s.checkRemoteHead(c, endpoint, fixture.Head)
}
-func (s *SendPackSuite) sendPackNoCheck(c *C, ep transport.Endpoint,
+func (s *ReceivePackSuite) receivePackNoCheck(c *C, ep transport.Endpoint,
req *packp.ReferenceUpdateRequest, fixture *fixtures.Fixture,
callAdvertisedReferences bool) (*packp.ReportStatus, error) {
url := ""
@@ -196,7 +196,7 @@ func (s *SendPackSuite) sendPackNoCheck(c *C, ep transport.Endpoint,
ep.String(), url, callAdvertisedReferences,
)
- r, err := s.Client.NewSendPackSession(ep)
+ r, err := s.Client.NewReceivePackSession(ep)
c.Assert(err, IsNil, comment)
defer func() { c.Assert(r.Close(), IsNil, comment) }()
@@ -213,10 +213,10 @@ func (s *SendPackSuite) sendPackNoCheck(c *C, ep transport.Endpoint,
req.Packfile = s.emptyPackfile()
}
- return r.SendPack(req)
+ return r.ReceivePack(req)
}
-func (s *SendPackSuite) sendPack(c *C, ep transport.Endpoint,
+func (s *ReceivePackSuite) receivePack(c *C, ep transport.Endpoint,
req *packp.ReferenceUpdateRequest, fixture *fixtures.Fixture,
callAdvertisedReferences bool) {
@@ -229,7 +229,7 @@ func (s *SendPackSuite) sendPack(c *C, ep transport.Endpoint,
"failed with ep=%s fixture=%s callAdvertisedReferences=%s",
ep.String(), url, callAdvertisedReferences,
)
- report, err := s.sendPackNoCheck(c, ep, req, fixture, callAdvertisedReferences)
+ report, err := s.receivePackNoCheck(c, ep, req, fixture, callAdvertisedReferences)
c.Assert(err, IsNil, comment)
if req.Capabilities.Supports(capability.ReportStatus) {
@@ -240,14 +240,14 @@ func (s *SendPackSuite) sendPack(c *C, ep transport.Endpoint,
}
}
-func (s *SendPackSuite) checkRemoteHead(c *C, ep transport.Endpoint, head plumbing.Hash) {
+func (s *ReceivePackSuite) checkRemoteHead(c *C, ep transport.Endpoint, head plumbing.Hash) {
s.checkRemoteReference(c, ep, "refs/heads/master", head)
}
-func (s *SendPackSuite) checkRemoteReference(c *C, ep transport.Endpoint,
+func (s *ReceivePackSuite) checkRemoteReference(c *C, ep transport.Endpoint,
refName string, head plumbing.Hash) {
- r, err := s.Client.NewFetchPackSession(ep)
+ r, err := s.Client.NewUploadPackSession(ep)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
ar, err := r.AdvertisedReferences()
@@ -261,13 +261,13 @@ func (s *SendPackSuite) checkRemoteReference(c *C, ep transport.Endpoint,
}
}
-func (s *SendPackSuite) TestSendPackAddDeleteReference(c *C) {
+func (s *ReceivePackSuite) TestSendPackAddDeleteReference(c *C) {
s.testSendPackAddReference(c)
s.testSendPackDeleteReference(c)
}
-func (s *SendPackSuite) testSendPackAddReference(c *C) {
- r, err := s.Client.NewSendPackSession(s.Endpoint)
+func (s *ReceivePackSuite) testSendPackAddReference(c *C) {
+ r, err := s.Client.NewReceivePackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -284,12 +284,12 @@ func (s *SendPackSuite) testSendPackAddReference(c *C) {
req.Capabilities.Set(capability.ReportStatus)
}
- s.sendPack(c, s.Endpoint, req, nil, false)
+ s.receivePack(c, s.Endpoint, req, nil, false)
s.checkRemoteReference(c, s.Endpoint, "refs/heads/newbranch", fixture.Head)
}
-func (s *SendPackSuite) testSendPackDeleteReference(c *C) {
- r, err := s.Client.NewSendPackSession(s.Endpoint)
+func (s *ReceivePackSuite) testSendPackDeleteReference(c *C) {
+ r, err := s.Client.NewReceivePackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -306,11 +306,11 @@ func (s *SendPackSuite) testSendPackDeleteReference(c *C) {
req.Capabilities.Set(capability.ReportStatus)
}
- s.sendPack(c, s.Endpoint, req, nil, false)
+ s.receivePack(c, s.Endpoint, req, nil, false)
s.checkRemoteReference(c, s.Endpoint, "refs/heads/newbranch", plumbing.ZeroHash)
}
-func (s *SendPackSuite) emptyPackfile() io.ReadCloser {
+func (s *ReceivePackSuite) emptyPackfile() io.ReadCloser {
var buf bytes.Buffer
e := packfile.NewEncoder(&buf, memory.NewStorage(), false)
_, err := e.Encode(nil)
diff --git a/plumbing/transport/test/fetch_pack.go b/plumbing/transport/test/upload_pack.go
index 2984154..5af4b29 100644
--- a/plumbing/transport/test/fetch_pack.go
+++ b/plumbing/transport/test/upload_pack.go
@@ -18,39 +18,39 @@ import (
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
)
-type FetchPackSuite struct {
+type UploadPackSuite struct {
Endpoint transport.Endpoint
EmptyEndpoint transport.Endpoint
NonExistentEndpoint transport.Endpoint
- Client transport.Client
+ Client transport.Transport
}
-func (s *FetchPackSuite) TestInfoEmpty(c *C) {
- r, err := s.Client.NewFetchPackSession(s.EmptyEndpoint)
+func (s *UploadPackSuite) TestAdvertisedReferencesEmpty(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.EmptyEndpoint)
c.Assert(err, IsNil)
- info, err := r.AdvertisedReferences()
+ ar, err := r.AdvertisedReferences()
c.Assert(err, Equals, transport.ErrEmptyRemoteRepository)
- c.Assert(info, IsNil)
+ c.Assert(ar, IsNil)
}
-func (s *FetchPackSuite) TestInfoNotExists(c *C) {
- r, err := s.Client.NewFetchPackSession(s.NonExistentEndpoint)
+func (s *UploadPackSuite) TestAdvertisedReferencesNotExists(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.NonExistentEndpoint)
c.Assert(err, IsNil)
- info, err := r.AdvertisedReferences()
+ ar, err := r.AdvertisedReferences()
c.Assert(err, Equals, transport.ErrRepositoryNotFound)
- c.Assert(info, IsNil)
+ c.Assert(ar, IsNil)
- r, err = s.Client.NewFetchPackSession(s.NonExistentEndpoint)
+ r, err = s.Client.NewUploadPackSession(s.NonExistentEndpoint)
c.Assert(err, IsNil)
req := packp.NewUploadPackRequest()
req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
- reader, err := r.FetchPack(req)
+ reader, err := r.UploadPack(req)
c.Assert(err, Equals, transport.ErrRepositoryNotFound)
c.Assert(reader, IsNil)
}
-func (s *FetchPackSuite) TestCallAdvertisedReferenceTwice(c *C) {
- r, err := s.Client.NewFetchPackSession(s.Endpoint)
+func (s *UploadPackSuite) TestCallAdvertisedReferenceTwice(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
ar1, err := r.AdvertisedReferences()
c.Assert(err, IsNil)
@@ -60,8 +60,8 @@ func (s *FetchPackSuite) TestCallAdvertisedReferenceTwice(c *C) {
c.Assert(ar2, DeepEquals, ar1)
}
-func (s *FetchPackSuite) TestDefaultBranch(c *C) {
- r, err := s.Client.NewFetchPackSession(s.Endpoint)
+func (s *UploadPackSuite) TestDefaultBranch(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -72,8 +72,8 @@ func (s *FetchPackSuite) TestDefaultBranch(c *C) {
c.Assert(symrefs[0], Equals, "HEAD:refs/heads/master")
}
-func (s *FetchPackSuite) TestAdvertisedReferencesFilterUnsupported(c *C) {
- r, err := s.Client.NewFetchPackSession(s.Endpoint)
+func (s *UploadPackSuite) TestAdvertisedReferencesFilterUnsupported(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -82,8 +82,8 @@ func (s *FetchPackSuite) TestAdvertisedReferencesFilterUnsupported(c *C) {
c.Assert(info.Capabilities.Supports(capability.MultiACK), Equals, false)
}
-func (s *FetchPackSuite) TestCapabilities(c *C) {
- r, err := s.Client.NewFetchPackSession(s.Endpoint)
+func (s *UploadPackSuite) TestCapabilities(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -92,8 +92,8 @@ func (s *FetchPackSuite) TestCapabilities(c *C) {
c.Assert(info.Capabilities.Get(capability.Agent), HasLen, 1)
}
-func (s *FetchPackSuite) TestFullFetchPack(c *C) {
- r, err := s.Client.NewFetchPackSession(s.Endpoint)
+func (s *UploadPackSuite) TestFullUploadPack(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -104,28 +104,28 @@ func (s *FetchPackSuite) TestFullFetchPack(c *C) {
req := packp.NewUploadPackRequest()
req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
- reader, err := r.FetchPack(req)
+ reader, err := r.UploadPack(req)
c.Assert(err, IsNil)
s.checkObjectNumber(c, reader, 28)
}
-func (s *FetchPackSuite) TestFetchPack(c *C) {
- r, err := s.Client.NewFetchPackSession(s.Endpoint)
+func (s *UploadPackSuite) TestUploadPack(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
req := packp.NewUploadPackRequest()
req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
- reader, err := r.FetchPack(req)
+ reader, err := r.UploadPack(req)
c.Assert(err, IsNil)
s.checkObjectNumber(c, reader, 28)
}
-func (s *FetchPackSuite) TestFetchPackInvalidReq(c *C) {
- r, err := s.Client.NewFetchPackSession(s.Endpoint)
+func (s *UploadPackSuite) TestUploadPackInvalidReq(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -134,12 +134,12 @@ func (s *FetchPackSuite) TestFetchPackInvalidReq(c *C) {
req.Capabilities.Set(capability.Sideband)
req.Capabilities.Set(capability.Sideband64k)
- _, err = r.FetchPack(req)
+ _, err = r.UploadPack(req)
c.Assert(err, NotNil)
}
-func (s *FetchPackSuite) TestFetchPackNoChanges(c *C) {
- r, err := s.Client.NewFetchPackSession(s.Endpoint)
+func (s *UploadPackSuite) TestUploadPackNoChanges(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -147,13 +147,13 @@ func (s *FetchPackSuite) TestFetchPackNoChanges(c *C) {
req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
req.Haves = append(req.Haves, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
- reader, err := r.FetchPack(req)
+ reader, err := r.UploadPack(req)
c.Assert(err, Equals, transport.ErrEmptyUploadPackRequest)
c.Assert(reader, IsNil)
}
-func (s *FetchPackSuite) TestFetchPackMulti(c *C) {
- r, err := s.Client.NewFetchPackSession(s.Endpoint)
+func (s *UploadPackSuite) TestUploadPackMulti(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
defer func() { c.Assert(r.Close(), IsNil) }()
@@ -161,28 +161,28 @@ func (s *FetchPackSuite) TestFetchPackMulti(c *C) {
req.Wants = append(req.Wants, plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
req.Wants = append(req.Wants, plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"))
- reader, err := r.FetchPack(req)
+ reader, err := r.UploadPack(req)
c.Assert(err, IsNil)
s.checkObjectNumber(c, reader, 31)
}
-func (s *FetchPackSuite) TestFetchError(c *C) {
- r, err := s.Client.NewFetchPackSession(s.Endpoint)
+func (s *UploadPackSuite) TestFetchError(c *C) {
+ r, err := s.Client.NewUploadPackSession(s.Endpoint)
c.Assert(err, IsNil)
req := packp.NewUploadPackRequest()
req.Wants = append(req.Wants, plumbing.NewHash("1111111111111111111111111111111111111111"))
- reader, err := r.FetchPack(req)
- c.Assert(err, Equals, transport.ErrEmptyUploadPackRequest)
+ reader, err := r.UploadPack(req)
+ c.Assert(err, NotNil)
c.Assert(reader, IsNil)
//XXX: We do not test Close error, since implementations might return
// different errors if a previous error was found.
}
-func (s *FetchPackSuite) checkObjectNumber(c *C, r io.Reader, n int) {
+func (s *UploadPackSuite) checkObjectNumber(c *C, r io.Reader, n int) {
b, err := ioutil.ReadAll(r)
c.Assert(err, IsNil)
buf := bytes.NewBuffer(b)
diff --git a/remote.go b/remote.go
index 102a764..e8d56fc 100644
--- a/remote.go
+++ b/remote.go
@@ -130,7 +130,7 @@ func (r *Remote) fetch(o *FetchOptions) (refs storer.ReferenceStorer, err error)
o.RefSpecs = r.c.Fetch
}
- s, err := newFetchPackSession(r.c.URL)
+ s, err := newUploadPackSession(r.c.URL)
if err != nil {
return nil, err
}
@@ -173,25 +173,25 @@ func (r *Remote) fetch(o *FetchOptions) (refs storer.ReferenceStorer, err error)
return remoteRefs, err
}
-func newFetchPackSession(url string) (transport.FetchPackSession, error) {
+func newUploadPackSession(url string) (transport.UploadPackSession, error) {
c, ep, err := newClient(url)
if err != nil {
return nil, err
}
- return c.NewFetchPackSession(ep)
+ return c.NewUploadPackSession(ep)
}
-func newSendPackSession(url string) (transport.SendPackSession, error) {
+func newSendPackSession(url string) (transport.ReceivePackSession, error) {
c, ep, err := newClient(url)
if err != nil {
return nil, err
}
- return c.NewSendPackSession(ep)
+ return c.NewReceivePackSession(ep)
}
-func newClient(url string) (transport.Client, transport.Endpoint, error) {
+func newClient(url string) (transport.Transport, transport.Endpoint, error) {
ep, err := transport.NewEndpoint(url)
if err != nil {
return nil, transport.Endpoint{}, err
@@ -205,10 +205,10 @@ func newClient(url string) (transport.Client, transport.Endpoint, error) {
return c, ep, err
}
-func (r *Remote) fetchPack(o *FetchOptions, s transport.FetchPackSession,
+func (r *Remote) fetchPack(o *FetchOptions, s transport.UploadPackSession,
req *packp.UploadPackRequest) (err error) {
- reader, err := s.FetchPack(req)
+ reader, err := s.UploadPack(req)
if err != nil {
return err
}
@@ -219,7 +219,7 @@ func (r *Remote) fetchPack(o *FetchOptions, s transport.FetchPackSession,
return err
}
- if err = r.updateObjectStorage(
+ if err = packfile.UpdateObjectStorage(r.s,
buildSidebandIfSupported(req.Capabilities, reader, r.p),
); err != nil {
return err
@@ -406,28 +406,6 @@ func (r *Remote) newUploadPackRequest(o *FetchOptions,
return req, nil
}
-func (r *Remote) updateObjectStorage(reader io.Reader) error {
- if sw, ok := r.s.(storer.PackfileWriter); ok {
- w, err := sw.PackfileWriter()
- if err != nil {
- return err
- }
-
- defer w.Close()
- _, err = io.Copy(w, reader)
- return err
- }
-
- stream := packfile.NewScanner(reader)
- d, err := packfile.NewDecoder(stream, r.s)
- if err != nil {
- return err
- }
-
- _, err = d.Decode()
- return err
-}
-
func buildSidebandIfSupported(l *capability.List, reader io.Reader, p sideband.Progress) io.Reader {
var t sideband.Type
@@ -532,7 +510,7 @@ func referencesToHashes(refs storer.ReferenceStorer) ([]plumbing.Hash, error) {
return hs, nil
}
-func pushHashes(sess transport.SendPackSession, sto storer.EncodedObjectStorer,
+func pushHashes(sess transport.ReceivePackSession, sto storer.EncodedObjectStorer,
req *packp.ReferenceUpdateRequest, hs []plumbing.Hash) (*packp.ReportStatus, error) {
rd, wr := io.Pipe()
@@ -548,7 +526,7 @@ func pushHashes(sess transport.SendPackSession, sto storer.EncodedObjectStorer,
done <- wr.Close()
}()
- rs, err := sess.SendPack(req)
+ rs, err := sess.ReceivePack(req)
if err != nil {
return nil, err
}