@@ -2,6 +2,7 @@ package packp
2
2
3
3
import (
4
4
"bytes"
5
+ "fmt"
5
6
"io"
6
7
"sort"
7
8
@@ -21,9 +22,13 @@ func (a *AdvRefs) Encode(w io.Writer) error {
21
22
}
22
23
23
24
type advRefsEncoder struct {
24
- data * AdvRefs // data to encode
25
- pe * pktline.Encoder // where to write the encoded data
26
- err error // sticky error
25
+ data * AdvRefs // data to encode
26
+ pe * pktline.Encoder // where to write the encoded data
27
+ firstRefName string // reference name to encode in the first pkt-line (HEAD if present)
28
+ firstRefHash plumbing.Hash // hash referenced to encode in the first pkt-line (HEAD if present)
29
+ sortedRefs []string // hash references to encode ordered by increasing order
30
+ err error // sticky error
31
+
27
32
}
28
33
29
34
func newAdvRefsEncoder (w io.Writer ) * advRefsEncoder {
@@ -34,6 +39,8 @@ func newAdvRefsEncoder(w io.Writer) *advRefsEncoder {
34
39
35
40
func (e * advRefsEncoder ) Encode (v * AdvRefs ) error {
36
41
e .data = v
42
+ e .sortRefs ()
43
+ e .setFirstRef ()
37
44
38
45
for state := encodePrefix ; state != nil ; {
39
46
state = state (e )
@@ -42,6 +49,32 @@ func (e *advRefsEncoder) Encode(v *AdvRefs) error {
42
49
return e .err
43
50
}
44
51
52
+ func (e * advRefsEncoder ) sortRefs () {
53
+ if len (e .data .References ) > 0 {
54
+ refs := make ([]string , 0 , len (e .data .References ))
55
+ for refName := range e .data .References {
56
+ refs = append (refs , refName )
57
+ }
58
+
59
+ sort .Strings (refs )
60
+ e .sortedRefs = refs
61
+ }
62
+ }
63
+
64
+ func (e * advRefsEncoder ) setFirstRef () {
65
+ if e .data .Head != nil {
66
+ e .firstRefName = head
67
+ e .firstRefHash = * e .data .Head
68
+ return
69
+ }
70
+
71
+ if len (e .sortedRefs ) > 0 {
72
+ refName := e .sortedRefs [0 ]
73
+ e .firstRefName = refName
74
+ e .firstRefHash = e .data .References [refName ]
75
+ }
76
+ }
77
+
45
78
type encoderStateFn func (* advRefsEncoder ) encoderStateFn
46
79
47
80
func encodePrefix (e * advRefsEncoder ) encoderStateFn {
@@ -61,33 +94,27 @@ func encodePrefix(e *advRefsEncoder) encoderStateFn {
61
94
}
62
95
63
96
// Adds the first pkt-line payload: head hash, head ref and capabilities.
64
- // Also handle the special case when no HEAD ref is found.
97
+ // If HEAD ref is not found, the first reference ordered in increasing order will be used.
98
+ // If there aren't HEAD neither refs, the first line will be "PKT-LINE(zero-id SP "capabilities^{}" NUL capability-list)".
99
+ // See: https://github.com/git/git/blob/master/Documentation/technical/pack-protocol.txt
100
+ // See: https://github.com/git/git/blob/master/Documentation/technical/protocol-common.txt
65
101
func encodeFirstLine (e * advRefsEncoder ) encoderStateFn {
66
- head := formatHead ( e . data . Head )
67
- separator := formatSeparator ( e . data . Head )
102
+ const formatFirstLine = "%s %s \x00 %s \n "
103
+ var firstLine string
68
104
capabilities := formatCaps (e .data .Capabilities )
69
105
70
- if e .err = e .pe .Encodef ("%s %s\x00 %s\n " , head , separator , capabilities ); e .err != nil {
71
- return nil
72
- }
106
+ if e .firstRefName == "" {
107
+ firstLine = fmt .Sprintf (formatFirstLine , plumbing .ZeroHash .String (), "capabilities^{}" , capabilities )
108
+ } else {
109
+ firstLine = fmt .Sprintf (formatFirstLine , e .firstRefHash .String (), e .firstRefName , capabilities )
73
110
74
- return encodeRefs
75
- }
76
-
77
- func formatHead (h * plumbing.Hash ) string {
78
- if h == nil {
79
- return plumbing .ZeroHash .String ()
80
111
}
81
112
82
- return h .String ()
83
- }
84
-
85
- func formatSeparator (h * plumbing.Hash ) string {
86
- if h == nil {
87
- return noHead
113
+ if e .err = e .pe .EncodeString (firstLine ); e .err != nil {
114
+ return nil
88
115
}
89
116
90
- return head
117
+ return encodeRefs
91
118
}
92
119
93
120
func formatCaps (c * capability.List ) string {
@@ -101,8 +128,11 @@ func formatCaps(c *capability.List) string {
101
128
// Adds the (sorted) refs: hash SP refname EOL
102
129
// and their peeled refs if any.
103
130
func encodeRefs (e * advRefsEncoder ) encoderStateFn {
104
- refs := sortRefs (e .data .References )
105
- for _ , r := range refs {
131
+ for _ , r := range e .sortedRefs {
132
+ if r == e .firstRefName {
133
+ continue
134
+ }
135
+
106
136
hash , _ := e .data .References [r ]
107
137
if e .err = e .pe .Encodef ("%s %s\n " , hash .String (), r ); e .err != nil {
108
138
return nil
@@ -118,16 +148,6 @@ func encodeRefs(e *advRefsEncoder) encoderStateFn {
118
148
return encodeShallow
119
149
}
120
150
121
- func sortRefs (m map [string ]plumbing.Hash ) []string {
122
- ret := make ([]string , 0 , len (m ))
123
- for k := range m {
124
- ret = append (ret , k )
125
- }
126
- sort .Strings (ret )
127
-
128
- return ret
129
- }
130
-
131
151
// Adds the (sorted) shallows: "shallow" SP hash EOL
132
152
func encodeShallow (e * advRefsEncoder ) encoderStateFn {
133
153
sorted := sortShallows (e .data .Shallows )
0 commit comments