From 6825ee17a23754ac56a1fb9d4c09f321070c29b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Cort=C3=A9s?= Date: Thu, 21 Jan 2016 03:00:41 +0100 Subject: [PATCH 1/2] Fix commit.File() gorutine leak Commit.File() was leaking a goroutine because it was looping over an iterator without closing its channel. Now commit.File() calls the new Tree.File() method that searches the file in the repository by trasversing the dir tree instead of using the tree.Files() iterator. This not only prevent the goroutine leak, but also speeds up file searching. --- commit.go | 11 +- .../fixtures/alcortesm-binary-relations.pack | Bin 0 -> 12565 bytes .../packfile/fixtures/git-fixture.ofs-delta | Bin .../packfile/fixtures/git-fixture.ref-delta | Bin tree.go | 80 +++++++++++ tree_test.go | 134 ++++++++++++++++++ 6 files changed, 215 insertions(+), 10 deletions(-) create mode 100644 formats/packfile/fixtures/alcortesm-binary-relations.pack mode change 100755 => 100644 formats/packfile/fixtures/git-fixture.ofs-delta mode change 100755 => 100644 formats/packfile/fixtures/git-fixture.ref-delta create mode 100644 tree_test.go diff --git a/commit.go b/commit.go index 1dd09ab6b..ea6419f1d 100644 --- a/commit.go +++ b/commit.go @@ -3,7 +3,6 @@ package git import ( "bufio" "bytes" - "errors" "fmt" "io" "sort" @@ -11,9 +10,6 @@ import ( "gopkg.in/src-d/go-git.v2/core" ) -// New errors defined by this package. -var ErrFileNotFound = errors.New("file not found") - type Hash core.Hash // Commit points to a single tree, marking it as what the project looked like @@ -59,12 +55,7 @@ func (c *Commit) NumParents() int { // nil error if the file exists. If the file does not exists, it returns // a nil file and the ErrFileNotFound error. func (c *Commit) File(path string) (file *File, err error) { - for file := range c.Tree().Files() { - if file.Name == path { - return file, nil - } - } - return nil, ErrFileNotFound + return c.Tree().File(path) } // Decode transform an core.Object into a Blob struct diff --git a/formats/packfile/fixtures/alcortesm-binary-relations.pack b/formats/packfile/fixtures/alcortesm-binary-relations.pack new file mode 100644 index 0000000000000000000000000000000000000000..136273797d5ec0990783690ff1ce948520feea6a GIT binary patch literal 12565 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 LhR2fysf7Op6w 1; pathParts = pathParts[1:] { + if tree, err = tree.dir(pathParts[0]); err != nil { + return nil, err + } + } + + entry, err := tree.entry(pathParts[0]) + if err != nil { + return nil, err + } + + return &entry.Hash, nil +} + +var errDirNotFound = errors.New("directory not found") + +func (t *Tree) dir(baseName string) (*Tree, error) { + entry, err := t.entry(baseName) + if err != nil { + return nil, errDirNotFound + } + + obj, ok := t.r.Storage.Get(entry.Hash) + if !ok { // git submodule + return nil, errDirNotFound + } + + if obj.Type() != core.TreeObject { + return nil, errDirNotFound // a file + } + + tree := &Tree{r: t.r} + tree.Decode(obj) + + return tree, nil +} + +var errEntryNotFound = errors.New("entry not found") + +func (t *Tree) entry(baseName string) (*TreeEntry, error) { + for _, entry := range t.Entries { + if entry.Name == baseName { + return &entry, nil + } + } + + return nil, errEntryNotFound +} + func (t *Tree) Files() chan *File { ch := make(chan *File, 1) diff --git a/tree_test.go b/tree_test.go new file mode 100644 index 000000000..049161c12 --- /dev/null +++ b/tree_test.go @@ -0,0 +1,134 @@ +package git + +import ( + "os" + + "gopkg.in/src-d/go-git.v2/core" + "gopkg.in/src-d/go-git.v2/formats/packfile" + + . "gopkg.in/check.v1" +) + +type SuiteTree struct { + repos map[string]*Repository +} + +var _ = Suite(&SuiteTree{}) + +// create the repositories of the fixtures +func (s *SuiteTree) SetUpSuite(c *C) { + fixtureRepos := [...]struct { + url string + packfile string + }{ + {"https://github.com/tyba/git-fixture.git", "formats/packfile/fixtures/git-fixture.ofs-delta"}, + {"https://github.com/cpcs499/Final_Pres_P.git", "formats/packfile/fixtures/Final_Pres_P.ofs-delta"}, + {"https://github.com/jamesob/desk.git", "formats/packfile/fixtures/jamesob-desk.pack"}, + {"https://github.com/spinnaker/spinnaker.git", "formats/packfile/fixtures/spinnaker-spinnaker.pack"}, + {"https://github.com/alcortesm/binary-relations.git", "formats/packfile/fixtures/alcortesm-binary-relations.pack"}, + } + s.repos = make(map[string]*Repository, 0) + for _, fixRepo := range fixtureRepos { + s.repos[fixRepo.url] = NewPlainRepository() + + d, err := os.Open(fixRepo.packfile) + c.Assert(err, IsNil) + + r := packfile.NewReader(d) + r.Format = packfile.OFSDeltaFormat // TODO: how to know the format of a pack file ahead of time? + + _, err = r.Read(s.repos[fixRepo.url].Storage) + c.Assert(err, IsNil) + + c.Assert(d.Close(), IsNil) + } +} + +func (s *SuiteTree) TestFile(c *C) { + for i, t := range []struct { + repo string // the repo name as in localRepos + commit string // the commit to search for the file + path string // the path of the file to find + blobHash string // expected hash of the returned file + found bool // expected found value + }{ + // use git ls-tree commit to get the hash of the blobs + {"https://github.com/tyba/git-fixture.git", "b029517f6300c2da0f4b651b8642506cd6aaf45d", "not-found", + "", false}, + {"https://github.com/tyba/git-fixture.git", "b029517f6300c2da0f4b651b8642506cd6aaf45d", ".gitignore", + "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88", true}, + {"https://github.com/tyba/git-fixture.git", "b029517f6300c2da0f4b651b8642506cd6aaf45d", "LICENSE", + "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f", true}, + + {"https://github.com/tyba/git-fixture.git", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "not-found", + "", false}, + {"https://github.com/tyba/git-fixture.git", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", ".gitignore", + "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88", true}, + {"https://github.com/tyba/git-fixture.git", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "binary.jpg", + "d5c0f4ab811897cadf03aec358ae60d21f91c50d", true}, + {"https://github.com/tyba/git-fixture.git", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "LICENSE", + "c192bd6a24ea1ab01d78686e417c8bdc7c3d197f", true}, + + {"https://github.com/tyba/git-fixture.git", "35e85108805c84807bc66a02d91535e1e24b38b9", "binary.jpg", + "d5c0f4ab811897cadf03aec358ae60d21f91c50d", true}, + {"https://github.com/tyba/git-fixture.git", "b029517f6300c2da0f4b651b8642506cd6aaf45d", "binary.jpg", + "", false}, + + {"https://github.com/tyba/git-fixture.git", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "CHANGELOG", + "d3ff53e0564a9f87d8e84b6e28e5060e517008aa", true}, + {"https://github.com/tyba/git-fixture.git", "1669dce138d9b841a518c64b10914d88f5e488ea", "CHANGELOG", + "d3ff53e0564a9f87d8e84b6e28e5060e517008aa", true}, + {"https://github.com/tyba/git-fixture.git", "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69", "CHANGELOG", + "d3ff53e0564a9f87d8e84b6e28e5060e517008aa", true}, + {"https://github.com/tyba/git-fixture.git", "35e85108805c84807bc66a02d91535e1e24b38b9", "CHANGELOG", + "d3ff53e0564a9f87d8e84b6e28e5060e517008aa", false}, + {"https://github.com/tyba/git-fixture.git", "b8e471f58bcbca63b07bda20e428190409c2db47", "CHANGELOG", + "d3ff53e0564a9f87d8e84b6e28e5060e517008aa", true}, + {"https://github.com/tyba/git-fixture.git", "b029517f6300c2da0f4b651b8642506cd6aaf45d", "CHANGELOG", + "d3ff53e0564a9f87d8e84b6e28e5060e517008aa", false}, + + // git submodule + {"https://github.com/cpcs499/Final_Pres_P.git", "70bade703ce556c2c7391a8065c45c943e8b6bc3", "Final", + "", false}, + {"https://github.com/cpcs499/Final_Pres_P.git", "70bade703ce556c2c7391a8065c45c943e8b6bc3", "Final/not-found", + "", false}, + + {"https://github.com/jamesob/desk.git", "d4edaf0e8101fcea437ebd982d899fe2cc0f9f7b", "LICENSE", + "49c45e6cc893d6f5ebd5c9343fe4492360f339bf", true}, + {"https://github.com/jamesob/desk.git", "d4edaf0e8101fcea437ebd982d899fe2cc0f9f7b", "examples", + "", false}, + {"https://github.com/jamesob/desk.git", "d4edaf0e8101fcea437ebd982d899fe2cc0f9f7b", "examples/desk.sh", + "d9c7751138824cd2d539c23d5afe3f9d29836854", true}, + {"https://github.com/jamesob/desk.git", "d4edaf0e8101fcea437ebd982d899fe2cc0f9f7b", "examples/not-found", + "", false}, + {"https://github.com/jamesob/desk.git", "d4edaf0e8101fcea437ebd982d899fe2cc0f9f7b", "test/bashrc", + "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", true}, + {"https://github.com/jamesob/desk.git", "d4edaf0e8101fcea437ebd982d899fe2cc0f9f7b", "test/not-found", + "", false}, + + {"https://github.com/spinnaker/spinnaker.git", "b32b2aecae2cfca4840dd480f8082da206a538da", "etc/apache2/sites-available/spinnaker.conf", + "1d452c616be4fb16d2cc6b8a7e7a2208a6e64d2d", true}, + + {"https://github.com/alcortesm/binary-relations.git", "c44b5176e99085c8fe36fa27b045590a7b9d34c9", "Makefile", + "2dd2ad8c14de6612ed15813679a6554bad99330b", true}, + {"https://github.com/alcortesm/binary-relations.git", "c44b5176e99085c8fe36fa27b045590a7b9d34c9", "src/binrels", + "", false}, + {"https://github.com/alcortesm/binary-relations.git", "c44b5176e99085c8fe36fa27b045590a7b9d34c9", "src/map-slice", + "", false}, + {"https://github.com/alcortesm/binary-relations.git", "c44b5176e99085c8fe36fa27b045590a7b9d34c9", "src/map-slice/map-slice.go", + "12431e98381dd5097e1a19fe53429c72ef1f328e", true}, + {"https://github.com/alcortesm/binary-relations.git", "c44b5176e99085c8fe36fa27b045590a7b9d34c9", "src/map-slice/map-slice.go/not-found", + "", false}, + } { + commit, err := s.repos[t.repo].Commit(core.NewHash(t.commit)) + c.Assert(err, IsNil, Commentf("subtest %d: %v (%s)", i, err, t.commit)) + + tree := commit.Tree() + file, err := tree.File(t.path) + found := err == nil + c.Assert(found, Equals, t.found, Commentf("subtest %d, path=%s, commit=%s", i, t.path, t.commit)) + if found { + c.Assert(file.Hash.String(), Equals, t.blobHash, Commentf("subtest %d, commit=%s, path=%s", i, t.commit, t.path)) + } + } +} From 7e3259c191a9de23d88b6077dcb1cd427e925432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Cort=C3=A9s?= Date: Thu, 21 Jan 2016 03:29:57 +0100 Subject: [PATCH 2/2] performance increase for repos with many files and directories --- objects_test.go | 6 +++--- tree.go | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/objects_test.go b/objects_test.go index 1ead65db3..bc83cb7a5 100644 --- a/objects_test.go +++ b/objects_test.go @@ -53,9 +53,9 @@ func (s *ObjectsSuite) TestParseTree(c *C) { c.Assert(err, IsNil) c.Assert(tree.Entries, HasLen, 8) - c.Assert(tree.Entries[0].Name, Equals, ".gitignore") - c.Assert(tree.Entries[0].Mode.String(), Equals, "-rw-r--r--") - c.Assert(tree.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88") + c.Assert(tree.Entries[".gitignore"].Name, Equals, ".gitignore") + c.Assert(tree.Entries[".gitignore"].Mode.String(), Equals, "-rw-r--r--") + c.Assert(tree.Entries[".gitignore"].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88") count := 0 ch := tree.Files() diff --git a/tree.go b/tree.go index 232bfc257..053f5c111 100644 --- a/tree.go +++ b/tree.go @@ -15,7 +15,7 @@ import ( // Tree is basically like a directory - it references a bunch of other trees // and/or blobs (i.e. files and sub-directories) type Tree struct { - Entries []TreeEntry + Entries map[string]TreeEntry Hash core.Hash r *Repository @@ -97,13 +97,12 @@ func (t *Tree) dir(baseName string) (*Tree, error) { var errEntryNotFound = errors.New("entry not found") func (t *Tree) entry(baseName string) (*TreeEntry, error) { - for _, entry := range t.Entries { - if entry.Name == baseName { - return &entry, nil - } + entry, ok := t.Entries[baseName] + if !ok { + return nil, errEntryNotFound } - return nil, errEntryNotFound + return &entry, nil } func (t *Tree) Files() chan *File { @@ -145,6 +144,8 @@ func (t *Tree) Decode(o core.Object) error { return nil } + t.Entries = make(map[string]TreeEntry) + r := bufio.NewReader(o.Reader()) for { mode, err := r.ReadString(' ') @@ -172,11 +173,12 @@ func (t *Tree) Decode(o core.Object) error { return err } - t.Entries = append(t.Entries, TreeEntry{ + baseName := name[:len(name)-1] + t.Entries[baseName] = TreeEntry{ Hash: hash, Mode: os.FileMode(fm), - Name: name[:len(name)-1], - }) + Name: baseName, + } } return nil