diff options
-rw-r--r-- | plumbing/format/config/encoder.go | 8 | ||||
-rw-r--r-- | plumbing/format/packfile/delta_test.go | 24 | ||||
-rw-r--r-- | plumbing/format/packfile/patch_delta.go | 77 | ||||
-rw-r--r-- | plumbing/object/commit_walker.go | 48 | ||||
-rw-r--r-- | plumbing/object/commit_walker_test.go | 57 | ||||
-rw-r--r-- | plumbing/revlist/revlist.go | 39 | ||||
-rw-r--r-- | plumbing/transport/git/receive_pack_test.go | 4 | ||||
-rw-r--r-- | plumbing/transport/server/loader.go | 2 | ||||
-rw-r--r-- | remote.go | 12 | ||||
-rw-r--r-- | repository.go | 6 | ||||
-rw-r--r-- | repository_test.go | 45 | ||||
-rw-r--r-- | storage/filesystem/internal/dotgit/dotgit.go | 16 | ||||
-rw-r--r-- | storage/filesystem/internal/dotgit/dotgit_test.go | 1 | ||||
-rw-r--r-- | utils/merkletrie/filesystem/node.go | 6 | ||||
-rw-r--r-- | utils/merkletrie/filesystem/node_test.go | 10 | ||||
-rw-r--r-- | utils/merkletrie/index/node.go | 18 | ||||
-rw-r--r-- | utils/merkletrie/index/node_test.go | 29 | ||||
-rw-r--r-- | worktree.go | 5 | ||||
-rw-r--r-- | worktree_commit.go | 26 | ||||
-rw-r--r-- | worktree_linux.go | 1 | ||||
-rw-r--r-- | worktree_windows.go | 20 |
21 files changed, 346 insertions, 108 deletions
diff --git a/plumbing/format/config/encoder.go b/plumbing/format/config/encoder.go index 88bdf65..6d17a5a 100644 --- a/plumbing/format/config/encoder.go +++ b/plumbing/format/config/encoder.go @@ -3,6 +3,7 @@ package config import ( "fmt" "io" + "strings" ) // An Encoder writes config files to an output stream. @@ -61,7 +62,12 @@ func (e *Encoder) encodeSubsection(sectionName string, s *Subsection) error { func (e *Encoder) encodeOptions(opts Options) error { for _, o := range opts { - if err := e.printf("\t%s = %s\n", o.Key, o.Value); err != nil { + pattern := "\t%s = %s\n" + if strings.Index(o.Value, "\\") != -1 { + pattern = "\t%s = %q\n" + } + + if err := e.printf(pattern, o.Key, o.Value); err != nil { return err } } diff --git a/plumbing/format/packfile/delta_test.go b/plumbing/format/packfile/delta_test.go index 9ee3499..42b777a 100644 --- a/plumbing/format/packfile/delta_test.go +++ b/plumbing/format/packfile/delta_test.go @@ -1,7 +1,6 @@ package packfile import ( - "fmt" "math/rand" . "gopkg.in/check.v1" @@ -86,9 +85,28 @@ func (s *DeltaSuite) TestAddDelta(c *C) { baseBuf := genBytes(t.base) targetBuf := genBytes(t.target) delta := DiffDelta(baseBuf, targetBuf) - result := PatchDelta(baseBuf, delta) + result, err := PatchDelta(baseBuf, delta) - c.Log(fmt.Printf("Executing test case: %s\n", t.description)) + c.Log("Executing test case:", t.description) + c.Assert(err, IsNil) c.Assert(result, DeepEquals, targetBuf) } } + +func (s *DeltaSuite) TestIncompleteDelta(c *C) { + for _, t := range s.testCases { + c.Log("Incomplete delta on:", t.description) + baseBuf := genBytes(t.base) + targetBuf := genBytes(t.target) + delta := DiffDelta(baseBuf, targetBuf) + delta = delta[:len(delta)-2] + result, err := PatchDelta(baseBuf, delta) + c.Assert(err, NotNil) + c.Assert(result, IsNil) + } + + // check nil input too + result, err := PatchDelta(nil, nil) + c.Assert(err, NotNil) + c.Assert(result, IsNil) +} diff --git a/plumbing/format/packfile/patch_delta.go b/plumbing/format/packfile/patch_delta.go index 840f840..976cabc 100644 --- a/plumbing/format/packfile/patch_delta.go +++ b/plumbing/format/packfile/patch_delta.go @@ -1,6 +1,7 @@ package packfile import ( + "errors" "io/ioutil" "gopkg.in/src-d/go-git.v4/plumbing" @@ -30,7 +31,11 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) error { return err } - dst := PatchDelta(src, delta) + dst, err := PatchDelta(src, delta) + if err != nil { + return err + } + target.SetSize(int64(len(dst))) if _, err := w.Write(dst); err != nil { @@ -40,15 +45,22 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) error { return nil } +var ( + ErrInvalidDelta = errors.New("invalid delta") + ErrDeltaCmd = errors.New("wrong delta command") +) + // PatchDelta returns the result of applying the modification deltas in delta to src. -func PatchDelta(src, delta []byte) []byte { +// An error will be returned if delta is corrupted (ErrDeltaLen) or an action command +// is not copy from source or copy from delta (ErrDeltaCmd). +func PatchDelta(src, delta []byte) ([]byte, error) { if len(delta) < deltaSizeMin { - return nil + return nil, ErrInvalidDelta } srcSz, delta := decodeLEB128(delta) if srcSz != uint(len(src)) { - return nil + return nil, ErrInvalidDelta } targetSz, delta := decodeLEB128(delta) @@ -57,12 +69,25 @@ func PatchDelta(src, delta []byte) []byte { var dest []byte var cmd byte for { + if len(delta) == 0 { + return nil, ErrInvalidDelta + } + cmd = delta[0] delta = delta[1:] if isCopyFromSrc(cmd) { var offset, sz uint - offset, delta = decodeOffset(cmd, delta) - sz, delta = decodeSize(cmd, delta) + var err error + offset, delta, err = decodeOffset(cmd, delta) + if err != nil { + return nil, err + } + + sz, delta, err = decodeSize(cmd, delta) + if err != nil { + return nil, err + } + if invalidSize(sz, targetSz) || invalidOffsetSize(offset, sz, srcSz) { break @@ -72,13 +97,18 @@ func PatchDelta(src, delta []byte) []byte { } else if isCopyFromDelta(cmd) { sz := uint(cmd) // cmd is the size itself if invalidSize(sz, targetSz) { - break + return nil, ErrInvalidDelta + } + + if uint(len(delta)) < sz { + return nil, ErrInvalidDelta } + dest = append(dest, delta[0:sz]...) remainingTargetSz -= sz delta = delta[sz:] } else { - return nil + return nil, ErrDeltaCmd } if remainingTargetSz <= 0 { @@ -86,7 +116,7 @@ func PatchDelta(src, delta []byte) []byte { } } - return dest + return dest, nil } // Decodes a number encoded as an unsigned LEB128 at the start of some @@ -124,39 +154,60 @@ func isCopyFromDelta(cmd byte) bool { return (cmd&0x80) == 0 && cmd != 0 } -func decodeOffset(cmd byte, delta []byte) (uint, []byte) { +func decodeOffset(cmd byte, delta []byte) (uint, []byte, error) { var offset uint if (cmd & 0x01) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } offset = uint(delta[0]) delta = delta[1:] } if (cmd & 0x02) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } offset |= uint(delta[0]) << 8 delta = delta[1:] } if (cmd & 0x04) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } offset |= uint(delta[0]) << 16 delta = delta[1:] } if (cmd & 0x08) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } offset |= uint(delta[0]) << 24 delta = delta[1:] } - return offset, delta + return offset, delta, nil } -func decodeSize(cmd byte, delta []byte) (uint, []byte) { +func decodeSize(cmd byte, delta []byte) (uint, []byte, error) { var sz uint if (cmd & 0x10) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } sz = uint(delta[0]) delta = delta[1:] } if (cmd & 0x20) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } sz |= uint(delta[0]) << 8 delta = delta[1:] } if (cmd & 0x40) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } sz |= uint(delta[0]) << 16 delta = delta[1:] } @@ -164,7 +215,7 @@ func decodeSize(cmd byte, delta []byte) (uint, []byte) { sz = 0x10000 } - return sz, delta + return sz, delta, nil } func invalidSize(sz, targetSz uint) bool { diff --git a/plumbing/object/commit_walker.go b/plumbing/object/commit_walker.go index 8d2c6e8..797c17a 100644 --- a/plumbing/object/commit_walker.go +++ b/plumbing/object/commit_walker.go @@ -18,10 +18,16 @@ type commitPreIterator struct { // The given callback will be called for each visited commit. Each commit will // be visited only once. If the callback returns an error, walking will stop // and will return the error. Other errors might be returned if the history -// cannot be traversed (e.g. missing objects). -func NewCommitPreorderIter(c *Commit) CommitIter { +// cannot be traversed (e.g. missing objects). Ignore allows to skip some +// commits from being iterated. +func NewCommitPreorderIter(c *Commit, ignore []plumbing.Hash) CommitIter { + seen := make(map[plumbing.Hash]bool) + for _, h := range ignore { + seen[h] = true + } + return &commitPreIterator{ - seen: make(map[plumbing.Hash]bool), + seen: seen, stack: make([]CommitIter, 0), start: c, } @@ -51,20 +57,33 @@ func (w *commitPreIterator) Next() (*Commit, error) { } } - // check and update seen if w.seen[c.Hash] { continue } w.seen[c.Hash] = true + if c.NumParents() > 0 { - w.stack = append(w.stack, c.Parents()) + w.stack = append(w.stack, filteredParentIter(c, w.seen)) } return c, nil } } +func filteredParentIter(c *Commit, seen map[plumbing.Hash]bool) CommitIter { + var hashes []plumbing.Hash + for _, h := range c.ParentHashes { + if !seen[h] { + hashes = append(hashes, h) + } + } + + return NewCommitIter(c.s, + storer.NewEncodedObjectLookupIter(c.s, plumbing.CommitObject, hashes), + ) +} + func (w *commitPreIterator) ForEach(cb func(*Commit) error) error { for { c, err := w.Next() @@ -98,11 +117,16 @@ type commitPostIterator struct { // history like WalkCommitHistory but in post-order. This means that after // walking a merge commit, the merged commit will be walked before the base // it was merged on. This can be useful if you wish to see the history in -// chronological order. -func NewCommitPostorderIter(c *Commit) CommitIter { +// chronological order. Ignore allows to skip some commits from being iterated. +func NewCommitPostorderIter(c *Commit, ignore []plumbing.Hash) CommitIter { + seen := make(map[plumbing.Hash]bool) + for _, h := range ignore { + seen[h] = true + } + return &commitPostIterator{ stack: []*Commit{c}, - seen: make(map[plumbing.Hash]bool), + seen: seen, } } @@ -114,17 +138,17 @@ func (w *commitPostIterator) Next() (*Commit, error) { c := w.stack[len(w.stack)-1] w.stack = w.stack[:len(w.stack)-1] + if w.seen[c.Hash] { continue } + w.seen[c.Hash] = true - err := c.Parents().ForEach(func(pcm *Commit) error { - w.stack = append(w.stack, pcm) + return c, c.Parents().ForEach(func(p *Commit) error { + w.stack = append(w.stack, p) return nil }) - - return c, err } } diff --git a/plumbing/object/commit_walker_test.go b/plumbing/object/commit_walker_test.go index 2a03057..48b504d 100644 --- a/plumbing/object/commit_walker_test.go +++ b/plumbing/object/commit_walker_test.go @@ -1,6 +1,10 @@ package object -import . "gopkg.in/check.v1" +import ( + "gopkg.in/src-d/go-git.v4/plumbing" + + . "gopkg.in/check.v1" +) type CommitWalkerSuite struct { BaseObjectsSuite @@ -12,8 +16,7 @@ func (s *CommitWalkerSuite) TestCommitPreIterator(c *C) { commit := s.commit(c, s.Fixture.Head) var commits []*Commit - wIter := NewCommitPreorderIter(commit) - wIter.ForEach(func(c *Commit) error { + NewCommitPreorderIter(commit, nil).ForEach(func(c *Commit) error { commits = append(commits, c) return nil }) @@ -35,12 +38,33 @@ func (s *CommitWalkerSuite) TestCommitPreIterator(c *C) { } } +func (s *CommitWalkerSuite) TestCommitPreIteratorWithIgnore(c *C) { + commit := s.commit(c, s.Fixture.Head) + + var commits []*Commit + NewCommitPreorderIter(commit, []plumbing.Hash{ + plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"), + }).ForEach(func(c *Commit) error { + commits = append(commits, c) + return nil + }) + + c.Assert(commits, HasLen, 2) + + expected := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "918c48b83bd081e863dbe1b80f8998f058cd8294", + } + for i, commit := range commits { + c.Assert(commit.Hash.String(), Equals, expected[i]) + } +} + func (s *CommitWalkerSuite) TestCommitPostIterator(c *C) { commit := s.commit(c, s.Fixture.Head) var commits []*Commit - wIter := NewCommitPostorderIter(commit) - wIter.ForEach(func(c *Commit) error { + NewCommitPostorderIter(commit, nil).ForEach(func(c *Commit) error { commits = append(commits, c) return nil }) @@ -57,6 +81,29 @@ func (s *CommitWalkerSuite) TestCommitPostIterator(c *C) { "b029517f6300c2da0f4b651b8642506cd6aaf45d", "35e85108805c84807bc66a02d91535e1e24b38b9", } + + for i, commit := range commits { + c.Assert(commit.Hash.String(), Equals, expected[i]) + } +} + +func (s *CommitWalkerSuite) TestCommitPostIteratorWithIgnore(c *C) { + commit := s.commit(c, s.Fixture.Head) + + var commits []*Commit + NewCommitPostorderIter(commit, []plumbing.Hash{ + plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"), + }).ForEach(func(c *Commit) error { + commits = append(commits, c) + return nil + }) + + c.Assert(commits, HasLen, 2) + + expected := []string{ + "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "918c48b83bd081e863dbe1b80f8998f058cd8294", + } for i, commit := range commits { c.Assert(commit.Hash.String(), Equals, expected[i]) } diff --git a/plumbing/revlist/revlist.go b/plumbing/revlist/revlist.go index 20bc99d..1bda2fa 100644 --- a/plumbing/revlist/revlist.go +++ b/plumbing/revlist/revlist.go @@ -16,11 +16,7 @@ import ( // the reachable objects from the given objects. Ignore param are object hashes // that we want to ignore on the result. All that objects must be accessible // from the object storer. -func Objects( - s storer.EncodedObjectStorer, - objects []plumbing.Hash, - ignore []plumbing.Hash) ([]plumbing.Hash, error) { - +func Objects(s storer.EncodedObjectStorer, objects, ignore []plumbing.Hash) ([]plumbing.Hash, error) { seen := hashListToSet(ignore) result := make(map[plumbing.Hash]bool) @@ -32,7 +28,7 @@ func Objects( } for _, h := range objects { - if err := processObject(s, h, seen, walkerFunc); err != nil { + if err := processObject(s, h, seen, ignore, walkerFunc); err != nil { return nil, err } } @@ -45,6 +41,7 @@ func processObject( s storer.EncodedObjectStorer, h plumbing.Hash, seen map[plumbing.Hash]bool, + ignore []plumbing.Hash, walkerFunc func(h plumbing.Hash), ) error { o, err := s.EncodedObject(plumbing.AnyObject, h) @@ -59,12 +56,12 @@ func processObject( switch do := do.(type) { case *object.Commit: - return reachableObjects(do, seen, walkerFunc) + return reachableObjects(do, seen, ignore, walkerFunc) case *object.Tree: return iterateCommitTrees(seen, do, walkerFunc) case *object.Tag: walkerFunc(do.Hash) - return processObject(s, do.Target, seen, walkerFunc) + return processObject(s, do.Target, seen, ignore, walkerFunc) case *object.Blob: walkerFunc(do.Hash) default: @@ -82,22 +79,24 @@ func processObject( func reachableObjects( commit *object.Commit, seen map[plumbing.Hash]bool, + ignore []plumbing.Hash, cb func(h plumbing.Hash)) error { - return object.NewCommitPreorderIter(commit). - ForEach(func(commit *object.Commit) error { - if seen[commit.Hash] { - return nil - } - cb(commit.Hash) + i := object.NewCommitPreorderIter(commit, ignore) + return i.ForEach(func(commit *object.Commit) error { + if seen[commit.Hash] { + return nil + } + + cb(commit.Hash) - tree, err := commit.Tree() - if err != nil { - return err - } + tree, err := commit.Tree() + if err != nil { + return err + } - return iterateCommitTrees(seen, tree, cb) - }) + return iterateCommitTrees(seen, tree, cb) + }) } // iterateCommitTrees iterate all reachable trees from the given commit diff --git a/plumbing/transport/git/receive_pack_test.go b/plumbing/transport/git/receive_pack_test.go index 326ef1b..f9afede 100644 --- a/plumbing/transport/git/receive_pack_test.go +++ b/plumbing/transport/git/receive_pack_test.go @@ -99,9 +99,11 @@ func (s *ReceivePackSuite) SetUpTest(c *C) { } func (s *ReceivePackSuite) TearDownTest(c *C) { - err := s.daemon.Process.Signal(os.Interrupt) + err := s.daemon.Process.Signal(os.Kill) c.Assert(err, IsNil) + _ = s.daemon.Wait() + err = os.RemoveAll(s.base) c.Assert(err, IsNil) } diff --git a/plumbing/transport/server/loader.go b/plumbing/transport/server/loader.go index d4eccd4..028ead4 100644 --- a/plumbing/transport/server/loader.go +++ b/plumbing/transport/server/loader.go @@ -10,7 +10,7 @@ import ( ) // DefaultLoader is a filesystem loader ignoring host and resolving paths to /. -var DefaultLoader = NewFilesystemLoader(osfs.New("/")) +var DefaultLoader = NewFilesystemLoader(osfs.New("")) // Loader loads repository's storer.Storer based on an optional host and a path. type Loader interface { @@ -104,6 +104,7 @@ func (r *Remote) Push(o *PushOptions) (err error) { req := packp.NewReferenceUpdateRequestFromCapabilities(ar.Capabilities) if err := r.addReferencesToUpdate(o.RefSpecs, remoteRefs, req); err != nil { + return err } @@ -121,6 +122,15 @@ func (r *Remote) Push(o *PushOptions) (err error) { return err } + stop, err := r.s.Shallow() + if err != nil { + return err + } + + // if we have shallow we should include this as part of the objects that + // we are aware. + haves = append(haves, stop...) + hashesToPush, err := revlist.Objects(r.s, objects, haves) if err != nil { return err @@ -486,7 +496,7 @@ func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, } found := false - iter := object.NewCommitPreorderIter(c) + iter := object.NewCommitPreorderIter(c, nil) return found, iter.ForEach(func(c *object.Commit) error { if c.Hash != old { return nil diff --git a/repository.go b/repository.go index 17f0dff..ec9f254 100644 --- a/repository.go +++ b/repository.go @@ -689,7 +689,7 @@ func (r *Repository) Log(o *LogOptions) (object.CommitIter, error) { return nil, err } - return object.NewCommitPreorderIter(commit), nil + return object.NewCommitPreorderIter(commit, nil), nil } // Tags returns all the References from Tags. This method returns all the tag @@ -918,7 +918,7 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err commit = c } case revision.CaretReg: - history := object.NewCommitPreorderIter(commit) + history := object.NewCommitPreorderIter(commit, nil) re := item.(revision.CaretReg).Regexp negate := item.(revision.CaretReg).Negate @@ -948,7 +948,7 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err commit = c case revision.AtDate: - history := object.NewCommitPreorderIter(commit) + history := object.NewCommitPreorderIter(commit, nil) date := item.(revision.AtDate).Date diff --git a/repository_test.go b/repository_test.go index f622007..f31cd1d 100644 --- a/repository_test.go +++ b/repository_test.go @@ -20,6 +20,7 @@ import ( . "gopkg.in/check.v1" "gopkg.in/src-d/go-billy.v3/memfs" "gopkg.in/src-d/go-billy.v3/osfs" + "gopkg.in/src-d/go-billy.v3/util" ) type RepositorySuite struct { @@ -767,6 +768,50 @@ func (s *RepositorySuite) TestPushToEmptyRepository(c *C) { c.Assert(err, IsNil) } +func (s *RepositorySuite) TestPushDepth(c *C) { + dir, err := ioutil.TempDir("", "push-depth") + defer os.RemoveAll(dir) + + origin, err := PlainClone(c.MkDir(), true, &CloneOptions{ + URL: fixtures.Basic().One().DotGit().Root(), + }) + + c.Assert(err, IsNil) + fs := origin.Storer.(*filesystem.Storage).Filesystem() + + r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{ + URL: fs.Root(), + Depth: 1, + }) + c.Assert(err, IsNil) + + err = util.WriteFile(r.wt, "foo", nil, 0755) + c.Assert(err, IsNil) + + w, err := r.Worktree() + c.Assert(err, IsNil) + + _, err = w.Add("foo") + c.Assert(err, IsNil) + + hash, err := w.Commit("foo", &CommitOptions{ + Author: defaultSignature(), + Committer: defaultSignature(), + }) + c.Assert(err, IsNil) + + err = r.Push(&PushOptions{}) + c.Assert(err, IsNil) + + remote, err := origin.Head() + c.Assert(err, IsNil) + c.Assert(remote.Hash(), Equals, hash) + + local, err := r.Head() + c.Assert(err, IsNil) + c.Assert(local.Hash(), Equals, remote.Hash()) +} + func (s *RepositorySuite) TestPushNonExistentRemote(c *C) { srcFs := fixtures.Basic().One().DotGit() sto, err := filesystem.NewStorage(srcFs) diff --git a/storage/filesystem/internal/dotgit/dotgit.go b/storage/filesystem/internal/dotgit/dotgit.go index 9bbf4e8..e2ff51b 100644 --- a/storage/filesystem/internal/dotgit/dotgit.go +++ b/storage/filesystem/internal/dotgit/dotgit.go @@ -348,7 +348,6 @@ func (d *DotGit) rewritePackedRefsWithoutRef(name plumbing.ReferenceName) (err e return err } - defer ioutil.CheckClose(f, &err) // Creating the temp file in the same directory as the target file // improves our chances for rename operation to be atomic. @@ -357,10 +356,6 @@ func (d *DotGit) rewritePackedRefsWithoutRef(name plumbing.ReferenceName) (err e return err } - tmpPath := tmp.Name() - defer ioutil.CheckClose(tmp, &err) - defer d.fs.Remove(tmpPath) - s := bufio.NewScanner(f) found := false for s.Scan() { @@ -388,7 +383,16 @@ func (d *DotGit) rewritePackedRefsWithoutRef(name plumbing.ReferenceName) (err e return nil } - return d.fs.Rename(tmpPath, packedRefsPath) + if err := f.Close(); err != nil { + ioutil.CheckClose(tmp, &err) + return err + } + + if err := tmp.Close(); err != nil { + return err + } + + return d.fs.Rename(tmp.Name(), packedRefsPath) } // process lines from a packed-refs file diff --git a/storage/filesystem/internal/dotgit/dotgit_test.go b/storage/filesystem/internal/dotgit/dotgit_test.go index d4cda0e..d935ec5 100644 --- a/storage/filesystem/internal/dotgit/dotgit_test.go +++ b/storage/filesystem/internal/dotgit/dotgit_test.go @@ -373,6 +373,7 @@ func (s *SuiteDotGit) TestObjectPackIdx(c *C) { idx, err := dir.ObjectPackIdx(f.PackfileHash) c.Assert(err, IsNil) c.Assert(filepath.Ext(idx.Name()), Equals, ".idx") + c.Assert(idx.Close(), IsNil) } func (s *SuiteDotGit) TestObjectPackNotFound(c *C) { diff --git a/utils/merkletrie/filesystem/node.go b/utils/merkletrie/filesystem/node.go index a8f3b86..f763e08 100644 --- a/utils/merkletrie/filesystem/node.go +++ b/utils/merkletrie/filesystem/node.go @@ -3,7 +3,7 @@ package filesystem import ( "io" "os" - "path/filepath" + "path" "gopkg.in/src-d/go-billy.v3" "gopkg.in/src-d/go-git.v4/plumbing" @@ -53,7 +53,7 @@ func (n *node) Hash() []byte { } func (n *node) Name() string { - return filepath.Base(n.path) + return path.Base(n.path) } func (n *node) IsDir() bool { @@ -107,7 +107,7 @@ func (n *node) calculateChildren() error { } func (n *node) newChildNode(file os.FileInfo) (*node, error) { - path := filepath.Join(n.path, file.Name()) + path := path.Join(n.path, file.Name()) hash, err := n.calculateHash(path, file) if err != nil { diff --git a/utils/merkletrie/filesystem/node_test.go b/utils/merkletrie/filesystem/node_test.go index bf1178a..42dd82e 100644 --- a/utils/merkletrie/filesystem/node_test.go +++ b/utils/merkletrie/filesystem/node_test.go @@ -4,6 +4,7 @@ import ( "bytes" "io" "os" + "path" "testing" . "gopkg.in/check.v1" @@ -133,18 +134,19 @@ func (s *NoderSuite) TestDiffChangeModeNotRelevant(c *C) { } func (s *NoderSuite) TestDiffDirectory(c *C) { + dir := path.Join("qux", "bar") fsA := memfs.New() - fsA.MkdirAll("qux/bar", 0644) + fsA.MkdirAll(dir, 0644) fsB := memfs.New() - fsB.MkdirAll("qux/bar", 0644) + fsB.MkdirAll(dir, 0644) ch, err := merkletrie.DiffTree( NewRootNode(fsA, map[string]plumbing.Hash{ - "qux/bar": plumbing.NewHash("aa102815663d23f8b75a47e7a01965dcdc96468c"), + dir: plumbing.NewHash("aa102815663d23f8b75a47e7a01965dcdc96468c"), }), NewRootNode(fsB, map[string]plumbing.Hash{ - "qux/bar": plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c"), + dir: plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c"), }), IsEquals, ) diff --git a/utils/merkletrie/index/node.go b/utils/merkletrie/index/node.go index 859c097..9622622 100644 --- a/utils/merkletrie/index/node.go +++ b/utils/merkletrie/index/node.go @@ -1,7 +1,7 @@ package index import ( - "path/filepath" + "path" "strings" "gopkg.in/src-d/go-git.v4/plumbing/format/index" @@ -28,19 +28,19 @@ func NewRootNode(idx *index.Index) noder.Noder { m := map[string]*node{rootNode: {isDir: true}} for _, e := range idx.Entries { - parts := strings.Split(e.Name, string(filepath.Separator)) + parts := strings.Split(e.Name, string("/")) - var path string + var fullpath string for _, part := range parts { - parent := path - path = filepath.Join(path, part) + parent := fullpath + fullpath = path.Join(fullpath, part) - if _, ok := m[path]; ok { + if _, ok := m[fullpath]; ok { continue } - n := &node{path: path} - if path == e.Name { + n := &node{path: fullpath} + if fullpath == e.Name { n.entry = e } else { n.isDir = true @@ -74,7 +74,7 @@ func (n *node) Hash() []byte { } func (n *node) Name() string { - return filepath.Base(n.path) + return path.Base(n.path) } func (n *node) IsDir() bool { diff --git a/utils/merkletrie/index/node_test.go b/utils/merkletrie/index/node_test.go index 00da8da..283ca74 100644 --- a/utils/merkletrie/index/node_test.go +++ b/utils/merkletrie/index/node_test.go @@ -2,6 +2,7 @@ package index import ( "bytes" + "path/filepath" "testing" . "gopkg.in/check.v1" @@ -43,15 +44,17 @@ func (s *NoderSuite) TestDiff(c *C) { func (s *NoderSuite) TestDiffChange(c *C) { indexA := &index.Index{ - Entries: []*index.Entry{ - {Name: "bar/baz/bar", Hash: plumbing.NewHash("8ab686eafeb1f44702738c8b0f24f2567c36da6d")}, - }, + Entries: []*index.Entry{{ + Name: filepath.Join("bar", "baz", "bar"), + Hash: plumbing.NewHash("8ab686eafeb1f44702738c8b0f24f2567c36da6d"), + }}, } indexB := &index.Index{ - Entries: []*index.Entry{ - {Name: "bar/baz/foo", Hash: plumbing.NewHash("8ab686eafeb1f44702738c8b0f24f2567c36da6d")}, - }, + Entries: []*index.Entry{{ + Name: filepath.Join("bar", "baz", "foo"), + Hash: plumbing.NewHash("8ab686eafeb1f44702738c8b0f24f2567c36da6d"), + }}, } ch, err := merkletrie.DiffTree(NewRootNode(indexA), NewRootNode(indexB), isEquals) @@ -61,15 +64,17 @@ func (s *NoderSuite) TestDiffChange(c *C) { func (s *NoderSuite) TestDiffDir(c *C) { indexA := &index.Index{ - Entries: []*index.Entry{ - {Name: "foo", Hash: plumbing.NewHash("8ab686eafeb1f44702738c8b0f24f2567c36da6d")}, - }, + Entries: []*index.Entry{{ + Name: "foo", + Hash: plumbing.NewHash("8ab686eafeb1f44702738c8b0f24f2567c36da6d"), + }}, } indexB := &index.Index{ - Entries: []*index.Entry{ - {Name: "foo/bar", Hash: plumbing.NewHash("8ab686eafeb1f44702738c8b0f24f2567c36da6d")}, - }, + Entries: []*index.Entry{{ + Name: filepath.Join("foo", "bar"), + Hash: plumbing.NewHash("8ab686eafeb1f44702738c8b0f24f2567c36da6d"), + }}, } ch, err := merkletrie.DiffTree(NewRootNode(indexA), NewRootNode(indexB), isEquals) diff --git a/worktree.go b/worktree.go index e0f5fdf..13b2497 100644 --- a/worktree.go +++ b/worktree.go @@ -496,6 +496,10 @@ func (w *Worktree) Submodules() (Submodules, error) { } c, err := w.r.Config() + if err != nil { + return nil, err + } + for _, s := range m.Submodules { l = append(l, w.newSubmodule(s, c.Submodules[s.Name])) } @@ -527,6 +531,7 @@ func (w *Worktree) readGitmodulesFile() (*config.Modules, error) { return nil, err } + defer f.Close() input, err := stdioutil.ReadAll(f) if err != nil { return nil, err diff --git a/worktree_commit.go b/worktree_commit.go index a342240..02a5d03 100644 --- a/worktree_commit.go +++ b/worktree_commit.go @@ -1,7 +1,7 @@ package git import ( - "path/filepath" + "path" "strings" "gopkg.in/src-d/go-git.v4/plumbing" @@ -128,36 +128,36 @@ func (h *buildTreeHelper) BuildTree(idx *index.Index) (plumbing.Hash, error) { } func (h *buildTreeHelper) commitIndexEntry(e *index.Entry) error { - parts := strings.Split(e.Name, string(filepath.Separator)) + parts := strings.Split(e.Name, "/") - var path string + var fullpath string for _, part := range parts { - parent := path - path = filepath.Join(path, part) + parent := fullpath + fullpath = path.Join(fullpath, part) - h.doBuildTree(e, parent, path) + h.doBuildTree(e, parent, fullpath) } return nil } -func (h *buildTreeHelper) doBuildTree(e *index.Entry, parent, path string) { - if _, ok := h.trees[path]; ok { +func (h *buildTreeHelper) doBuildTree(e *index.Entry, parent, fullpath string) { + if _, ok := h.trees[fullpath]; ok { return } - if _, ok := h.entries[path]; ok { + if _, ok := h.entries[fullpath]; ok { return } - te := object.TreeEntry{Name: filepath.Base(path)} + te := object.TreeEntry{Name: path.Base(fullpath)} - if path == e.Name { + if fullpath == e.Name { te.Mode = e.Mode te.Hash = e.Hash } else { te.Mode = filemode.Dir - h.trees[path] = &object.Tree{} + h.trees[fullpath] = &object.Tree{} } h.trees[parent].Entries = append(h.trees[parent].Entries, te) @@ -169,7 +169,7 @@ func (h *buildTreeHelper) copyTreeToStorageRecursive(parent string, t *object.Tr continue } - path := filepath.Join(parent, e.Name) + path := path.Join(parent, e.Name) var err error e.Hash, err = h.copyTreeToStorageRecursive(path, h.trees[path]) diff --git a/worktree_linux.go b/worktree_linux.go index 7209d7d..a33cd2f 100644 --- a/worktree_linux.go +++ b/worktree_linux.go @@ -12,7 +12,6 @@ import ( func init() { fillSystemInfo = func(e *index.Entry, sys interface{}) { if os, ok := sys.(*syscall.Stat_t); ok { - e.CreatedAt = time.Unix(int64(os.Ctim.Sec), int64(os.Ctim.Nsec)) e.Dev = uint32(os.Dev) e.Inode = uint32(os.Ino) diff --git a/worktree_windows.go b/worktree_windows.go new file mode 100644 index 0000000..d59448e --- /dev/null +++ b/worktree_windows.go @@ -0,0 +1,20 @@ +// +build windows + +package git + +import ( + "syscall" + "time" + + "gopkg.in/src-d/go-git.v4/plumbing/format/index" +) + +func init() { + fillSystemInfo = func(e *index.Entry, sys interface{}) { + if os, ok := sys.(*syscall.Win32FileAttributeData); ok { + seconds := os.CreationTime.Nanoseconds() / 1000000000 + nanoseconds := os.CreationTime.Nanoseconds() - seconds*1000000000 + e.CreatedAt = time.Unix(seconds, nanoseconds) + } + } +} |