5
5
"errors"
6
6
"fmt"
7
7
"io"
8
+ "path"
8
9
"sort"
10
+ "strings"
9
11
"time"
10
12
11
13
"github.com/go-git/go-git/v5/plumbing/hash"
@@ -14,7 +16,7 @@ import (
14
16
15
17
var (
16
18
// EncodeVersionSupported is the range of supported index versions
17
- EncodeVersionSupported uint32 = 3
19
+ EncodeVersionSupported uint32 = 4
18
20
19
21
// ErrInvalidTimestamp is returned by Encode if a Index with a Entry with
20
22
// negative timestamp values
@@ -23,15 +25,16 @@ var (
23
25
24
26
// An Encoder writes an Index to an output stream.
25
27
type Encoder struct {
26
- w io.Writer
27
- hash hash.Hash
28
+ w io.Writer
29
+ hash hash.Hash
30
+ lastEntry * Entry
28
31
}
29
32
30
33
// NewEncoder returns a new encoder that writes to w.
31
34
func NewEncoder (w io.Writer ) * Encoder {
32
35
h := hash .New (hash .CryptoType )
33
36
mw := io .MultiWriter (w , h )
34
- return & Encoder {mw , h }
37
+ return & Encoder {mw , h , nil }
35
38
}
36
39
37
40
// Encode writes the Index to the stream of the encoder.
@@ -41,7 +44,6 @@ func (e *Encoder) Encode(idx *Index) error {
41
44
42
45
func (e * Encoder ) encode (idx * Index , footer bool ) error {
43
46
44
- // TODO: support v4
45
47
// TODO: support extensions
46
48
if idx .Version > EncodeVersionSupported {
47
49
return ErrUnsupportedVersion
@@ -73,7 +75,7 @@ func (e *Encoder) encodeEntries(idx *Index) error {
73
75
sort .Sort (byName (idx .Entries ))
74
76
75
77
for _ , entry := range idx .Entries {
76
- if err := e .encodeEntry (entry ); err != nil {
78
+ if err := e .encodeEntry (idx , entry ); err != nil {
77
79
return err
78
80
}
79
81
entryLength := entryHeaderLength
@@ -82,15 +84,15 @@ func (e *Encoder) encodeEntries(idx *Index) error {
82
84
}
83
85
84
86
wrote := entryLength + len (entry .Name )
85
- if err := e .padEntry (wrote ); err != nil {
87
+ if err := e .padEntry (idx , wrote ); err != nil {
86
88
return err
87
89
}
88
90
}
89
91
90
92
return nil
91
93
}
92
94
93
- func (e * Encoder ) encodeEntry (entry * Entry ) error {
95
+ func (e * Encoder ) encodeEntry (idx * Index , entry * Entry ) error {
94
96
sec , nsec , err := e .timeToUint32 (& entry .CreatedAt )
95
97
if err != nil {
96
98
return err
@@ -141,9 +143,45 @@ func (e *Encoder) encodeEntry(entry *Entry) error {
141
143
return err
142
144
}
143
145
146
+ switch idx .Version {
147
+ case 2 , 3 :
148
+ err = e .encodeEntryName (entry )
149
+ case 4 :
150
+ err = e .encodeEntryNameV4 (entry )
151
+ default :
152
+ err = ErrUnsupportedVersion
153
+ }
154
+
155
+ return err
156
+ }
157
+
158
+ func (e * Encoder ) encodeEntryName (entry * Entry ) error {
144
159
return binary .Write (e .w , []byte (entry .Name ))
145
160
}
146
161
162
+ func (e * Encoder ) encodeEntryNameV4 (entry * Entry ) error {
163
+ name := entry .Name
164
+ l := 0
165
+ if e .lastEntry != nil {
166
+ dir := path .Dir (e .lastEntry .Name ) + "/"
167
+ if strings .HasPrefix (entry .Name , dir ) {
168
+ l = len (e .lastEntry .Name ) - len (dir )
169
+ name = strings .TrimPrefix (entry .Name , dir )
170
+ } else {
171
+ l = len (e .lastEntry .Name )
172
+ }
173
+ }
174
+
175
+ e .lastEntry = entry
176
+
177
+ err := binary .WriteVariableWidthInt (e .w , int64 (l ))
178
+ if err != nil {
179
+ return err
180
+ }
181
+
182
+ return binary .Write (e .w , []byte (name + string ('\x00' )))
183
+ }
184
+
147
185
func (e * Encoder ) encodeRawExtension (signature string , data []byte ) error {
148
186
if len (signature ) != 4 {
149
187
return fmt .Errorf ("invalid signature length" )
@@ -179,7 +217,11 @@ func (e *Encoder) timeToUint32(t *time.Time) (uint32, uint32, error) {
179
217
return uint32 (t .Unix ()), uint32 (t .Nanosecond ()), nil
180
218
}
181
219
182
- func (e * Encoder ) padEntry (wrote int ) error {
220
+ func (e * Encoder ) padEntry (idx * Index , wrote int ) error {
221
+ if idx .Version == 4 {
222
+ return nil
223
+ }
224
+
183
225
padLen := 8 - wrote % 8
184
226
185
227
_ , err := e .w .Write (bytes .Repeat ([]byte {'\x00' }, padLen ))
0 commit comments