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

Commit 841abfb

Browse files
authored
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.
1 parent 90d67bb commit 841abfb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1641
-308
lines changed

cli/go-git/main.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/jessevdk/go-flags"
8+
)
9+
10+
const (
11+
bin = "go-git"
12+
receivePackBin = "git-receive-pack"
13+
uploadPackBin = "git-upload-pack"
14+
)
15+
16+
func main() {
17+
switch filepath.Base(os.Args[0]) {
18+
case receivePackBin:
19+
os.Args = append([]string{"git", "receive-pack"}, os.Args[1:]...)
20+
case uploadPackBin:
21+
os.Args = append([]string{"git", "upload-pack"}, os.Args[1:]...)
22+
}
23+
24+
parser := flags.NewNamedParser(bin, flags.Default)
25+
parser.AddCommand("receive-pack", "", "", &CmdReceivePack{})
26+
parser.AddCommand("upload-pack", "", "", &CmdUploadPack{})
27+
parser.AddCommand("version", "Show the version information.", "", &CmdVersion{})
28+
29+
_, err := parser.Parse()
30+
if err != nil {
31+
if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrCommandRequired {
32+
parser.WriteHelp(os.Stdout)
33+
}
34+
35+
os.Exit(1)
36+
}
37+
}
38+
39+
type cmd struct {
40+
Verbose bool `short:"v" description:"Activates the verbose mode"`
41+
}

cli/go-git/receive_pack.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"gopkg.in/src-d/go-git.v4/plumbing/transport/file"
9+
)
10+
11+
type CmdReceivePack struct {
12+
cmd
13+
14+
Args struct {
15+
GitDir string `positional-arg-name:"git-dir" required:"true"`
16+
} `positional-args:"yes"`
17+
}
18+
19+
func (CmdReceivePack) Usage() string {
20+
//TODO: git-receive-pack returns error code 129 if arguments are invalid.
21+
return fmt.Sprintf("usage: %s <git-dir>", os.Args[0])
22+
}
23+
24+
func (c *CmdReceivePack) Execute(args []string) error {
25+
gitDir, err := filepath.Abs(c.Args.GitDir)
26+
if err != nil {
27+
return err
28+
}
29+
30+
if err := file.ServeReceivePack(gitDir); err != nil {
31+
fmt.Fprintln(os.Stderr, "ERR:", err)
32+
os.Exit(128)
33+
}
34+
35+
return nil
36+
}

cli/go-git/upload_pack.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"gopkg.in/src-d/go-git.v4/plumbing/transport/file"
9+
)
10+
11+
//TODO: usage: git upload-pack [--strict] [--timeout=<n>] <dir>
12+
type CmdUploadPack struct {
13+
cmd
14+
15+
Args struct {
16+
GitDir string `positional-arg-name:"git-dir" required:"true"`
17+
} `positional-args:"yes"`
18+
}
19+
20+
func (CmdUploadPack) Usage() string {
21+
//TODO: git-upload-pack returns error code 129 if arguments are invalid.
22+
return fmt.Sprintf("usage: %s <git-dir>", os.Args[0])
23+
}
24+
25+
func (c *CmdUploadPack) Execute(args []string) error {
26+
gitDir, err := filepath.Abs(c.Args.GitDir)
27+
if err != nil {
28+
return err
29+
}
30+
31+
if err := file.ServeUploadPack(gitDir); err != nil {
32+
fmt.Fprintln(os.Stderr, "ERR:", err)
33+
os.Exit(128)
34+
}
35+
36+
return nil
37+
}

cli/go-git/version.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package main
2+
3+
import "fmt"
4+
5+
var version string
6+
var build string
7+
8+
type CmdVersion struct{}
9+
10+
func (c *CmdVersion) Execute(args []string) error {
11+
fmt.Printf("%s (%s) - build %s\n", bin, version, build)
12+
13+
return nil
14+
}

plumbing/format/packfile/common.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package packfile
22

3+
import (
4+
"io"
5+
6+
"gopkg.in/src-d/go-git.v4/plumbing/storer"
7+
)
8+
39
var signature = []byte{'P', 'A', 'C', 'K'}
410

511
const (
@@ -13,3 +19,27 @@ const (
1319
maskLength = uint8(127) // 0111 1111
1420
maskType = uint8(112) // 0111 0000
1521
)
22+
23+
// UpdateObjectStorage updates the given ObjectStorer with the contents of the
24+
// packfile.
25+
func UpdateObjectStorage(s storer.EncodedObjectStorer, packfile io.Reader) error {
26+
if sw, ok := s.(storer.PackfileWriter); ok {
27+
w, err := sw.PackfileWriter()
28+
if err != nil {
29+
return err
30+
}
31+
32+
defer w.Close()
33+
_, err = io.Copy(w, packfile)
34+
return err
35+
}
36+
37+
stream := NewScanner(packfile)
38+
d, err := NewDecoder(stream, s)
39+
if err != nil {
40+
return err
41+
}
42+
43+
_, err = d.Decode()
44+
return err
45+
}

plumbing/protocol/packp/capability/list.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,16 @@ func (l *List) Delete(capability Capability) {
167167
}
168168
}
169169

170+
// All returns a slice with all defined capabilities.
171+
func (l *List) All() []Capability {
172+
var cs []Capability
173+
for _, key := range l.sort {
174+
cs = append(cs, Capability(key))
175+
}
176+
177+
return cs
178+
}
179+
170180
// String generates the capabilities strings, the capabilities are sorted in
171181
// insertion order
172182
func (l *List) String() string {

plumbing/protocol/packp/capability/list_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,14 @@ func (s *SuiteCapabilities) TestAddErrMultipleArgumentsAtTheSameTime(c *check.C)
171171
err := cap.Add(Agent, "foo", "bar")
172172
c.Assert(err, check.Equals, ErrMultipleArguments)
173173
}
174+
175+
func (s *SuiteCapabilities) TestAll(c *check.C) {
176+
cap := NewList()
177+
c.Assert(NewList().All(), check.IsNil)
178+
179+
cap.Add(Agent, "foo")
180+
c.Assert(cap.All(), check.DeepEquals, []Capability{Agent})
181+
182+
cap.Add(OFSDelta)
183+
c.Assert(cap.All(), check.DeepEquals, []Capability{Agent, OFSDelta})
184+
}

plumbing/protocol/packp/shallowupd.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func (r *ShallowUpdate) Decode(reader io.Reader) error {
2424

2525
for s.Scan() {
2626
line := s.Bytes()
27+
line = bytes.TrimSpace(line)
2728

2829
var err error
2930
switch {
@@ -71,3 +72,21 @@ func (r *ShallowUpdate) decodeLine(line, prefix []byte, expLen int) (plumbing.Ha
7172
raw := string(line[expLen-40 : expLen])
7273
return plumbing.NewHash(raw), nil
7374
}
75+
76+
func (r *ShallowUpdate) Encode(w io.Writer) error {
77+
e := pktline.NewEncoder(w)
78+
79+
for _, h := range r.Shallows {
80+
if err := e.Encodef("%s%s\n", shallow, h.String()); err != nil {
81+
return err
82+
}
83+
}
84+
85+
for _, h := range r.Unshallows {
86+
if err := e.Encodef("%s%s\n", unshallow, h.String()); err != nil {
87+
return err
88+
}
89+
}
90+
91+
return e.Flush()
92+
}

plumbing/protocol/packp/shallowupd_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,26 @@ type ShallowUpdateSuite struct{}
1212

1313
var _ = Suite(&ShallowUpdateSuite{})
1414

15+
func (s *ShallowUpdateSuite) TestDecodeWithLF(c *C) {
16+
raw := "" +
17+
"0035shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
18+
"0035shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" +
19+
"0000"
20+
21+
su := &ShallowUpdate{}
22+
err := su.Decode(bytes.NewBufferString(raw))
23+
c.Assert(err, IsNil)
24+
25+
plumbing.HashesSort(su.Shallows)
26+
27+
c.Assert(su.Unshallows, HasLen, 0)
28+
c.Assert(su.Shallows, HasLen, 2)
29+
c.Assert(su.Shallows, DeepEquals, []plumbing.Hash{
30+
plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
31+
plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
32+
})
33+
}
34+
1535
func (s *ShallowUpdateSuite) TestDecode(c *C) {
1636
raw := "" +
1737
"0034shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
@@ -61,3 +81,70 @@ func (s *ShallowUpdateSuite) TestDecodeMalformed(c *C) {
6181
err := su.Decode(bytes.NewBufferString(raw))
6282
c.Assert(err, NotNil)
6383
}
84+
85+
func (s *ShallowUpdateSuite) TestEncodeEmpty(c *C) {
86+
su := &ShallowUpdate{}
87+
buf := bytes.NewBuffer(nil)
88+
c.Assert(su.Encode(buf), IsNil)
89+
c.Assert(buf.String(), Equals, "0000")
90+
}
91+
92+
func (s *ShallowUpdateSuite) TestEncode(c *C) {
93+
su := &ShallowUpdate{
94+
Shallows: []plumbing.Hash{
95+
plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
96+
plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
97+
},
98+
Unshallows: []plumbing.Hash{
99+
plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
100+
plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
101+
},
102+
}
103+
buf := bytes.NewBuffer(nil)
104+
c.Assert(su.Encode(buf), IsNil)
105+
106+
expected := "" +
107+
"0035shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
108+
"0035shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" +
109+
"0037unshallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
110+
"0037unshallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" +
111+
"0000"
112+
113+
c.Assert(buf.String(), Equals, expected)
114+
}
115+
116+
func (s *ShallowUpdateSuite) TestEncodeShallow(c *C) {
117+
su := &ShallowUpdate{
118+
Shallows: []plumbing.Hash{
119+
plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
120+
plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
121+
},
122+
}
123+
buf := bytes.NewBuffer(nil)
124+
c.Assert(su.Encode(buf), IsNil)
125+
126+
expected := "" +
127+
"0035shallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
128+
"0035shallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" +
129+
"0000"
130+
131+
c.Assert(buf.String(), Equals, expected)
132+
}
133+
134+
func (s *ShallowUpdateSuite) TestEncodeUnshallow(c *C) {
135+
su := &ShallowUpdate{
136+
Unshallows: []plumbing.Hash{
137+
plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
138+
plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
139+
},
140+
}
141+
buf := bytes.NewBuffer(nil)
142+
c.Assert(su.Encode(buf), IsNil)
143+
144+
expected := "" +
145+
"0037unshallow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" +
146+
"0037unshallow bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n" +
147+
"0000"
148+
149+
c.Assert(buf.String(), Equals, expected)
150+
}

plumbing/protocol/packp/srvresp.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,17 @@ func (r *ServerResponse) decodeACKLine(line []byte) error {
6868
r.ACKs = append(r.ACKs, h)
6969
return nil
7070
}
71+
72+
// Encode encodes the ServerResponse into a writer.
73+
func (r *ServerResponse) Encode(w io.Writer) error {
74+
if len(r.ACKs) > 1 {
75+
return errors.New("multi_ack and multi_ack_detailed are not supported")
76+
}
77+
78+
e := pktline.NewEncoder(w)
79+
if len(r.ACKs) == 0 {
80+
return e.Encodef("%s\n", nak)
81+
}
82+
83+
return e.Encodef("%s %s\n", ack, r.ACKs[0].String())
84+
}

plumbing/protocol/packp/updreq_decode.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ func parseCommand(b []byte) (*Command, error) {
225225
return nil, errInvalidNewObjId(err)
226226
}
227227

228-
return &Command{Old: oh, New: nh, Name: n}, nil
228+
return &Command{Old: oh, New: nh, Name: plumbing.ReferenceName(n)}, nil
229229
}
230230

231231
func parseHash(s string) (plumbing.Hash, error) {

0 commit comments

Comments
 (0)