@@ -2,6 +2,7 @@ package packp
22
33import (
44 "bytes"
5+ "fmt"
56 "io"
67 "sort"
78
@@ -21,9 +22,13 @@ func (a *AdvRefs) Encode(w io.Writer) error {
2122}
2223
2324type 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+
2732}
2833
2934func newAdvRefsEncoder (w io.Writer ) * advRefsEncoder {
@@ -34,6 +39,8 @@ func newAdvRefsEncoder(w io.Writer) *advRefsEncoder {
3439
3540func (e * advRefsEncoder ) Encode (v * AdvRefs ) error {
3641 e .data = v
42+ e .sortRefs ()
43+ e .setFirstRef ()
3744
3845 for state := encodePrefix ; state != nil ; {
3946 state = state (e )
@@ -42,6 +49,32 @@ func (e *advRefsEncoder) Encode(v *AdvRefs) error {
4249 return e .err
4350}
4451
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+
4578type encoderStateFn func (* advRefsEncoder ) encoderStateFn
4679
4780func encodePrefix (e * advRefsEncoder ) encoderStateFn {
@@ -61,33 +94,27 @@ func encodePrefix(e *advRefsEncoder) encoderStateFn {
6194}
6295
6396// 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
65101func 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
68104 capabilities := formatCaps (e .data .Capabilities )
69105
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 )
73110
74- return encodeRefs
75- }
76-
77- func formatHead (h * plumbing.Hash ) string {
78- if h == nil {
79- return plumbing .ZeroHash .String ()
80111 }
81112
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
88115 }
89116
90- return head
117+ return encodeRefs
91118}
92119
93120func formatCaps (c * capability.List ) string {
@@ -101,8 +128,11 @@ func formatCaps(c *capability.List) string {
101128// Adds the (sorted) refs: hash SP refname EOL
102129// and their peeled refs if any.
103130func 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+
106136 hash , _ := e .data .References [r ]
107137 if e .err = e .pe .Encodef ("%s %s\n " , hash .String (), r ); e .err != nil {
108138 return nil
@@ -118,16 +148,6 @@ func encodeRefs(e *advRefsEncoder) encoderStateFn {
118148 return encodeShallow
119149}
120150
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-
131151// Adds the (sorted) shallows: "shallow" SP hash EOL
132152func encodeShallow (e * advRefsEncoder ) encoderStateFn {
133153 sorted := sortShallows (e .data .Shallows )
0 commit comments