Skip to content

Commit 9973a38

Browse files
authored
Merge pull request #1251 from BeChris/index_v4_encoding
plumbing: Properly encode index version 4
2 parents 2915a1d + e2e8cbf commit 9973a38

File tree

2 files changed

+108
-10
lines changed

2 files changed

+108
-10
lines changed

plumbing/format/index/encoder.go

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"errors"
66
"fmt"
77
"io"
8+
"path"
89
"sort"
10+
"strings"
911
"time"
1012

1113
"github.com/go-git/go-git/v5/plumbing/hash"
@@ -14,7 +16,7 @@ import (
1416

1517
var (
1618
// EncodeVersionSupported is the range of supported index versions
17-
EncodeVersionSupported uint32 = 3
19+
EncodeVersionSupported uint32 = 4
1820

1921
// ErrInvalidTimestamp is returned by Encode if a Index with a Entry with
2022
// negative timestamp values
@@ -23,15 +25,16 @@ var (
2325

2426
// An Encoder writes an Index to an output stream.
2527
type Encoder struct {
26-
w io.Writer
27-
hash hash.Hash
28+
w io.Writer
29+
hash hash.Hash
30+
lastEntry *Entry
2831
}
2932

3033
// NewEncoder returns a new encoder that writes to w.
3134
func NewEncoder(w io.Writer) *Encoder {
3235
h := hash.New(hash.CryptoType)
3336
mw := io.MultiWriter(w, h)
34-
return &Encoder{mw, h}
37+
return &Encoder{mw, h, nil}
3538
}
3639

3740
// Encode writes the Index to the stream of the encoder.
@@ -41,7 +44,6 @@ func (e *Encoder) Encode(idx *Index) error {
4144

4245
func (e *Encoder) encode(idx *Index, footer bool) error {
4346

44-
// TODO: support v4
4547
// TODO: support extensions
4648
if idx.Version > EncodeVersionSupported {
4749
return ErrUnsupportedVersion
@@ -73,7 +75,7 @@ func (e *Encoder) encodeEntries(idx *Index) error {
7375
sort.Sort(byName(idx.Entries))
7476

7577
for _, entry := range idx.Entries {
76-
if err := e.encodeEntry(entry); err != nil {
78+
if err := e.encodeEntry(idx, entry); err != nil {
7779
return err
7880
}
7981
entryLength := entryHeaderLength
@@ -82,15 +84,15 @@ func (e *Encoder) encodeEntries(idx *Index) error {
8284
}
8385

8486
wrote := entryLength + len(entry.Name)
85-
if err := e.padEntry(wrote); err != nil {
87+
if err := e.padEntry(idx, wrote); err != nil {
8688
return err
8789
}
8890
}
8991

9092
return nil
9193
}
9294

93-
func (e *Encoder) encodeEntry(entry *Entry) error {
95+
func (e *Encoder) encodeEntry(idx *Index, entry *Entry) error {
9496
sec, nsec, err := e.timeToUint32(&entry.CreatedAt)
9597
if err != nil {
9698
return err
@@ -141,9 +143,45 @@ func (e *Encoder) encodeEntry(entry *Entry) error {
141143
return err
142144
}
143145

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 {
144159
return binary.Write(e.w, []byte(entry.Name))
145160
}
146161

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+
147185
func (e *Encoder) encodeRawExtension(signature string, data []byte) error {
148186
if len(signature) != 4 {
149187
return fmt.Errorf("invalid signature length")
@@ -179,7 +217,11 @@ func (e *Encoder) timeToUint32(t *time.Time) (uint32, uint32, error) {
179217
return uint32(t.Unix()), uint32(t.Nanosecond()), nil
180218
}
181219

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+
183225
padLen := 8 - wrote%8
184226

185227
_, err := e.w.Write(bytes.Repeat([]byte{'\x00'}, padLen))

plumbing/format/index/encoder_test.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,64 @@ func (s *IndexSuite) TestEncode(c *C) {
5656

5757
}
5858

59+
func (s *IndexSuite) TestEncodeV4(c *C) {
60+
idx := &Index{
61+
Version: 4,
62+
Entries: []*Entry{{
63+
CreatedAt: time.Now(),
64+
ModifiedAt: time.Now(),
65+
Dev: 4242,
66+
Inode: 424242,
67+
UID: 84,
68+
GID: 8484,
69+
Size: 42,
70+
Stage: TheirMode,
71+
Hash: plumbing.NewHash("e25b29c8946e0e192fae2edc1dabf7be71e8ecf3"),
72+
Name: "foo",
73+
}, {
74+
CreatedAt: time.Now(),
75+
ModifiedAt: time.Now(),
76+
Name: "bar",
77+
Size: 82,
78+
}, {
79+
CreatedAt: time.Now(),
80+
ModifiedAt: time.Now(),
81+
Name: strings.Repeat(" ", 20),
82+
Size: 82,
83+
}, {
84+
CreatedAt: time.Now(),
85+
ModifiedAt: time.Now(),
86+
Name: "baz/bar",
87+
Size: 82,
88+
}, {
89+
CreatedAt: time.Now(),
90+
ModifiedAt: time.Now(),
91+
Name: "baz/bar/bar",
92+
Size: 82,
93+
}},
94+
}
95+
96+
buf := bytes.NewBuffer(nil)
97+
e := NewEncoder(buf)
98+
err := e.Encode(idx)
99+
c.Assert(err, IsNil)
100+
101+
output := &Index{}
102+
d := NewDecoder(buf)
103+
err = d.Decode(output)
104+
c.Assert(err, IsNil)
105+
106+
c.Assert(cmp.Equal(idx, output), Equals, true)
107+
108+
c.Assert(output.Entries[0].Name, Equals, strings.Repeat(" ", 20))
109+
c.Assert(output.Entries[1].Name, Equals, "bar")
110+
c.Assert(output.Entries[2].Name, Equals, "baz/bar")
111+
c.Assert(output.Entries[3].Name, Equals, "baz/bar/bar")
112+
c.Assert(output.Entries[4].Name, Equals, "foo")
113+
}
114+
59115
func (s *IndexSuite) TestEncodeUnsupportedVersion(c *C) {
60-
idx := &Index{Version: 4}
116+
idx := &Index{Version: 5}
61117

62118
buf := bytes.NewBuffer(nil)
63119
e := NewEncoder(buf)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy