Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit b5da4e9

Browse files
authored
Fix issue 275 (edited) (#276)
Fix #275 . It was not possible to write a test for this issue as the original fsnoder didn't support filenames with length > 1. Therefore this patch has 3 commits: add support for long filenames in fsnoder. add a test case for the issue using the new long filenames from step 1. fix the issue by comparing paths level by level instead of lexigographically over the whole path.
1 parent a79bc09 commit b5da4e9

File tree

8 files changed

+302
-81
lines changed

8 files changed

+302
-81
lines changed

utils/merkletrie/difftree.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,6 @@ package merkletrie
249249

250250
import (
251251
"fmt"
252-
"strings"
253252

254253
"srcd.works/go-git.v4/utils/merkletrie/noder"
255254
)
@@ -302,7 +301,7 @@ func diffNodes(changes *Changes, ii *doubleIter) error {
302301
var err error
303302

304303
// compare their full paths as strings
305-
switch strings.Compare(from.String(), to.String()) {
304+
switch from.Compare(to) {
306305
case -1:
307306
if err = changes.AddRecursiveDelete(from); err != nil {
308307
return err

utils/merkletrie/difftree_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,3 +427,13 @@ func (s *DiffTreeSuite) TestSameNames(c *C) {
427427
},
428428
})
429429
}
430+
431+
func (s *DiffTreeSuite) TestIssue275(c *C) {
432+
do(c, []diffTreeTest{
433+
{
434+
"(a(b(c.go<1>) b.go<2>))",
435+
"(a(b(c.go<1> d.go<3>) b.go<2>))",
436+
"+a/b/d.go",
437+
},
438+
})
439+
}

utils/merkletrie/internal/fsnoder/doc.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ will create a noder as follows:
1818
1919
Files are expressed as:
2020
21-
- a single letter for the name of the file.
21+
- one or more letters and dots for the name of the file
2222
2323
- a single number, between angle brackets, for the contents of the file.
2424
25+
- examples: a<1>, foo.go<2>.
26+
2527
Directories are expressed as:
2628
27-
- a single letter for the name of the directory.
29+
- one or more letters for the name of the directory.
2830
2931
- its elements between parents, separated with spaces, in any order.
3032

utils/merkletrie/internal/fsnoder/new.go

Lines changed: 69 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,24 @@ func decodeDir(data []byte, isRoot bool) (*dir, error) {
2929
return nil, io.EOF
3030
}
3131

32-
// get the name of the dir (a single letter) and remove it from the
33-
// data. In case the there is no name and isRoot is true, just use
34-
// "" as the name.
32+
// get the name of the dir and remove it from the data. In case the
33+
// there is no name and isRoot is true, just use "" as the name.
3534
var name string
36-
if data[0] == dirStartMark {
35+
switch end := bytes.IndexRune(data, dirStartMark); end {
36+
case -1:
37+
return nil, fmt.Errorf("%c not found")
38+
case 0:
3739
if isRoot {
3840
name = ""
3941
} else {
4042
return nil, fmt.Errorf("inner unnamed dirs not allowed: %s", data)
4143
}
42-
} else {
43-
name = string(data[0])
44-
data = data[1:]
44+
default:
45+
name = string(data[0:end])
46+
data = data[end:]
4547
}
4648

47-
// check that data is enclosed in parents and it is big enough and
48-
// remove them.
49-
if len(data) < 2 {
50-
return nil, fmt.Errorf("malformed data: too short")
51-
}
52-
if data[0] != dirStartMark {
53-
return nil, fmt.Errorf("malformed data: first %q not found",
54-
dirStartMark)
55-
}
49+
// check data ends with the dirEndMark
5650
if data[len(data)-1] != dirEndMark {
5751
return nil, fmt.Errorf("malformed data: last %q not found",
5852
dirEndMark)
@@ -67,11 +61,11 @@ func decodeDir(data []byte, isRoot bool) (*dir, error) {
6761
return newDir(name, children)
6862
}
6963

70-
func isNumber(b byte) bool {
64+
func isNumber(b rune) bool {
7165
return '0' <= b && b <= '9'
7266
}
7367

74-
func isLetter(b byte) bool {
68+
func isLetter(b rune) bool {
7569
return ('a' <= b && b <= 'z') || ('A' <= b && b <= 'Z')
7670
}
7771

@@ -126,64 +120,77 @@ func decodeChild(data []byte) (noder.Noder, error) {
126120
return nil, fmt.Errorf("element too short: %s", clean)
127121
}
128122

129-
switch clean[1] {
130-
case fileStartMark:
123+
fileNameEnd := bytes.IndexRune(data, fileStartMark)
124+
dirNameEnd := bytes.IndexRune(data, dirStartMark)
125+
switch {
126+
case fileNameEnd == -1 && dirNameEnd == -1:
127+
return nil, fmt.Errorf(
128+
"malformed child, no file or dir start mark found")
129+
case fileNameEnd == -1:
130+
return decodeDir(clean, nonRoot)
131+
case dirNameEnd == -1:
131132
return decodeFile(clean)
132-
case dirStartMark:
133+
case dirNameEnd < fileNameEnd:
133134
return decodeDir(clean, nonRoot)
134-
default:
135-
if clean[0] == dirStartMark {
136-
return nil, fmt.Errorf("non-root unnamed dir are not allowed: %s",
137-
clean)
138-
}
139-
return nil, fmt.Errorf("malformed dir element: %s", clean)
135+
case dirNameEnd > fileNameEnd:
136+
return decodeFile(clean)
140137
}
138+
139+
return nil, fmt.Errorf("unreachable")
141140
}
142141

143142
func decodeFile(data []byte) (noder.Noder, error) {
144-
if len(data) == 3 {
145-
return decodeEmptyFile(data)
143+
nameEnd := bytes.IndexRune(data, fileStartMark)
144+
if nameEnd == -1 {
145+
return nil, fmt.Errorf("malformed file, no %c found", fileStartMark)
146+
}
147+
contentStart := nameEnd + 1
148+
contentEnd := bytes.IndexRune(data, fileEndMark)
149+
if contentEnd == -1 {
150+
return nil, fmt.Errorf("malformed file, no %c found", fileEndMark)
151+
}
152+
153+
switch {
154+
case nameEnd > contentEnd:
155+
return nil, fmt.Errorf("malformed file, found %c before %c",
156+
fileEndMark, fileStartMark)
157+
case contentStart == contentEnd:
158+
name := string(data[:nameEnd])
159+
if !validFileName(name) {
160+
return nil, fmt.Errorf("invalid file name")
161+
}
162+
return newFile(name, "")
163+
default:
164+
name := string(data[:nameEnd])
165+
if !validFileName(name) {
166+
return nil, fmt.Errorf("invalid file name")
167+
}
168+
contents := string(data[contentStart:contentEnd])
169+
if !validFileContents(contents) {
170+
return nil, fmt.Errorf("invalid file contents")
171+
}
172+
return newFile(name, contents)
146173
}
174+
}
147175

148-
if len(data) != 4 {
149-
return nil, fmt.Errorf("length is not 4")
150-
}
151-
if !isLetter(data[0]) {
152-
return nil, fmt.Errorf("name must be a letter")
153-
}
154-
if data[1] != '<' {
155-
return nil, fmt.Errorf("wrong file start character")
156-
}
157-
if !isNumber(data[2]) {
158-
return nil, fmt.Errorf("contents must be a number")
159-
}
160-
if data[3] != '>' {
161-
return nil, fmt.Errorf("wrong file end character")
176+
func validFileName(s string) bool {
177+
for _, c := range s {
178+
if !isLetter(c) && c != '.' {
179+
return false
180+
}
162181
}
163182

164-
name := string(data[0])
165-
contents := string(data[2])
166-
167-
return newFile(name, contents)
183+
return true
168184
}
169185

170-
func decodeEmptyFile(data []byte) (noder.Noder, error) {
171-
if len(data) != 3 {
172-
return nil, fmt.Errorf("length is not 3: %s", data)
173-
}
174-
if !isLetter(data[0]) {
175-
return nil, fmt.Errorf("name must be a letter: %s", data)
176-
}
177-
if data[1] != '<' {
178-
return nil, fmt.Errorf("wrong file start character: %s", data)
179-
}
180-
if data[2] != '>' {
181-
return nil, fmt.Errorf("wrong file end character: %s", data)
186+
func validFileContents(s string) bool {
187+
for _, c := range s {
188+
if !isNumber(c) {
189+
return false
190+
}
182191
}
183192

184-
name := string(data[0])
185-
186-
return newFile(name, "")
193+
return true
187194
}
188195

189196
// HashEqual returns if a and b have the same hash.

utils/merkletrie/internal/fsnoder/new_test.go

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,8 @@ func (s *FSNoderSuite) TestUnnamedInnerFails(c *C) {
4040
c.Assert(err, Not(IsNil))
4141
}
4242

43-
func (s *FSNoderSuite) TestMalformedInnerName(c *C) {
44-
_, err := New("(ab<>)")
45-
c.Assert(err, Not(IsNil))
46-
}
47-
4843
func (s *FSNoderSuite) TestMalformedFile(c *C) {
49-
_, err := New("(a<12>)")
50-
c.Assert(err, Not(IsNil))
51-
_, err = New("(4<>)")
44+
_, err := New("(4<>)")
5245
c.Assert(err, Not(IsNil))
5346
_, err = New("(4<1>)")
5447
c.Assert(err, Not(IsNil))
@@ -66,13 +59,11 @@ func (s *FSNoderSuite) TestMalformedFile(c *C) {
6659
_, err = decodeFile([]byte("a<1?"))
6760
c.Assert(err, Not(IsNil))
6861

69-
_, err = decodeEmptyFile([]byte("a<1>"))
62+
_, err = decodeFile([]byte("a?>"))
7063
c.Assert(err, Not(IsNil))
71-
_, err = decodeEmptyFile([]byte("a?>"))
64+
_, err = decodeFile([]byte("1<>"))
7265
c.Assert(err, Not(IsNil))
73-
_, err = decodeEmptyFile([]byte("1<>"))
74-
c.Assert(err, Not(IsNil))
75-
_, err = decodeEmptyFile([]byte("a<?"))
66+
_, err = decodeFile([]byte("a<?"))
7667
c.Assert(err, Not(IsNil))
7768
}
7869

@@ -211,6 +202,32 @@ func (s *FSNoderSuite) TestDirWithEmtpyFileSameName(c *C) {
211202
check(c, input, expected)
212203
}
213204

205+
func (s *FSNoderSuite) TestDirWithFileLongContents(c *C) {
206+
input := "(A(a<12>))"
207+
208+
a1, err := newFile("a", "12")
209+
c.Assert(err, IsNil)
210+
A, err := newDir("A", []noder.Noder{a1})
211+
c.Assert(err, IsNil)
212+
expected, err := newDir("", []noder.Noder{A})
213+
c.Assert(err, IsNil)
214+
215+
check(c, input, expected)
216+
}
217+
218+
func (s *FSNoderSuite) TestDirWithFileLongName(c *C) {
219+
input := "(A(abc<12>))"
220+
221+
a1, err := newFile("abc", "12")
222+
c.Assert(err, IsNil)
223+
A, err := newDir("A", []noder.Noder{a1})
224+
c.Assert(err, IsNil)
225+
expected, err := newDir("", []noder.Noder{A})
226+
c.Assert(err, IsNil)
227+
228+
check(c, input, expected)
229+
}
230+
214231
func (s *FSNoderSuite) TestDirWithFile(c *C) {
215232
input := "(A(a<1>))"
216233

utils/merkletrie/noder/noder_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package noder
22

3-
import . "gopkg.in/check.v1"
3+
import (
4+
"testing"
5+
6+
. "gopkg.in/check.v1"
7+
)
8+
9+
func Test(t *testing.T) { TestingT(t) }
410

511
type NoderSuite struct{}
612

utils/merkletrie/noder/path.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package noder
22

3-
import "bytes"
3+
import (
4+
"bytes"
5+
"strings"
6+
)
47

58
// Path values represent a noder and its ancestors. The root goes first
69
// and the actual final noder the path is refering to will be the last.
@@ -57,3 +60,29 @@ func (p Path) Children() ([]Noder, error) {
5760
func (p Path) NumChildren() (int, error) {
5861
return p.Last().NumChildren()
5962
}
63+
64+
// Compare returns -1, 0 or 1 if the path p is smaller, equal or bigger
65+
// than other, in "directory order"; for example:
66+
//
67+
// "a" < "b"
68+
// "a/b/c/d/z" < "b"
69+
// "a/b/a" > "a/b"
70+
func (p Path) Compare(other Path) int {
71+
i := 0
72+
for {
73+
switch {
74+
case len(other) == len(p) && i == len(p):
75+
return 0
76+
case i == len(other):
77+
return 1
78+
case i == len(p):
79+
return -1
80+
default:
81+
cmp := strings.Compare(p[i].Name(), other[i].Name())
82+
if cmp != 0 {
83+
return cmp
84+
}
85+
}
86+
i++
87+
}
88+
}

0 commit comments

Comments
 (0)