aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--_examples/commit/main.go2
-rw-r--r--plumbing/object/commit_walker_bfs.go2
-rw-r--r--plumbing/object/tree.go2
-rw-r--r--plumbing/object/tree_test.go3
-rw-r--r--repository.go17
-rw-r--r--repository_test.go30
-rw-r--r--storage/transactional/storage.go43
-rw-r--r--storage/transactional/storage_test.go33
-rw-r--r--utils/diff/diff.go18
10 files changed, 138 insertions, 14 deletions
diff --git a/.travis.yml b/.travis.yml
index c68b5f4..3a65f3e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,8 @@
language: go
go:
- - "1.10"
- "1.11"
+ - "1.12"
go_import_path: gopkg.in/src-d/go-git.v4
diff --git a/_examples/commit/main.go b/_examples/commit/main.go
index ec296b9..f184b81 100644
--- a/_examples/commit/main.go
+++ b/_examples/commit/main.go
@@ -27,7 +27,7 @@ func main() {
// ... we need a file to commit so let's create a new file inside of the
// worktree of the project using the go standard library.
- Info("echo \"hellow world!\" > example-git-file")
+ Info("echo \"hello world!\" > example-git-file")
filename := filepath.Join(directory, "example-git-file")
err = ioutil.WriteFile(filename, []byte("hello world!"), 0644)
CheckIfError(err)
diff --git a/plumbing/object/commit_walker_bfs.go b/plumbing/object/commit_walker_bfs.go
index aef1cf2..dabfe75 100644
--- a/plumbing/object/commit_walker_bfs.go
+++ b/plumbing/object/commit_walker_bfs.go
@@ -67,7 +67,7 @@ func (w *bfsCommitIterator) Next() (*Commit, error) {
for _, h := range c.ParentHashes {
err := w.appendHash(c.s, h)
if err != nil {
- return nil, nil
+ return nil, err
}
}
diff --git a/plumbing/object/tree.go b/plumbing/object/tree.go
index 78d61a1..1f9ea26 100644
--- a/plumbing/object/tree.go
+++ b/plumbing/object/tree.go
@@ -135,7 +135,7 @@ func (t *Tree) FindEntry(path string) (*TreeEntry, error) {
pathCurrent := ""
// search for the longest path in the tree path cache
- for i := len(pathParts); i > 1; i-- {
+ for i := len(pathParts) - 1; i > 1; i-- {
path := filepath.Join(pathParts[:i]...)
tree, ok := t.t[path]
diff --git a/plumbing/object/tree_test.go b/plumbing/object/tree_test.go
index 889c63a..b408595 100644
--- a/plumbing/object/tree_test.go
+++ b/plumbing/object/tree_test.go
@@ -125,6 +125,9 @@ func (s *TreeSuite) TestFindEntryNotFound(c *C) {
e, err := s.Tree.FindEntry("not-found")
c.Assert(e, IsNil)
c.Assert(err, Equals, ErrEntryNotFound)
+ e, err = s.Tree.FindEntry("not-found/not-found/not-found")
+ c.Assert(e, IsNil)
+ c.Assert(err, Equals, ErrDirectoryNotFound)
}
// Overrides returned plumbing.EncodedObject for given hash.
diff --git a/repository.go b/repository.go
index de92d64..e5b12b0 100644
--- a/repository.go
+++ b/repository.go
@@ -49,6 +49,7 @@ var (
ErrRepositoryAlreadyExists = errors.New("repository already exists")
ErrRemoteNotFound = errors.New("remote not found")
ErrRemoteExists = errors.New("remote already exists")
+ ErrAnonymousRemoteName = errors.New("anonymous remote name must be 'anonymous'")
ErrWorktreeNotProvided = errors.New("worktree should be provided")
ErrIsBareRepository = errors.New("worktree not available in a bare repository")
ErrUnableToResolveCommit = errors.New("unable to resolve commit")
@@ -492,6 +493,22 @@ func (r *Repository) CreateRemote(c *config.RemoteConfig) (*Remote, error) {
return remote, r.Storer.SetConfig(cfg)
}
+// CreateRemoteAnonymous creates a new anonymous remote. c.Name must be "anonymous".
+// It's used like 'git fetch git@github.com:src-d/go-git.git master:master'.
+func (r *Repository) CreateRemoteAnonymous(c *config.RemoteConfig) (*Remote, error) {
+ if err := c.Validate(); err != nil {
+ return nil, err
+ }
+
+ if c.Name != "anonymous" {
+ return nil, ErrAnonymousRemoteName
+ }
+
+ remote := newRemote(r.Storer, c)
+
+ return remote, nil
+}
+
// DeleteRemote delete a remote from the repository and delete the config
func (r *Repository) DeleteRemote(name string) error {
cfg, err := r.Storer.Config()
diff --git a/repository_test.go b/repository_test.go
index 1549737..ccbe29b 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -231,6 +231,36 @@ func (s *RepositorySuite) TestCreateRemoteInvalid(c *C) {
c.Assert(remote, IsNil)
}
+func (s *RepositorySuite) TestCreateRemoteAnonymous(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+ remote, err := r.CreateRemoteAnonymous(&config.RemoteConfig{
+ Name: "anonymous",
+ URLs: []string{"http://foo/foo.git"},
+ })
+
+ c.Assert(err, IsNil)
+ c.Assert(remote.Config().Name, Equals, "anonymous")
+}
+
+func (s *RepositorySuite) TestCreateRemoteAnonymousInvalidName(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+ remote, err := r.CreateRemoteAnonymous(&config.RemoteConfig{
+ Name: "not_anonymous",
+ URLs: []string{"http://foo/foo.git"},
+ })
+
+ c.Assert(err, Equals, ErrAnonymousRemoteName)
+ c.Assert(remote, IsNil)
+}
+
+func (s *RepositorySuite) TestCreateRemoteAnonymousInvalid(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+ remote, err := r.CreateRemoteAnonymous(&config.RemoteConfig{})
+
+ c.Assert(err, Equals, config.ErrRemoteConfigEmptyName)
+ c.Assert(remote, IsNil)
+}
+
func (s *RepositorySuite) TestDeleteRemote(c *C) {
r, _ := Init(memory.NewStorage(), nil)
_, err := r.CreateRemote(&config.RemoteConfig{
diff --git a/storage/transactional/storage.go b/storage/transactional/storage.go
index fbb3d35..b81b104 100644
--- a/storage/transactional/storage.go
+++ b/storage/transactional/storage.go
@@ -1,6 +1,9 @@
package transactional
import (
+ "io"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/storage"
)
@@ -10,7 +13,13 @@ import (
//
// The API and functionality of this package are considered EXPERIMENTAL and is
// not considered stable nor production ready.
-type Storage struct {
+type Storage interface {
+ storage.Storer
+ Commit() error
+}
+
+// basic implements the Storage interface.
+type basic struct {
s, temporal storage.Storer
*ObjectStorage
@@ -20,11 +29,18 @@ type Storage struct {
*ConfigStorage
}
+// packageWriter implements storer.PackfileWriter interface over
+// a Storage with a temporal storer that supports it.
+type packageWriter struct {
+ *basic
+ pw storer.PackfileWriter
+}
+
// NewStorage returns a new Storage based on two repositories, base is the base
-// repository where the read operations are read and temportal is were all
+// repository where the read operations are read and temporal is were all
// the write operations are stored.
-func NewStorage(base, temporal storage.Storer) *Storage {
- return &Storage{
+func NewStorage(base, temporal storage.Storer) Storage {
+ st := &basic{
s: base,
temporal: temporal,
@@ -34,10 +50,20 @@ func NewStorage(base, temporal storage.Storer) *Storage {
ShallowStorage: NewShallowStorage(base, temporal),
ConfigStorage: NewConfigStorage(base, temporal),
}
+
+ pw, ok := temporal.(storer.PackfileWriter)
+ if ok {
+ return &packageWriter{
+ basic: st,
+ pw: pw,
+ }
+ }
+
+ return st
}
// Module it honors the storage.ModuleStorer interface.
-func (s *Storage) Module(name string) (storage.Storer, error) {
+func (s *basic) Module(name string) (storage.Storer, error) {
base, err := s.s.Module(name)
if err != nil {
return nil, err
@@ -52,7 +78,7 @@ func (s *Storage) Module(name string) (storage.Storer, error) {
}
// Commit it copies the content of the temporal storage into the base storage.
-func (s *Storage) Commit() error {
+func (s *basic) Commit() error {
for _, c := range []interface{ Commit() error }{
s.ObjectStorage,
s.ReferenceStorage,
@@ -67,3 +93,8 @@ func (s *Storage) Commit() error {
return nil
}
+
+// PackfileWriter honors storage.PackfileWriter.
+func (s *packageWriter) PackfileWriter() (io.WriteCloser, error) {
+ return s.pw.PackfileWriter()
+}
diff --git a/storage/transactional/storage_test.go b/storage/transactional/storage_test.go
index 6aaea0d..63ebfb1 100644
--- a/storage/transactional/storage_test.go
+++ b/storage/transactional/storage_test.go
@@ -4,7 +4,12 @@ import (
"testing"
. "gopkg.in/check.v1"
+ "gopkg.in/src-d/go-billy.v4/memfs"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/cache"
+ "gopkg.in/src-d/go-git.v4/plumbing/storer"
+ "gopkg.in/src-d/go-git.v4/storage"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
"gopkg.in/src-d/go-git.v4/storage/memory"
"gopkg.in/src-d/go-git.v4/storage/test"
)
@@ -13,13 +18,25 @@ func Test(t *testing.T) { TestingT(t) }
type StorageSuite struct {
test.BaseStorageSuite
+ temporal func() storage.Storer
}
-var _ = Suite(&StorageSuite{})
+var _ = Suite(&StorageSuite{
+ temporal: func() storage.Storer {
+ return memory.NewStorage()
+ },
+})
+
+var _ = Suite(&StorageSuite{
+ temporal: func() storage.Storer {
+ fs := memfs.New()
+ return filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
+ },
+})
func (s *StorageSuite) SetUpTest(c *C) {
base := memory.NewStorage()
- temporal := memory.NewStorage()
+ temporal := s.temporal()
s.BaseStorageSuite = test.NewBaseStorageSuite(NewStorage(base, temporal))
s.BaseStorageSuite.SetUpTest(c)
@@ -27,7 +44,7 @@ func (s *StorageSuite) SetUpTest(c *C) {
func (s *StorageSuite) TestCommit(c *C) {
base := memory.NewStorage()
- temporal := memory.NewStorage()
+ temporal := s.temporal()
st := NewStorage(base, temporal)
commit := base.NewEncodedObject()
@@ -50,3 +67,13 @@ func (s *StorageSuite) TestCommit(c *C) {
c.Assert(err, IsNil)
c.Assert(obj.Hash(), Equals, commit.Hash())
}
+
+func (s *StorageSuite) TestTransactionalPackfileWriter(c *C) {
+ base := memory.NewStorage()
+ temporal := s.temporal()
+ st := NewStorage(base, temporal)
+
+ _, tmpOK := temporal.(storer.PackfileWriter)
+ _, ok := st.(storer.PackfileWriter)
+ c.Assert(ok, Equals, tmpOK)
+}
diff --git a/utils/diff/diff.go b/utils/diff/diff.go
index f49ae55..6142ed0 100644
--- a/utils/diff/diff.go
+++ b/utils/diff/diff.go
@@ -8,14 +8,30 @@ package diff
import (
"bytes"
+ "time"
"github.com/sergi/go-diff/diffmatchpatch"
)
// Do computes the (line oriented) modifications needed to turn the src
-// string into the dst string.
+// string into the dst string. The underlying algorithm is Meyers,
+// its complexity is O(N*d) where N is min(lines(src), lines(dst)) and d
+// is the size of the diff.
func Do(src, dst string) (diffs []diffmatchpatch.Diff) {
+ // the default timeout is time.Second which may be too small under heavy load
+ return DoWithTimeout(src, dst, time.Hour)
+}
+
+// DoWithTimeout computes the (line oriented) modifications needed to turn the src
+// string into the dst string. The `timeout` argument specifies the maximum
+// amount of time it is allowed to spend in this function. If the timeout
+// is exceeded, the parts of the strings which were not considered are turned into
+// a bulk delete+insert and the half-baked suboptimal result is returned at once.
+// The underlying algorithm is Meyers, its complexity is O(N*d) where N is
+// min(lines(src), lines(dst)) and d is the size of the diff.
+func DoWithTimeout (src, dst string, timeout time.Duration) (diffs []diffmatchpatch.Diff) {
dmp := diffmatchpatch.New()
+ dmp.DiffTimeout = timeout
wSrc, wDst, warray := dmp.DiffLinesToRunes(src, dst)
diffs = dmp.DiffMainRunes(wSrc, wDst, false)
diffs = dmp.DiffCharsToLines(diffs, warray)