From 27b67a493d3b0a4f15252b77b546115c00ad2226 Mon Sep 17 00:00:00 2001 From: Josh Betz Date: Mon, 31 Jul 2017 11:46:31 -0500 Subject: Normalize filenames before comparing. Some multibyte characters can have multiple representations. Before comparing strings, we need to normalize them. In this case we're normalizing to normalized form C, but it shouldn't matter as long as both strings are normalized to the same form. Fixes https://github.com/src-d/go-git/issues/495 --- utils/merkletrie/noder/path.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/utils/merkletrie/noder/path.go b/utils/merkletrie/noder/path.go index 85742db..d2e2932 100644 --- a/utils/merkletrie/noder/path.go +++ b/utils/merkletrie/noder/path.go @@ -3,6 +3,8 @@ package noder import ( "bytes" "strings" + + "golang.org/x/text/unicode/norm" ) // Path values represent a noder and its ancestors. The root goes first @@ -78,7 +80,11 @@ func (p Path) Compare(other Path) int { case i == len(p): return -1 default: - cmp := strings.Compare(p[i].Name(), other[i].Name()) + form := norm.Form(norm.NFC) + this := form.String(p[i].Name()) + that := form.String(other[i].Name()) + + cmp := strings.Compare(this, that) if cmp != 0 { return cmp } -- cgit From 5c1a2ec798eb9b78d66b16fbbcbdc3b928d8b496 Mon Sep 17 00:00:00 2001 From: Máximo Cuadros Date: Wed, 2 Aug 2017 17:28:02 +0200 Subject: worktree: normalized string comparison tests --- utils/merkletrie/noder/path_test.go | 12 ++++++++++- worktree_test.go | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/utils/merkletrie/noder/path_test.go b/utils/merkletrie/noder/path_test.go index 44e3c3c..be25444 100644 --- a/utils/merkletrie/noder/path_test.go +++ b/utils/merkletrie/noder/path_test.go @@ -1,6 +1,9 @@ package noder -import . "gopkg.in/check.v1" +import ( + "golang.org/x/text/unicode/norm" + . "gopkg.in/check.v1" +) type PathSuite struct{} @@ -149,3 +152,10 @@ func (s *PathSuite) TestCompareMixedDepths(c *C) { c.Assert(p1.Compare(p2), Equals, 1) c.Assert(p2.Compare(p1), Equals, -1) } + +func (s *PathSuite) TestCompareNormalization(c *C) { + p1 := Path([]Noder{&noderMock{name: norm.Form(norm.NFKC).String("페")}}) + p2 := Path([]Noder{&noderMock{name: norm.Form(norm.NFKD).String("페")}}) + c.Assert(p1.Compare(p2), Equals, 0) + c.Assert(p2.Compare(p1), Equals, 0) +} diff --git a/worktree_test.go b/worktree_test.go index 10774a4..4be02f6 100644 --- a/worktree_test.go +++ b/worktree_test.go @@ -7,6 +7,8 @@ import ( "os" "path/filepath" + "golang.org/x/text/unicode/norm" + "gopkg.in/src-d/go-git.v4/config" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/filemode" @@ -304,6 +306,47 @@ func (s *WorktreeSuite) TestCheckoutSymlink(c *C) { c.Assert(err, IsNil) } +func (s *WorktreeSuite) TestFilenameNormalization(c *C) { + url := c.MkDir() + path := fixtures.Basic().ByTag("worktree").One().Worktree().Root() + + server, err := PlainClone(url, false, &CloneOptions{ + URL: path, + }) + + filename := "페" + + w, err := server.Worktree() + c.Assert(err, IsNil) + util.WriteFile(w.Filesystem, filename, []byte("foo"), 0755) + _, err = w.Add(filename) + c.Assert(err, IsNil) + _, err = w.Commit("foo", &CommitOptions{Author: defaultSignature()}) + c.Assert(err, IsNil) + + r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ + URL: url, + }) + + w, err = r.Worktree() + c.Assert(err, IsNil) + + status, err := w.Status() + c.Assert(err, IsNil) + c.Assert(status.IsClean(), Equals, true) + + err = w.Filesystem.Remove(filename) + c.Assert(err, IsNil) + + modFilename := norm.Form(norm.NFKD).String(filename) + util.WriteFile(w.Filesystem, modFilename, []byte("foo"), 0755) + _, err = w.Add(filename) + + status, err = w.Status() + c.Assert(err, IsNil) + c.Assert(status.IsClean(), Equals, true) +} + func (s *WorktreeSuite) TestCheckoutSubmodule(c *C) { url := "https://github.com/git-fixtures/submodule.git" r := s.NewRepositoryWithEmptyWorktree(fixtures.ByURL(url).One()) -- cgit