5
5
"errors"
6
6
"fmt"
7
7
"io"
8
+ "strings"
8
9
9
10
"github.com/go-git/go-git/v5/plumbing"
10
11
"github.com/go-git/go-git/v5/plumbing/format/pktline"
@@ -13,25 +14,52 @@ import (
13
14
const ackLineLen = 44
14
15
15
16
// ServerResponse object acknowledgement from upload-pack service
16
- // TODO: support multi_ack and multi_ack_detailed capabilities
17
17
type ServerResponse struct {
18
- ACKs []plumbing. Hash
18
+ ACKs []ACK
19
19
}
20
20
21
- // Decode decodes the response into the struct, isMultiACK should be true, if
22
- // the request was done with multi_ack or multi_ack_detailed capabilities.
21
+ // ACKStatus represents the status of an object acknowledgement.
22
+ type ACKStatus byte
23
+
24
+ // String returns the string representation of the ACKStatus.
25
+ func (s ACKStatus ) String () string {
26
+ switch s {
27
+ case ACKContinue :
28
+ return "continue"
29
+ case ACKCommon :
30
+ return "common"
31
+ case ACKReady :
32
+ return "ready"
33
+ }
34
+
35
+ return ""
36
+ }
37
+
38
+ // ACKStatus values
39
+ const (
40
+ ACKContinue ACKStatus = iota + 1
41
+ ACKCommon
42
+ ACKReady
43
+ )
44
+
45
+ // ACK represents an object acknowledgement. A status can be zero when the
46
+ // response doesn't support multi_ack and multi_ack_detailed capabilities.
47
+ type ACK struct {
48
+ Hash plumbing.Hash
49
+ Status ACKStatus
50
+ }
51
+
52
+ // Decode decodes the response into the struct.
23
53
func (r * ServerResponse ) Decode (reader io.Reader ) error {
24
54
var err error
25
- for {
55
+ for err == nil {
26
56
var p []byte
27
57
_ , p , err = pktline .ReadLine (reader )
28
58
if err != nil {
29
59
break
30
60
}
31
61
32
- if err := r .decodeLine (p ); err != nil {
33
- return err
34
- }
62
+ err = r .decodeLine (p )
35
63
}
36
64
37
65
if errors .Is (err , io .EOF ) {
@@ -52,31 +80,71 @@ func (r *ServerResponse) decodeLine(line []byte) error {
52
80
}
53
81
54
82
if bytes .Equal (line [0 :3 ], nak ) {
55
- return nil
83
+ return io . EOF
56
84
}
57
85
}
58
86
59
87
return fmt .Errorf ("unexpected content %q" , string (line ))
60
88
}
61
89
62
- func (r * ServerResponse ) decodeACKLine (line []byte ) error {
63
- if len (line ) < ackLineLen {
90
+ func (r * ServerResponse ) decodeACKLine (line []byte ) (err error ) {
91
+ parts := bytes .Split (line , []byte (" " ))
92
+ if len (line ) < ackLineLen || len (parts ) < 2 {
64
93
return fmt .Errorf ("malformed ACK %q" , line )
65
94
}
66
95
67
- sp := bytes .Index (line , []byte (" " ))
68
- h := plumbing .NewHash (string (line [sp + 1 : sp + 41 ]))
69
- r .ACKs = append (r .ACKs , h )
70
- return nil
96
+ var ack ACK
97
+ // TODO: Dynamic hash size and sha256 support
98
+ ack .Hash = plumbing .NewHash (string (parts [1 ]))
99
+ err = io .EOF
100
+
101
+ if len (parts ) > 2 {
102
+ err = nil
103
+ switch status := strings .TrimSpace (string (parts [2 ])); status {
104
+ case "continue" :
105
+ ack .Status = ACKContinue
106
+ case "common" :
107
+ ack .Status = ACKCommon
108
+ case "ready" :
109
+ ack .Status = ACKReady
110
+ }
111
+ }
112
+
113
+ r .ACKs = append (r .ACKs , ack )
114
+ return
71
115
}
72
116
73
117
// Encode encodes the ServerResponse into a writer.
74
118
func (r * ServerResponse ) Encode (w io.Writer ) error {
75
- if len (r .ACKs ) == 0 {
119
+ return encodeServerResponse (w , r .ACKs )
120
+ }
121
+
122
+ // encodeServerResponse encodes the ServerResponse into a writer.
123
+ func encodeServerResponse (w io.Writer , acks []ACK ) error {
124
+ if len (acks ) == 0 {
76
125
_ , err := pktline .WriteString (w , string (nak )+ "\n " )
77
126
return err
78
127
}
79
128
80
- _ , err := pktline .Writef (w , "%s %s\n " , ack , r .ACKs [0 ].String ())
81
- return err
129
+ var multiAck bool
130
+ for _ , a := range acks {
131
+ var err error
132
+ if a .Status > 0 {
133
+ _ , err = pktline .Writef (w , "%s %s %s\n " , ack , a .Hash , a .Status )
134
+ if ! multiAck {
135
+ multiAck = true
136
+ }
137
+ } else {
138
+ _ , err = pktline .Writef (w , "%s %s\n " , ack , acks [0 ].Hash )
139
+ }
140
+ if err != nil {
141
+ return err
142
+ }
143
+
144
+ if ! multiAck {
145
+ break
146
+ }
147
+ }
148
+
149
+ return nil
82
150
}
0 commit comments