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

server: add git server implementation #190

Merged
merged 14 commits into from
Jan 4, 2017
Merged
63 changes: 63 additions & 0 deletions cli/go-git/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"fmt"
"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"`
}

func (c *cmd) print(format string, a ...interface{}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this dead code?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Removed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

if !c.Verbose {
return
}

fmt.Printf(format, a...)
}

func resolvePath(path string) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be done calling filepath.Abs(path)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

if filepath.IsAbs(path) {
return path, nil
}

wd, err := os.Getwd()
if err != nil {
return path, err
}

return filepath.Join(wd, path), nil
}
35 changes: 35 additions & 0 deletions cli/go-git/receive_pack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"fmt"
"os"

"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 := resolvePath(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
}
36 changes: 36 additions & 0 deletions cli/go-git/upload_pack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"fmt"
"os"

"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 := resolvePath(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
}
14 changes: 14 additions & 0 deletions cli/go-git/version.go
Original file line number Diff line number Diff line change
@@ -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
}
30 changes: 30 additions & 0 deletions plumbing/format/packfile/common.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -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
}
10 changes: 10 additions & 0 deletions plumbing/protocol/packp/capability/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 11 additions & 0 deletions plumbing/protocol/packp/capability/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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})
}
19 changes: 19 additions & 0 deletions plumbing/protocol/packp/shallowupd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
}
87 changes: 87 additions & 0 deletions plumbing/protocol/packp/shallowupd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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" +
Expand Down Expand Up @@ -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)
}
14 changes: 14 additions & 0 deletions plumbing/protocol/packp/srvresp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
2 changes: 1 addition & 1 deletion plumbing/protocol/packp/updreq_decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading