aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common.go1
-rw-r--r--common_test.go25
-rw-r--r--examples/clone/main.go5
-rw-r--r--examples/common_test.go6
-rw-r--r--examples/custom_http/main.go6
-rw-r--r--examples/log/main.go9
-rw-r--r--examples/open/main.go2
-rw-r--r--examples/progress/main.go5
-rw-r--r--examples/push/main.go12
-rw-r--r--examples/remotes/main.go7
-rw-r--r--examples/showcase/main.go9
-rw-r--r--examples/storage/aerospike/storage.go41
-rw-r--r--examples/storage/main.go21
-rw-r--r--fixtures/fixtures.go6
-rw-r--r--plumbing/format/index/decoder.go11
-rw-r--r--plumbing/format/index/decoder_test.go7
-rw-r--r--plumbing/object/tree.go20
-rw-r--r--plumbing/object/tree_test.go286
-rw-r--r--plumbing/reference.go3
-rw-r--r--remote_test.go2
-rw-r--r--repository.go216
-rw-r--r--repository_test.go337
-rw-r--r--storage/filesystem/index.go44
-rw-r--r--storage/filesystem/internal/dotgit/dotgit.go11
-rw-r--r--storage/filesystem/internal/dotgit/dotgit_test.go32
-rw-r--r--storage/filesystem/object.go20
-rw-r--r--storage/filesystem/storage.go2
-rw-r--r--storage/memory/storage.go19
-rw-r--r--storage/test/storage_suite.go23
-rw-r--r--worktree.go311
-rw-r--r--worktree_test.go166
31 files changed, 1320 insertions, 345 deletions
diff --git a/common.go b/common.go
index 72b7b4b..56390ac 100644
--- a/common.go
+++ b/common.go
@@ -15,6 +15,7 @@ type Storer interface {
storer.EncodedObjectStorer
storer.ReferenceStorer
storer.ShallowStorer
+ storer.IndexStorer
config.ConfigStorer
}
diff --git a/common_test.go b/common_test.go
index 032fdfa..9c20a9a 100644
--- a/common_test.go
+++ b/common_test.go
@@ -5,12 +5,17 @@ import (
"testing"
"gopkg.in/src-d/go-git.v4/fixtures"
+ "gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
"gopkg.in/src-d/go-git.v4/plumbing/transport/client"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
+ "gopkg.in/src-d/go-git.v4/storage/memory"
. "gopkg.in/check.v1"
+ memoryfs "srcd.works/go-billy.v1/memory"
+ "srcd.works/go-billy.v1/os"
)
func Test(t *testing.T) { TestingT(t) }
@@ -44,7 +49,13 @@ func (s *BaseSuite) buildBasicRepository(c *C) {
}
func (s *BaseSuite) NewRepository(f *fixtures.Fixture) *Repository {
- r, err := NewFilesystemRepository(f.DotGit().Base())
+ fs := os.New(f.DotGit().Base())
+ st, err := filesystem.NewStorage(fs)
+ if err != nil {
+ panic(err)
+ }
+
+ r, err := Open(st, fs)
if err != nil {
panic(err)
}
@@ -58,13 +69,12 @@ func (s *BaseSuite) NewRepositoryFromPackfile(f *fixtures.Fixture) *Repository {
return r
}
- r := NewMemoryRepository()
-
+ storer := memory.NewStorage()
p := f.Packfile()
defer p.Close()
n := packfile.NewScanner(p)
- d, err := packfile.NewDecoder(n, r.s)
+ d, err := packfile.NewDecoder(n, storer)
if err != nil {
panic(err)
}
@@ -74,6 +84,13 @@ func (s *BaseSuite) NewRepositoryFromPackfile(f *fixtures.Fixture) *Repository {
panic(err)
}
+ storer.SetReference(plumbing.NewHashReference(plumbing.HEAD, f.Head))
+
+ r, err := Open(storer, memoryfs.New())
+ if err != nil {
+ panic(err)
+ }
+
s.cache[h] = r
return r
}
diff --git a/examples/clone/main.go b/examples/clone/main.go
index 13257ca..fc50960 100644
--- a/examples/clone/main.go
+++ b/examples/clone/main.go
@@ -13,13 +13,10 @@ func main() {
url := os.Args[1]
directory := os.Args[2]
- r, err := git.NewFilesystemRepository(directory)
- CheckIfError(err)
-
// Clone the given repository to the given directory
Info("git clone %s %s", url, directory)
- err = r.Clone(&git.CloneOptions{
+ r, err := git.PlainClone(directory, false, &git.CloneOptions{
URL: url,
Depth: 1,
})
diff --git a/examples/common_test.go b/examples/common_test.go
index 9ec1f05..0baf2f1 100644
--- a/examples/common_test.go
+++ b/examples/common_test.go
@@ -16,12 +16,12 @@ var examplesTest = flag.Bool("examples", false, "run the examples tests")
var defaultURL = "https://github.com/mcuadros/basic.git"
var args = map[string][]string{
- "showcase": []string{defaultURL},
+ "showcase": []string{defaultURL, tempFolder()},
"custom_http": []string{defaultURL},
"clone": []string{defaultURL, tempFolder()},
"progress": []string{defaultURL, tempFolder()},
- "open": []string{filepath.Join(cloneRepository(defaultURL, tempFolder()), ".git")},
- "push": []string{setEmptyRemote(filepath.Join(cloneRepository(defaultURL, tempFolder()), ".git"))},
+ "open": []string{cloneRepository(defaultURL, tempFolder())},
+ "push": []string{setEmptyRemote(cloneRepository(defaultURL, tempFolder()))},
}
var ignored = map[string]bool{
diff --git a/examples/custom_http/main.go b/examples/custom_http/main.go
index 0853569..19f2ca3 100644
--- a/examples/custom_http/main.go
+++ b/examples/custom_http/main.go
@@ -11,6 +11,7 @@ import (
. "gopkg.in/src-d/go-git.v4/examples"
"gopkg.in/src-d/go-git.v4/plumbing/transport/client"
githttp "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
+ "gopkg.in/src-d/go-git.v4/storage/memory"
)
// Here is an example to configure http client according to our own needs.
@@ -37,13 +38,10 @@ func main() {
// Override http(s) default protocol to use our custom client
client.InstallProtocol("https", githttp.NewClient(customClient))
- // Create an in-memory repository
- r := git.NewMemoryRepository()
-
// Clone repository using the new client if the protocol is https://
Info("git clone %s", url)
- err := r.Clone(&git.CloneOptions{URL: url})
+ r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{URL: url})
CheckIfError(err)
// Retrieve the branch pointed by HEAD
diff --git a/examples/log/main.go b/examples/log/main.go
index 6ab0bb7..eb41010 100644
--- a/examples/log/main.go
+++ b/examples/log/main.go
@@ -5,17 +5,14 @@ import (
"gopkg.in/src-d/go-git.v4"
. "gopkg.in/src-d/go-git.v4/examples"
+ "gopkg.in/src-d/go-git.v4/storage/memory"
)
func main() {
- // Instances an in-memory git repository
- r := git.NewMemoryRepository()
-
// Clones the given repository, creating the remote, the local branches
- // and fetching the objects, exactly as:
+ // and fetching the objects, everything in memory:
Info("git clone https://github.com/src-d/go-siva")
-
- err := r.Clone(&git.CloneOptions{URL: "https://github.com/src-d/go-siva"})
+ r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{URL: "https://github.com/src-d/go-siva"})
CheckIfError(err)
// Gets the HEAD history from HEAD, just like does:
diff --git a/examples/open/main.go b/examples/open/main.go
index 90fd406..1a9ea64 100644
--- a/examples/open/main.go
+++ b/examples/open/main.go
@@ -13,7 +13,7 @@ func main() {
path := os.Args[1]
// We instance a new repository targeting the given path (the .git folder)
- r, err := git.NewFilesystemRepository(path)
+ r, err := git.PlainOpen(path)
CheckIfError(err)
// Length of the HEAD history
diff --git a/examples/progress/main.go b/examples/progress/main.go
index 0da9db8..46ff57a 100644
--- a/examples/progress/main.go
+++ b/examples/progress/main.go
@@ -12,13 +12,10 @@ func main() {
url := os.Args[1]
directory := os.Args[2]
- r, err := git.NewFilesystemRepository(directory)
- CheckIfError(err)
-
// Clone the given repository to the given directory
Info("git clone %s %s", url, directory)
- err = r.Clone(&git.CloneOptions{
+ _, err := git.PlainClone(directory, false, &git.CloneOptions{
URL: url,
Depth: 1,
diff --git a/examples/push/main.go b/examples/push/main.go
index 9a30599..250e5fe 100644
--- a/examples/push/main.go
+++ b/examples/push/main.go
@@ -1,7 +1,6 @@
package main
import (
- "fmt"
"os"
"gopkg.in/src-d/go-git.v4"
@@ -9,14 +8,13 @@ import (
)
func main() {
- CheckArgs("<repo path>")
- repoPath := os.Args[1]
+ CheckArgs("<repository-path>")
+ path := os.Args[1]
- repo, err := git.NewFilesystemRepository(repoPath)
+ r, err := git.PlainOpen(path)
CheckIfError(err)
- err = repo.Push(&git.PushOptions{})
+ Info("git push")
+ err = r.Push(&git.PushOptions{})
CheckIfError(err)
-
- fmt.Print("pushed")
}
diff --git a/examples/remotes/main.go b/examples/remotes/main.go
index c0d71f5..6c9593a 100644
--- a/examples/remotes/main.go
+++ b/examples/remotes/main.go
@@ -7,17 +7,18 @@ import (
"gopkg.in/src-d/go-git.v4/config"
. "gopkg.in/src-d/go-git.v4/examples"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/storage/memory"
)
func main() {
// Create a new repository
Info("git init")
- r := git.NewMemoryRepository()
+ r, err := git.Init(memory.NewStorage(), nil)
+ CheckIfError(err)
// Add a new remote, with the default fetch refspec
Info("git remote add example https://github.com/git-fixtures/basic.git")
-
- _, err := r.CreateRemote(&config.RemoteConfig{
+ _, err = r.CreateRemote(&config.RemoteConfig{
Name: "example",
URL: "https://github.com/git-fixtures/basic.git",
})
diff --git a/examples/showcase/main.go b/examples/showcase/main.go
index 4d1206d..0b9d82e 100644
--- a/examples/showcase/main.go
+++ b/examples/showcase/main.go
@@ -12,16 +12,15 @@ import (
)
func main() {
- CheckArgs("<url>")
+ CheckArgs("<url> <path>")
url := os.Args[1]
-
- r := git.NewMemoryRepository()
+ path := os.Args[2]
// Clone the given repository, creating the remote, the local branches
// and fetching the objects, exactly as:
- Info("git clone %s", url)
+ Info("git clone %s %s", url, path)
- err := r.Clone(&git.CloneOptions{URL: url})
+ r, err := git.PlainClone(path, false, &git.CloneOptions{URL: url})
CheckIfError(err)
// Getting the latest commit on the current branch
diff --git a/examples/storage/aerospike/storage.go b/examples/storage/aerospike/storage.go
index 3ce5f5b..712c329 100644
--- a/examples/storage/aerospike/storage.go
+++ b/examples/storage/aerospike/storage.go
@@ -8,6 +8,7 @@ import (
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/index"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
driver "github.com/aerospike/aerospike-client-go"
@@ -248,6 +249,44 @@ func (s *Storage) buildConfigKey() (*driver.Key, error) {
return driver.NewKey(s.ns, configSet, fmt.Sprintf("%s|config", s.url))
}
+func (s *Storage) Index() (*index.Index, error) {
+ key, err := s.buildIndexKey()
+ if err != nil {
+ return nil, err
+ }
+
+ rec, err := s.client.Get(nil, key)
+ if err != nil {
+ return nil, err
+ }
+
+ idx := &index.Index{}
+ return idx, json.Unmarshal(rec.Bins["blob"].([]byte), idx)
+}
+
+func (s *Storage) SetIndex(idx *index.Index) error {
+ key, err := s.buildIndexKey()
+ if err != nil {
+ return err
+ }
+
+ json, err := json.Marshal(idx)
+ if err != nil {
+ return err
+ }
+
+ bins := driver.BinMap{
+ urlField: s.url,
+ "blob": json,
+ }
+
+ return s.client.Put(nil, key, bins)
+}
+
+func (s *Storage) buildIndexKey() (*driver.Key, error) {
+ return driver.NewKey(s.ns, configSet, fmt.Sprintf("%s|index", s.url))
+}
+
func (s *Storage) Shallow() ([]plumbing.Hash, error) {
key, err := s.buildShallowKey()
if err != nil {
@@ -283,7 +322,7 @@ func (s *Storage) SetShallow(hash []plumbing.Hash) error {
}
func (s *Storage) buildShallowKey() (*driver.Key, error) {
- return driver.NewKey(s.ns, configSet, fmt.Sprintf("%s|config", s.url))
+ return driver.NewKey(s.ns, configSet, fmt.Sprintf("%s|shallow", s.url))
}
func createIndexes(c *driver.Client, ns string) error {
diff --git a/examples/storage/main.go b/examples/storage/main.go
index b047e43..05752a1 100644
--- a/examples/storage/main.go
+++ b/examples/storage/main.go
@@ -26,30 +26,31 @@ func main() {
s, err := aerospike.NewStorage(client, "test", url)
CheckIfError(err)
- // A new repository instance using as storage the custom implementation
- r, err := git.NewRepository(s)
- CheckIfError(err)
-
switch action {
case "clone":
- clone(r, url)
+ clone(s, url)
case "log":
- log(r)
+ log(s)
default:
panic("unknown option")
}
}
-func clone(r *git.Repository, url string) {
+func clone(s git.Storer, url string) {
// Clone the given repository, all the objects, references and
- // configuration sush as remotes, are save into the Aerospike database.
+ // configuration sush as remotes, are save into the Aerospike database
+ // using the custom storer
Info("git clone %s", url)
- err := r.Clone(&git.CloneOptions{URL: url})
+ _, err := git.Clone(s, nil, &git.CloneOptions{URL: url})
CheckIfError(err)
}
-func log(r *git.Repository) {
+func log(s git.Storer) {
+ // We open the repository using as storer the custom implementation
+ r, err := git.Open(s, nil)
+ CheckIfError(err)
+
// Prints the history of the repository starting in the current HEAD, the
// objects are retrieved from Aerospike database.
Info("git log --oneline")
diff --git a/fixtures/fixtures.go b/fixtures/fixtures.go
index a37c5e5..b3fc717 100644
--- a/fixtures/fixtures.go
+++ b/fixtures/fixtures.go
@@ -67,7 +67,10 @@ var fixtures = Fixtures{{
URL: "https://github.com/git-fixtures/basic.git",
DotGitHash: plumbing.NewHash("935e5ac17c41c309c356639816ea0694a568c484"),
}, {
-
+ Tags: []string{"worktree"},
+ URL: "https://github.com/git-fixtures/basic.git",
+ WorktreeHash: plumbing.NewHash("e4413db6700d0e72e7680b17c3d5ebbc2d1861bc"),
+}, {
Tags: []string{"packfile", ".git", "unpacked", "multi-packfile"},
URL: "https://github.com/src-d/go-git.git",
Head: plumbing.NewHash("e8788ad9165781196e917292d6055cba1d78664e"),
@@ -155,6 +158,7 @@ type Fixture struct {
Head plumbing.Hash
PackfileHash plumbing.Hash
DotGitHash plumbing.Hash
+ WorktreeHash plumbing.Hash
ObjectsCount int32
}
diff --git a/plumbing/format/index/decoder.go b/plumbing/format/index/decoder.go
index 9069c9e..aba5fe8 100644
--- a/plumbing/format/index/decoder.go
+++ b/plumbing/format/index/decoder.go
@@ -112,8 +112,15 @@ func (d *Decoder) readEntry(idx *Index) (*Entry, error) {
}
read := entryHeaderLength
- e.CreatedAt = time.Unix(int64(sec), int64(nsec))
- e.ModifiedAt = time.Unix(int64(msec), int64(mnsec))
+
+ if sec != 0 || nsec != 0 {
+ e.CreatedAt = time.Unix(int64(sec), int64(nsec))
+ }
+
+ if msec != 0 || mnsec != 0 {
+ e.ModifiedAt = time.Unix(int64(msec), int64(mnsec))
+ }
+
e.Stage = Stage(flags>>12) & 0x3
if flags&entryExtended != 0 {
diff --git a/plumbing/format/index/decoder_test.go b/plumbing/format/index/decoder_test.go
index 4614701..47a8186 100644
--- a/plumbing/format/index/decoder_test.go
+++ b/plumbing/format/index/decoder_test.go
@@ -41,6 +41,7 @@ func (s *IndexSuite) TestDecodeEntries(c *C) {
c.Assert(idx.Entries, HasLen, 9)
e := idx.Entries[0]
+
c.Assert(e.CreatedAt.Unix(), Equals, int64(1480626693))
c.Assert(e.CreatedAt.Nanosecond(), Equals, 498593596)
c.Assert(e.ModifiedAt.Unix(), Equals, int64(1480626693))
@@ -111,10 +112,8 @@ func (s *IndexSuite) TestDecodeMergeConflict(c *C) {
// stagged files
for i, e := range idx.Entries[4:7] {
c.Assert(e.Stage, Equals, expected[i].Stage)
- c.Assert(e.CreatedAt.Unix(), Equals, int64(0))
- c.Assert(e.CreatedAt.Nanosecond(), Equals, 0)
- c.Assert(e.ModifiedAt.Unix(), Equals, int64(0))
- c.Assert(e.ModifiedAt.Nanosecond(), Equals, 0)
+ c.Assert(e.CreatedAt.IsZero(), Equals, true)
+ c.Assert(e.ModifiedAt.IsZero(), Equals, true)
c.Assert(e.Dev, Equals, uint32(0))
c.Assert(e.Inode, Equals, uint32(0))
c.Assert(e.UID, Equals, uint32(0))
diff --git a/plumbing/object/tree.go b/plumbing/object/tree.go
index f3e03bc..e197310 100644
--- a/plumbing/object/tree.go
+++ b/plumbing/object/tree.go
@@ -18,8 +18,12 @@ import (
const (
maxTreeDepth = 1024
startingStackSize = 8
- submoduleMode = 0160000
- directoryMode = 0040000
+
+ FileMode os.FileMode = 0100644
+ ExecutableMode os.FileMode = 0100755
+ SubmoduleMode os.FileMode = 0160000
+ SymlinkMode os.FileMode = 0120000
+ TreeMode os.FileMode = 0040000
)
// New errors defined by this package.
@@ -239,10 +243,14 @@ func (t *Tree) decodeFileMode(mode string) (os.FileMode, error) {
}
m := os.FileMode(fm)
- switch fm {
- case 0040000: //tree
+ switch os.FileMode(fm) {
+ case FileMode:
+ m = 0644
+ case ExecutableMode:
+ m = 0755
+ case TreeMode:
m = m | os.ModeDir
- case 0120000: //symlink
+ case SymlinkMode:
m = m | os.ModeSymlink
}
@@ -367,7 +375,7 @@ func (w *TreeWalker) Next() (name string, entry TreeEntry, err error) {
return
}
- if entry.Mode == submoduleMode {
+ if entry.Mode == SubmoduleMode {
err = nil
continue
}
diff --git a/plumbing/object/tree_test.go b/plumbing/object/tree_test.go
index 9b9614b..8613300 100644
--- a/plumbing/object/tree_test.go
+++ b/plumbing/object/tree_test.go
@@ -28,7 +28,7 @@ func (s *TreeSuite) TestDecode(c *C) {
c.Assert(s.Tree.Entries, HasLen, 8)
c.Assert(s.Tree.Entries[0].Name, Equals, ".gitignore")
c.Assert(s.Tree.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88")
- c.Assert(s.Tree.Entries[0].Mode.String(), Equals, "-rw-r--r--")
+ c.Assert(s.Tree.Entries[0].Mode, Equals, os.FileMode(0644))
c.Assert(s.Tree.Entries[4].Name, Equals, "go")
c.Assert(s.Tree.Entries[4].Hash.String(), Equals, "a39771a7651f97faf5c72e08224d857fc35133db")
c.Assert(s.Tree.Entries[4].Mode.String(), Equals, "d---------")
@@ -883,567 +883,567 @@ func (s *TreeSuite) TestTreeDecodeReadBug(c *C) {
expected := &Tree{
Entries: []TreeEntry{
{
- Name: "alter.c", Mode: 0x81a4,
+ Name: "alter.c", Mode: 0644,
Hash: plumbing.Hash{0xa4, 0x9d, 0x33, 0x49, 0xd7, 0xe2, 0x3f, 0xb5, 0x81, 0x19, 0x4f, 0x4c, 0xb5, 0x9a, 0xc0, 0xd5, 0x1b, 0x2, 0x1f, 0x78},
},
{
- Name: "analyze.c", Mode: 0x81a4,
+ Name: "analyze.c", Mode: 0644,
Hash: plumbing.Hash{0x9a, 0x3e, 0x95, 0x97, 0xdb, 0xb, 0x3, 0x20, 0x77, 0xc9, 0x1d, 0x96, 0x9d, 0x22, 0xc6, 0x27, 0x3f, 0x70, 0x2a, 0xc},
},
{
- Name: "attach.c", Mode: 0x81a4,
+ Name: "attach.c", Mode: 0644,
Hash: plumbing.Hash{0xb8, 0xe1, 0x21, 0x99, 0xb5, 0x7d, 0xe8, 0x11, 0xea, 0xe0, 0xd0, 0x61, 0x42, 0xd5, 0xac, 0x4f, 0xd4, 0x30, 0xb1, 0xd8},
},
{
- Name: "auth.c", Mode: 0x81a4,
+ Name: "auth.c", Mode: 0644,
Hash: plumbing.Hash{0xd3, 0x8b, 0xb8, 0x36, 0xa7, 0x84, 0xfb, 0xfa, 0xb6, 0xab, 0x7b, 0x3, 0xd4, 0xe6, 0xdd, 0x43, 0xed, 0xc4, 0x1f, 0xa7},
},
{
- Name: "backup.c", Mode: 0x81a4,
+ Name: "backup.c", Mode: 0644,
Hash: plumbing.Hash{0x25, 0x2f, 0x61, 0xcf, 0xca, 0xa8, 0xfc, 0xf3, 0x13, 0x7e, 0x8, 0xed, 0x68, 0x47, 0xdc, 0xfe, 0x1d, 0xc1, 0xde, 0x54},
},
{
- Name: "bitvec.c", Mode: 0x81a4,
+ Name: "bitvec.c", Mode: 0644,
Hash: plumbing.Hash{0x52, 0x18, 0x4a, 0xa9, 0x64, 0xce, 0x18, 0x98, 0xf3, 0x5d, 0x1b, 0x3d, 0x87, 0x87, 0x1c, 0x2d, 0xe, 0xf4, 0xc5, 0x3d},
},
{
- Name: "btmutex.c", Mode: 0x81a4,
+ Name: "btmutex.c", Mode: 0644,
Hash: plumbing.Hash{0xd8, 0x7d, 0x4d, 0x5f, 0xee, 0xb6, 0x30, 0x7a, 0xec, 0xdc, 0x9a, 0x83, 0x11, 0x14, 0x89, 0xab, 0x30, 0xc6, 0x78, 0xc3},
},
{
- Name: "btree.c", Mode: 0x81a4,
+ Name: "btree.c", Mode: 0644,
Hash: plumbing.Hash{0x3c, 0xa6, 0x5, 0x83, 0xe3, 0xc8, 0xe3, 0x12, 0x0, 0xf9, 0x73, 0xe0, 0xe9, 0xc4, 0x53, 0x62, 0x58, 0xb2, 0x64, 0x39},
},
{
- Name: "btree.h", Mode: 0x81a4,
+ Name: "btree.h", Mode: 0644,
Hash: plumbing.Hash{0xac, 0xe0, 0xf8, 0xcd, 0x21, 0x77, 0x70, 0xa2, 0xf6, 0x6b, 0x2e, 0xb8, 0x71, 0xbb, 0xc5, 0xfd, 0xc6, 0xfc, 0x2b, 0x68},
},
{
- Name: "btreeInt.h", Mode: 0x81a4,
+ Name: "btreeInt.h", Mode: 0644,
Hash: plumbing.Hash{0xce, 0x3c, 0x54, 0x93, 0xf8, 0xca, 0xd0, 0xbc, 0x54, 0x8a, 0xe8, 0xe4, 0x4e, 0x51, 0x28, 0x31, 0xd8, 0xfa, 0xc4, 0x31},
},
{
- Name: "build.c", Mode: 0x81a4,
+ Name: "build.c", Mode: 0644,
Hash: plumbing.Hash{0x3c, 0x91, 0xcd, 0xcf, 0xdb, 0x7b, 0x1, 0x7c, 0xbc, 0x2d, 0x5c, 0x29, 0x57, 0x1a, 0x98, 0x27, 0xd, 0xe0, 0x71, 0xe6},
},
{
- Name: "callback.c", Mode: 0x81a4,
+ Name: "callback.c", Mode: 0644,
Hash: plumbing.Hash{0xd4, 0xc, 0x65, 0xcb, 0x92, 0x45, 0x80, 0x29, 0x6a, 0xd0, 0x69, 0xa0, 0x4b, 0xf9, 0xc9, 0xe9, 0x53, 0x4e, 0xca, 0xa7},
},
{
- Name: "complete.c", Mode: 0x81a4,
+ Name: "complete.c", Mode: 0644,
Hash: plumbing.Hash{0x9e, 0x91, 0x40, 0x8, 0x5c, 0x0, 0x46, 0xed, 0x3b, 0xf6, 0xf4, 0x48, 0x52, 0x20, 0x69, 0x2d, 0xca, 0x17, 0x43, 0xc5},
},
{
- Name: "crypto.c", Mode: 0x81a4,
+ Name: "crypto.c", Mode: 0644,
Hash: plumbing.Hash{0x25, 0x51, 0xe6, 0xba, 0x2, 0x39, 0xf8, 0x5a, 0x35, 0x77, 0x96, 0xa8, 0xdd, 0xa8, 0xca, 0x3e, 0x29, 0x70, 0x93, 0xf8},
},
{
- Name: "crypto.h", Mode: 0x81a4,
+ Name: "crypto.h", Mode: 0644,
Hash: plumbing.Hash{0xf7, 0x1f, 0x53, 0x2c, 0xdc, 0x44, 0x8f, 0xa, 0x1d, 0xd5, 0xc6, 0xef, 0xf5, 0xfb, 0xd3, 0x3a, 0x91, 0x55, 0xaa, 0x97},
},
{
- Name: "crypto_cc.c", Mode: 0x81a4,
+ Name: "crypto_cc.c", Mode: 0644,
Hash: plumbing.Hash{0x53, 0x7d, 0xf7, 0xe3, 0xb3, 0x6a, 0xb5, 0xcf, 0xdd, 0x6f, 0xca, 0x40, 0x28, 0xeb, 0xca, 0xe1, 0x86, 0x87, 0xd6, 0x4d},
},
{
- Name: "crypto_impl.c", Mode: 0x81a4,
+ Name: "crypto_impl.c", Mode: 0644,
Hash: plumbing.Hash{0xa5, 0x89, 0x27, 0xc7, 0x6e, 0xf6, 0x20, 0x56, 0x77, 0xbe, 0x5c, 0x1a, 0x8e, 0x80, 0xc9, 0x83, 0x56, 0xb3, 0xa9, 0xd3},
},
{
- Name: "crypto_libtomcrypt.c", Mode: 0x81a4,
+ Name: "crypto_libtomcrypt.c", Mode: 0644,
Hash: plumbing.Hash{0x1a, 0x33, 0x83, 0xe0, 0x1, 0xa7, 0x21, 0x11, 0xc3, 0xf6, 0x61, 0x92, 0x22, 0xb0, 0x65, 0xf4, 0xbd, 0x1, 0xb, 0xe1},
},
{
- Name: "crypto_openssl.c", Mode: 0x81a4,
+ Name: "crypto_openssl.c", Mode: 0644,
Hash: plumbing.Hash{0xd0, 0x19, 0x81, 0x3b, 0x47, 0x6c, 0x52, 0xd0, 0x20, 0xe2, 0xc0, 0xac, 0xd5, 0x24, 0xe9, 0xea, 0x3d, 0xf, 0xb9, 0xfe},
},
{
- Name: "ctime.c", Mode: 0x81a4,
+ Name: "ctime.c", Mode: 0644,
Hash: plumbing.Hash{0x60, 0x59, 0x5f, 0xf8, 0x8d, 0x92, 0xf7, 0x8, 0x26, 0x4, 0xfb, 0xd9, 0xdf, 0x9a, 0xfe, 0xa1, 0x6a, 0xe8, 0x6f, 0xf},
},
{
- Name: "date.c", Mode: 0x81a4,
+ Name: "date.c", Mode: 0644,
Hash: plumbing.Hash{0x75, 0x8d, 0xd7, 0xc8, 0x9b, 0xca, 0x39, 0x37, 0xa9, 0xd, 0x70, 0x6e, 0xa9, 0x82, 0xce, 0x3a, 0xcf, 0x11, 0xd1, 0x83},
},
{
- Name: "delete.c", Mode: 0x81a4,
+ Name: "delete.c", Mode: 0644,
Hash: plumbing.Hash{0x63, 0x4e, 0x11, 0x55, 0x63, 0xae, 0x12, 0xba, 0x65, 0x58, 0xcc, 0xc5, 0x12, 0xae, 0xd6, 0x31, 0xc0, 0x66, 0xba, 0xd8},
},
{
- Name: "expr.c", Mode: 0x81a4,
+ Name: "expr.c", Mode: 0644,
Hash: plumbing.Hash{0x66, 0x3, 0x97, 0xe0, 0x78, 0xae, 0x48, 0xb2, 0xe7, 0x17, 0x5e, 0x33, 0x85, 0x67, 0x78, 0x19, 0x72, 0x2d, 0xdd, 0x6c},
},
{
- Name: "fault.c", Mode: 0x81a4,
+ Name: "fault.c", Mode: 0644,
Hash: plumbing.Hash{0xc3, 0x2, 0x8c, 0x4f, 0x93, 0x6e, 0xdf, 0x96, 0x71, 0x2d, 0xbe, 0x73, 0xa0, 0x76, 0x62, 0xf0, 0xa2, 0x6b, 0x1d, 0xa},
},
{
- Name: "fkey.c", Mode: 0x81a4,
+ Name: "fkey.c", Mode: 0644,
Hash: plumbing.Hash{0xac, 0x35, 0xbc, 0x19, 0x4c, 0xde, 0xb1, 0x27, 0x98, 0x9b, 0x9, 0x40, 0x35, 0xce, 0xe0, 0x6f, 0x57, 0x37, 0x6f, 0x5e},
},
{
- Name: "func.c", Mode: 0x81a4,
+ Name: "func.c", Mode: 0644,
Hash: plumbing.Hash{0xc0, 0x2f, 0x9, 0x6a, 0xda, 0xd5, 0xbc, 0xe9, 0xac, 0x83, 0xd3, 0x5f, 0xf, 0x46, 0x9, 0xd6, 0xf6, 0xd4, 0x3b, 0xe5},
},
{
- Name: "global.c", Mode: 0x81a4,
+ Name: "global.c", Mode: 0644,
Hash: plumbing.Hash{0x7b, 0x2, 0xcf, 0x21, 0x30, 0xe0, 0xd1, 0xa7, 0xb8, 0x89, 0xd8, 0x44, 0xc, 0xcc, 0x82, 0x8, 0xf7, 0xb6, 0x7b, 0xf9},
},
{
- Name: "hash.c", Mode: 0x81a4,
+ Name: "hash.c", Mode: 0644,
Hash: plumbing.Hash{0xe8, 0x1d, 0xcf, 0x95, 0xe4, 0x38, 0x48, 0xfa, 0x70, 0x86, 0xb7, 0xf7, 0x81, 0xc0, 0x90, 0xad, 0xc7, 0xe6, 0xca, 0x8e},
},
{
- Name: "hash.h", Mode: 0x81a4,
+ Name: "hash.h", Mode: 0644,
Hash: plumbing.Hash{0x82, 0xb7, 0xc5, 0x8c, 0x71, 0x9, 0xb, 0x54, 0x7e, 0x10, 0x17, 0x42, 0xaa, 0x9, 0x51, 0x73, 0x9f, 0xf2, 0xee, 0xe7},
},
{
- Name: "hwtime.h", Mode: 0x81a4,
+ Name: "hwtime.h", Mode: 0644,
Hash: plumbing.Hash{0xb8, 0xbc, 0x5a, 0x29, 0x5b, 0xe3, 0xfa, 0xc8, 0x35, 0x1f, 0xa9, 0xf0, 0x8a, 0x77, 0x57, 0x9d, 0x59, 0xc9, 0xa8, 0xe4},
},
{
- Name: "insert.c", Mode: 0x81a4,
+ Name: "insert.c", Mode: 0644,
Hash: plumbing.Hash{0x9a, 0x56, 0x61, 0xf5, 0x9a, 0x72, 0x95, 0x2b, 0xe6, 0xc1, 0x67, 0xa0, 0xc2, 0xdb, 0x15, 0x9b, 0x91, 0xb7, 0x1f, 0xae},
},
{
- Name: "journal.c", Mode: 0x81a4,
+ Name: "journal.c", Mode: 0644,
Hash: plumbing.Hash{0xfe, 0xd2, 0x7b, 0xe3, 0xe3, 0x80, 0x55, 0xd2, 0x20, 0x43, 0x95, 0xcd, 0xe6, 0xff, 0xc9, 0x45, 0x89, 0xfb, 0xf5, 0xe8},
},
{
- Name: "legacy.c", Mode: 0x81a4,
+ Name: "legacy.c", Mode: 0644,
Hash: plumbing.Hash{0x94, 0x64, 0x9a, 0xe7, 0x5, 0xab, 0x93, 0x85, 0x10, 0x8d, 0xd, 0x88, 0x7a, 0xf0, 0x75, 0x92, 0x89, 0xfb, 0x23, 0xcb},
},
{
- Name: "lempar.c", Mode: 0x81a4,
+ Name: "lempar.c", Mode: 0644,
Hash: plumbing.Hash{0x2a, 0xfa, 0xa6, 0xce, 0xa6, 0xd8, 0x29, 0x60, 0x2c, 0x27, 0x86, 0xc1, 0xf8, 0xa3, 0x7f, 0x56, 0x7c, 0xf6, 0xfd, 0x53},
},
{
- Name: "loadext.c", Mode: 0x81a4,
+ Name: "loadext.c", Mode: 0644,
Hash: plumbing.Hash{0xcd, 0xcf, 0x6a, 0x93, 0xb8, 0xc4, 0xf, 0x91, 0x4b, 0x94, 0x24, 0xe, 0xf1, 0x4c, 0xb4, 0xa3, 0xa, 0x37, 0xec, 0xa1},
},
{
- Name: "main.c", Mode: 0x81a4,
+ Name: "main.c", Mode: 0644,
Hash: plumbing.Hash{0x39, 0xf6, 0x4, 0x21, 0xe6, 0x81, 0x27, 0x7c, 0xc3, 0xdb, 0xa0, 0x9a, 0xbe, 0x7c, 0xf7, 0x90, 0xd5, 0x28, 0xf5, 0xc3},
},
{
- Name: "malloc.c", Mode: 0x81a4,
+ Name: "malloc.c", Mode: 0644,
Hash: plumbing.Hash{0x35, 0xa4, 0x4e, 0x5f, 0x61, 0xc2, 0xe4, 0x4c, 0x48, 0x1c, 0x62, 0x51, 0xbd, 0xa, 0xae, 0x7a, 0xcd, 0xa4, 0xde, 0xb},
},
{
- Name: "mem0.c", Mode: 0x81a4,
+ Name: "mem0.c", Mode: 0644,
Hash: plumbing.Hash{0xd, 0xb, 0x66, 0x67, 0xd6, 0xa, 0x95, 0x5a, 0x6, 0x96, 0xdf, 0x62, 0x89, 0xb4, 0x91, 0x78, 0x96, 0x93, 0x43, 0xaa},
},
{
- Name: "mem1.c", Mode: 0x81a4,
+ Name: "mem1.c", Mode: 0644,
Hash: plumbing.Hash{0x35, 0x78, 0x49, 0x6f, 0x33, 0x3, 0x7, 0xb2, 0x31, 0xdf, 0xb5, 0x3c, 0xc, 0x2e, 0x1c, 0x6b, 0x32, 0x3d, 0x79, 0x1e},
},
{
- Name: "mem2.c", Mode: 0x81a4,
+ Name: "mem2.c", Mode: 0644,
Hash: plumbing.Hash{0x26, 0x44, 0x8e, 0xa8, 0xaa, 0xe0, 0x36, 0x6a, 0xf0, 0x54, 0x1a, 0xfe, 0xa4, 0x79, 0xb, 0x42, 0xf4, 0xa6, 0x9b, 0x5a},
},
{
- Name: "mem3.c", Mode: 0x81a4,
+ Name: "mem3.c", Mode: 0644,
Hash: plumbing.Hash{0x1a, 0x1b, 0x79, 0x1f, 0x28, 0xf8, 0xcf, 0x3c, 0xe4, 0xf9, 0xa3, 0x5c, 0xda, 0xd7, 0xb7, 0x10, 0x75, 0x68, 0xc7, 0x15},
},
{
- Name: "mem5.c", Mode: 0x81a4,
+ Name: "mem5.c", Mode: 0644,
Hash: plumbing.Hash{0x78, 0x3c, 0xef, 0x61, 0x76, 0xc5, 0x9c, 0xbf, 0x30, 0x91, 0x46, 0x31, 0x9, 0x5a, 0x1a, 0x54, 0xf4, 0xe4, 0x2e, 0x8},
},
{
- Name: "memjournal.c", Mode: 0x81a4,
+ Name: "memjournal.c", Mode: 0644,
Hash: plumbing.Hash{0x5, 0x72, 0x59, 0x48, 0xf6, 0x5d, 0x42, 0x7b, 0x7, 0xf7, 0xf9, 0x29, 0xac, 0xa3, 0xff, 0x22, 0x4b, 0x17, 0x53, 0xdf},
},
{
- Name: "mutex.c", Mode: 0x81a4,
+ Name: "mutex.c", Mode: 0644,
Hash: plumbing.Hash{0xb5, 0x67, 0xe7, 0xc2, 0x7e, 0xf2, 0x4, 0x10, 0x86, 0xaf, 0xe0, 0xf6, 0x96, 0x66, 0xe2, 0x7b, 0xf5, 0x9, 0x8a, 0x59},
},
{
- Name: "mutex.h", Mode: 0x81a4,
+ Name: "mutex.h", Mode: 0644,
Hash: plumbing.Hash{0x9, 0x78, 0x81, 0x22, 0x52, 0x77, 0x89, 0xa, 0x9c, 0x36, 0xc2, 0x4d, 0x41, 0xf6, 0x11, 0x4d, 0x64, 0xc0, 0x6d, 0xb3},
},
{
- Name: "mutex_noop.c", Mode: 0x81a4,
+ Name: "mutex_noop.c", Mode: 0644,
Hash: plumbing.Hash{0x45, 0x6e, 0x82, 0xa2, 0x5e, 0x27, 0x1b, 0x6, 0x14, 0xe7, 0xf4, 0xf8, 0x3c, 0x22, 0x85, 0x53, 0xb7, 0xfa, 0x1, 0x58},
},
{
- Name: "mutex_unix.c", Mode: 0x81a4,
+ Name: "mutex_unix.c", Mode: 0644,
Hash: plumbing.Hash{0xec, 0xa7, 0x29, 0x58, 0x31, 0xc2, 0xf0, 0xee, 0x48, 0xba, 0x54, 0xd0, 0x62, 0x91, 0x4d, 0x6, 0xa1, 0xdd, 0x8e, 0xbe},
},
{
- Name: "mutex_w32.c", Mode: 0x81a4,
+ Name: "mutex_w32.c", Mode: 0644,
Hash: plumbing.Hash{0x27, 0xd1, 0xa, 0xf5, 0xbd, 0x33, 0x1b, 0xdb, 0x97, 0x3f, 0x61, 0x45, 0xb7, 0x4f, 0x72, 0xb6, 0x7, 0xcf, 0xc4, 0x6e},
},
{
- Name: "notify.c", Mode: 0x81a4,
+ Name: "notify.c", Mode: 0644,
Hash: plumbing.Hash{0xfc, 0xab, 0x5b, 0xfa, 0xf0, 0x19, 0x8, 0xd3, 0xde, 0x93, 0xfa, 0x88, 0xb5, 0xea, 0xe9, 0xe9, 0x6c, 0xa3, 0xc8, 0xe8},
},
{
- Name: "os.c", Mode: 0x81a4,
+ Name: "os.c", Mode: 0644,
Hash: plumbing.Hash{0xbe, 0x2e, 0xa4, 0xcf, 0xc0, 0x19, 0x59, 0x93, 0xa3, 0x40, 0xc9, 0x2, 0xae, 0xdd, 0xf1, 0xbe, 0x4b, 0x8e, 0xd7, 0x3a},
},
{
- Name: "os.h", Mode: 0x81a4,
+ Name: "os.h", Mode: 0644,
Hash: plumbing.Hash{0x7, 0xa, 0x2d, 0xdd, 0x17, 0xf7, 0x71, 0xf9, 0x8f, 0xf8, 0xcc, 0xd6, 0xf0, 0x33, 0xbd, 0xac, 0xc5, 0xe9, 0xf6, 0xc},
},
{
- Name: "os_common.h", Mode: 0x81a4,
+ Name: "os_common.h", Mode: 0644,
Hash: plumbing.Hash{0xf6, 0xc3, 0xe7, 0xff, 0x89, 0x46, 0x30, 0x86, 0x40, 0x18, 0x22, 0xf4, 0x81, 0xe7, 0xe3, 0xb8, 0x7b, 0x2c, 0x78, 0xc7},
},
{
- Name: "os_unix.c", Mode: 0x81a4,
+ Name: "os_unix.c", Mode: 0644,
Hash: plumbing.Hash{0xab, 0xc2, 0x3a, 0x45, 0x2e, 0x72, 0xf7, 0x1c, 0x76, 0xaf, 0xa9, 0x98, 0x3c, 0x3a, 0xd9, 0xd4, 0x25, 0x61, 0x6c, 0x6d},
},
{
- Name: "os_win.c", Mode: 0x81a4,
+ Name: "os_win.c", Mode: 0644,
Hash: plumbing.Hash{0xae, 0xb0, 0x88, 0x14, 0xb3, 0xda, 0xbe, 0x81, 0xb8, 0x4c, 0xda, 0x91, 0x85, 0x82, 0xb0, 0xf, 0xfd, 0x86, 0xe4, 0x87},
},
{
- Name: "pager.c", Mode: 0x81a4,
+ Name: "pager.c", Mode: 0644,
Hash: plumbing.Hash{0x61, 0x72, 0x7f, 0xaa, 0x9c, 0xf, 0x3d, 0x56, 0x62, 0x65, 0xbe, 0x7e, 0xec, 0x5b, 0x2a, 0x35, 0xf6, 0xa4, 0xbc, 0x9f},
},
{
- Name: "pager.h", Mode: 0x81a4,
+ Name: "pager.h", Mode: 0644,
Hash: plumbing.Hash{0x6f, 0x65, 0x91, 0x36, 0xe2, 0x76, 0x7, 0x9d, 0xa4, 0x3a, 0x2e, 0x39, 0xe1, 0xb6, 0x86, 0x37, 0xec, 0xad, 0xcf, 0x68},
},
{
- Name: "parse.y", Mode: 0x81a4,
+ Name: "parse.y", Mode: 0644,
Hash: plumbing.Hash{0x83, 0x10, 0xb2, 0x69, 0x89, 0xb0, 0x5b, 0xed, 0x1e, 0x1b, 0x3, 0xda, 0x80, 0xf5, 0xc0, 0xa5, 0x2e, 0x9a, 0xd1, 0xd2},
},
{
- Name: "pcache.c", Mode: 0x81a4,
+ Name: "pcache.c", Mode: 0644,
Hash: plumbing.Hash{0x48, 0x2a, 0x18, 0x8b, 0xee, 0x19, 0x91, 0xbc, 0x8a, 0xda, 0xc9, 0x6a, 0x19, 0x3a, 0x53, 0xe5, 0x46, 0x2a, 0x8c, 0x10},
},
{
- Name: "pcache.h", Mode: 0x81a4,
+ Name: "pcache.h", Mode: 0644,
Hash: plumbing.Hash{0xf4, 0xd4, 0xad, 0x71, 0xc1, 0xd, 0x78, 0xc6, 0xda, 0xbd, 0xe2, 0x52, 0x15, 0xcd, 0x41, 0x5a, 0x76, 0x1, 0x48, 0xca},
},
{
- Name: "pcache1.c", Mode: 0x81a4,
+ Name: "pcache1.c", Mode: 0644,
Hash: plumbing.Hash{0x41, 0x47, 0xd2, 0xef, 0xf5, 0x5b, 0xdd, 0x9f, 0xf7, 0xc6, 0x86, 0xc, 0x60, 0x18, 0x10, 0x20, 0x16, 0x6c, 0x5f, 0x50},
},
{
- Name: "pragma.c", Mode: 0x81a4,
+ Name: "pragma.c", Mode: 0644,
Hash: plumbing.Hash{0x22, 0x97, 0x71, 0x69, 0x61, 0x7d, 0x49, 0x22, 0xb3, 0x99, 0x3f, 0x76, 0x9d, 0x90, 0xfa, 0x7b, 0xc4, 0x41, 0xea, 0x50},
},
{
- Name: "prepare.c", Mode: 0x81a4,
+ Name: "prepare.c", Mode: 0644,
Hash: plumbing.Hash{0xd7, 0x8d, 0x83, 0xcb, 0xd8, 0x78, 0x97, 0xf5, 0x73, 0x30, 0x3f, 0x9f, 0x57, 0xab, 0x8d, 0xe0, 0x24, 0xa6, 0xe3, 0xf8},
},
{
- Name: "printf.c", Mode: 0x81a4,
+ Name: "printf.c", Mode: 0644,
Hash: plumbing.Hash{0x9f, 0x68, 0xd2, 0x4, 0xff, 0xdc, 0x9f, 0x3d, 0x42, 0x7f, 0x80, 0xa8, 0x23, 0x9a, 0x7f, 0xa3, 0xa9, 0x8a, 0xec, 0xbd},
},
{
- Name: "random.c", Mode: 0x81a4,
+ Name: "random.c", Mode: 0644,
Hash: plumbing.Hash{0x23, 0x4e, 0xbd, 0xf6, 0x58, 0xf4, 0x36, 0xcc, 0x7c, 0x68, 0xf0, 0x27, 0xc4, 0x8b, 0xe, 0x1b, 0x9b, 0xa3, 0x4e, 0x98},
},
{
- Name: "resolve.c", Mode: 0x81a4,
+ Name: "resolve.c", Mode: 0644,
Hash: plumbing.Hash{0x91, 0xef, 0xca, 0xa1, 0xa1, 0x6b, 0xfc, 0x98, 0xfb, 0x35, 0xd8, 0x5c, 0xad, 0x15, 0x6b, 0x93, 0x53, 0x3e, 0x4e, 0x6},
},
{
- Name: "rowset.c", Mode: 0x81a4,
+ Name: "rowset.c", Mode: 0644,
Hash: plumbing.Hash{0x57, 0x61, 0xf9, 0x85, 0x50, 0xb1, 0x76, 0xcc, 0xe1, 0x1d, 0xcb, 0xce, 0xc9, 0x38, 0x99, 0xa0, 0x75, 0xbb, 0x64, 0xfd},
},
{
- Name: "select.c", Mode: 0x81a4,
+ Name: "select.c", Mode: 0644,
Hash: plumbing.Hash{0xf3, 0xf1, 0x49, 0x9, 0x63, 0x95, 0x5b, 0x8e, 0xd0, 0xc9, 0xfe, 0x6e, 0x1e, 0xec, 0x83, 0x6c, 0x1a, 0x52, 0x94, 0xb4},
},
{
- Name: "shell.c", Mode: 0x81a4,
+ Name: "shell.c", Mode: 0644,
Hash: plumbing.Hash{0x1b, 0xe2, 0x87, 0x1f, 0xed, 0x9a, 0x1f, 0xdf, 0x1d, 0xf7, 0x19, 0x8e, 0x11, 0x25, 0x36, 0x0, 0xec, 0xba, 0x76, 0xcc},
},
{
- Name: "sqlcipher.h", Mode: 0x81a4,
+ Name: "sqlcipher.h", Mode: 0644,
Hash: plumbing.Hash{0x82, 0x75, 0x30, 0x95, 0xcd, 0x17, 0x23, 0xc5, 0xff, 0x4f, 0x11, 0x15, 0xe4, 0x97, 0x55, 0x91, 0xee, 0x34, 0xf5, 0xce},
},
{
- Name: "sqlite.h.in", Mode: 0x81a4,
+ Name: "sqlite.h.in", Mode: 0644,
Hash: plumbing.Hash{0x66, 0x8, 0x82, 0x31, 0x75, 0xde, 0x5b, 0x6a, 0xd, 0x37, 0x8f, 0xdb, 0xc, 0x38, 0x18, 0xb6, 0xab, 0x4f, 0xbf, 0x8e},
},
{
- Name: "sqlite3.rc", Mode: 0x81a4,
+ Name: "sqlite3.rc", Mode: 0644,
Hash: plumbing.Hash{0x96, 0x98, 0x76, 0xda, 0x1e, 0x57, 0x14, 0x3d, 0xe0, 0xb4, 0xd1, 0xc7, 0x62, 0x9f, 0xd3, 0x35, 0x6f, 0x2e, 0x1c, 0x96},
},
{
- Name: "sqlite3ext.h", Mode: 0x81a4,
+ Name: "sqlite3ext.h", Mode: 0644,
Hash: plumbing.Hash{0x92, 0x8b, 0xb3, 0xba, 0xd9, 0xdd, 0x64, 0x3c, 0x30, 0x1d, 0xd2, 0xb0, 0xac, 0x22, 0x28, 0x7a, 0x81, 0x28, 0x48, 0x84},
},
{
- Name: "sqliteInt.h", Mode: 0x81a4,
+ Name: "sqliteInt.h", Mode: 0644,
Hash: plumbing.Hash{0x59, 0x50, 0xf2, 0x37, 0xd9, 0xf9, 0xf2, 0xd3, 0xef, 0x6b, 0xd8, 0xbe, 0x34, 0x2d, 0xcf, 0x64, 0x89, 0x22, 0x51, 0x42},
},
{
- Name: "sqliteLimit.h", Mode: 0x81a4,
+ Name: "sqliteLimit.h", Mode: 0644,
Hash: plumbing.Hash{0xc7, 0xae, 0xe5, 0x3c, 0xeb, 0xca, 0x94, 0xda, 0x51, 0xe7, 0x1a, 0x82, 0x2e, 0xa5, 0xa6, 0xde, 0xb9, 0x3, 0x85, 0xdf},
},
{
- Name: "status.c", Mode: 0x81a4,
+ Name: "status.c", Mode: 0644,
Hash: plumbing.Hash{0x28, 0x34, 0x9e, 0x6d, 0x3d, 0x20, 0x88, 0xe0, 0x0, 0x3b, 0x76, 0xf8, 0xa, 0x89, 0x54, 0xfa, 0xec, 0x59, 0x30, 0xba},
},
{
- Name: "table.c", Mode: 0x81a4,
+ Name: "table.c", Mode: 0644,
Hash: plumbing.Hash{0x26, 0xbb, 0xfb, 0x4f, 0x45, 0x6c, 0x42, 0x98, 0x25, 0x29, 0xea, 0x1a, 0x63, 0xa0, 0x17, 0x51, 0xdd, 0x3e, 0xe9, 0x5a},
},
{
- Name: "tclsqlite.c", Mode: 0x81a4,
+ Name: "tclsqlite.c", Mode: 0644,
Hash: plumbing.Hash{0xf1, 0xbb, 0x29, 0x21, 0xda, 0xc, 0x68, 0xa4, 0xf1, 0xc8, 0xe1, 0x5c, 0xf5, 0x66, 0xb2, 0x33, 0xe9, 0x2a, 0x51, 0x9f},
},
{
- Name: "test1.c", Mode: 0x81a4,
+ Name: "test1.c", Mode: 0644,
Hash: plumbing.Hash{0xa6, 0x38, 0xe4, 0x80, 0xad, 0xdf, 0x14, 0x43, 0x9c, 0xdf, 0xa4, 0xee, 0x16, 0x4d, 0xc3, 0x1b, 0x79, 0xf8, 0xbc, 0xac},
},
{
- Name: "test2.c", Mode: 0x81a4,
+ Name: "test2.c", Mode: 0644,
Hash: plumbing.Hash{0xd1, 0x30, 0xe9, 0xd0, 0x1b, 0x70, 0x24, 0xa5, 0xec, 0x6d, 0x73, 0x5, 0x92, 0xee, 0x4d, 0x1f, 0xb0, 0x2c, 0xfd, 0xb4},
},
{
- Name: "test3.c", Mode: 0x81a4,
+ Name: "test3.c", Mode: 0644,
Hash: plumbing.Hash{0xe3, 0xed, 0x31, 0xc, 0x81, 0x4, 0xfe, 0x36, 0x21, 0xce, 0xbb, 0xf, 0x51, 0xd1, 0x1, 0x45, 0x1, 0x8d, 0x4f, 0xac},
},
{
- Name: "test4.c", Mode: 0x81a4,
+ Name: "test4.c", Mode: 0644,
Hash: plumbing.Hash{0xa6, 0x37, 0x5c, 0x7c, 0xc4, 0x3, 0xf6, 0xc, 0xaa, 0xb7, 0xe9, 0x59, 0x53, 0x3e, 0x3d, 0xb1, 0xff, 0x75, 0xa, 0xe4},
},
{
- Name: "test5.c", Mode: 0x81a4,
+ Name: "test5.c", Mode: 0644,
Hash: plumbing.Hash{0x30, 0x3d, 0x12, 0x5, 0xb2, 0x26, 0x28, 0x42, 0x3d, 0x98, 0x6f, 0x71, 0xe2, 0x7c, 0x7c, 0xf7, 0x14, 0xa7, 0x45, 0xa6},
},
{
- Name: "test6.c", Mode: 0x81a4,
+ Name: "test6.c", Mode: 0644,
Hash: plumbing.Hash{0xc1, 0x51, 0xea, 0x42, 0x98, 0x9b, 0xb, 0xe2, 0x4e, 0xe4, 0xb9, 0xa4, 0xbe, 0x37, 0x8b, 0x4f, 0x63, 0x6d, 0xb6, 0x41},
},
{
- Name: "test7.c", Mode: 0x81a4,
+ Name: "test7.c", Mode: 0644,
Hash: plumbing.Hash{0x3c, 0xd4, 0xa2, 0x24, 0xd7, 0xe8, 0xe1, 0x6b, 0xd7, 0xcb, 0xe4, 0x9e, 0x2d, 0x3e, 0x94, 0xce, 0x9b, 0x17, 0xbd, 0x76},
},
{
- Name: "test8.c", Mode: 0x81a4,
+ Name: "test8.c", Mode: 0644,
Hash: plumbing.Hash{0xc5, 0x73, 0x93, 0x32, 0xd4, 0x6e, 0x57, 0x12, 0x1d, 0xa2, 0x7c, 0x3e, 0x88, 0xfd, 0xe7, 0x5a, 0xeb, 0x87, 0x10, 0xf7},
},
{
- Name: "test9.c", Mode: 0x81a4,
+ Name: "test9.c", Mode: 0644,
Hash: plumbing.Hash{0xe5, 0x99, 0x3e, 0x8f, 0xf7, 0x8f, 0x61, 0xc2, 0x43, 0x5b, 0x6f, 0x97, 0xa3, 0xb4, 0x63, 0xe2, 0x27, 0xc7, 0x67, 0xac},
},
{
- Name: "test_async.c", Mode: 0x81a4,
+ Name: "test_async.c", Mode: 0644,
Hash: plumbing.Hash{0xb0, 0xb9, 0x43, 0x18, 0x5b, 0xfc, 0x23, 0xc1, 0x7f, 0xd0, 0x8f, 0x55, 0x76, 0x8c, 0xac, 0x12, 0xa9, 0xf5, 0x69, 0x51},
},
{
- Name: "test_autoext.c", Mode: 0x81a4,
+ Name: "test_autoext.c", Mode: 0644,
Hash: plumbing.Hash{0xb5, 0x1, 0x3f, 0x31, 0x73, 0xa2, 0x17, 0x6e, 0x2d, 0x9f, 0xc, 0xaa, 0x99, 0x19, 0x30, 0x36, 0xbf, 0xc3, 0x7e, 0x91},
},
{
- Name: "test_backup.c", Mode: 0x81a4,
+ Name: "test_backup.c", Mode: 0644,
Hash: plumbing.Hash{0xe9, 0x67, 0x42, 0x4a, 0x29, 0xf, 0x73, 0x8a, 0xec, 0xfd, 0xac, 0x57, 0x8e, 0x9b, 0x87, 0xa4, 0xc4, 0xae, 0x8d, 0x7f},
},
{
- Name: "test_btree.c", Mode: 0x81a4,
+ Name: "test_btree.c", Mode: 0644,
Hash: plumbing.Hash{0xdb, 0x72, 0x88, 0x9b, 0x2a, 0xfb, 0x62, 0x72, 0x82, 0x8d, 0xda, 0x86, 0x6d, 0xcc, 0xf1, 0x22, 0xa4, 0x9a, 0x72, 0x99},
},
{
- Name: "test_config.c", Mode: 0x81a4,
+ Name: "test_config.c", Mode: 0644,
Hash: plumbing.Hash{0x53, 0x47, 0x27, 0xa0, 0x80, 0x42, 0xb6, 0xca, 0xd6, 0x7e, 0x26, 0x7e, 0x87, 0xb4, 0x3, 0xa4, 0x1a, 0x73, 0xb2, 0x99},
},
{
- Name: "test_demovfs.c", Mode: 0x81a4,
+ Name: "test_demovfs.c", Mode: 0644,
Hash: plumbing.Hash{0x63, 0x76, 0x27, 0x7, 0x1d, 0x9e, 0x28, 0xf4, 0xb3, 0x45, 0x1b, 0xbb, 0xdd, 0xf8, 0x8, 0xd1, 0xa9, 0x12, 0x0, 0xf8},
},
{
- Name: "test_devsym.c", Mode: 0x81a4,
+ Name: "test_devsym.c", Mode: 0644,
Hash: plumbing.Hash{0x21, 0xf0, 0xf6, 0x84, 0xd8, 0x61, 0x11, 0x67, 0x70, 0xde, 0xfc, 0xde, 0xcd, 0x53, 0x2b, 0xa3, 0xee, 0xab, 0xa9, 0x75},
},
{
- Name: "test_fs.c", Mode: 0x81a4,
+ Name: "test_fs.c", Mode: 0644,
Hash: plumbing.Hash{0x47, 0x8c, 0xad, 0x80, 0xb1, 0x6a, 0x90, 0x9b, 0x23, 0xbd, 0x3, 0xc2, 0xda, 0xd8, 0xb4, 0x49, 0xa7, 0x45, 0x87, 0xa1},
},
{
- Name: "test_func.c", Mode: 0x81a4,
+ Name: "test_func.c", Mode: 0644,
Hash: plumbing.Hash{0x6f, 0x9b, 0xb0, 0x3d, 0xc8, 0x8a, 0x21, 0xd6, 0x58, 0xbf, 0x99, 0x99, 0xba, 0xf6, 0x6d, 0xc1, 0xd5, 0x2e, 0xbc, 0x54},
},
{
- Name: "test_hexio.c", Mode: 0x81a4,
+ Name: "test_hexio.c", Mode: 0644,
Hash: plumbing.Hash{0xb2, 0xb, 0x5c, 0xe7, 0x30, 0xab, 0x7f, 0xa8, 0x0, 0xd2, 0xd0, 0xcc, 0x38, 0xc7, 0x72, 0x75, 0x59, 0x3e, 0xbd, 0xbb},
},
{
- Name: "test_init.c", Mode: 0x81a4,
+ Name: "test_init.c", Mode: 0644,
Hash: plumbing.Hash{0xe3, 0x72, 0x4d, 0x8b, 0xe3, 0x14, 0xdb, 0x9, 0xee, 0xa8, 0x4, 0xb, 0x9d, 0xdf, 0xc8, 0xa8, 0xbe, 0xee, 0x22, 0x91},
},
{
- Name: "test_intarray.c", Mode: 0x81a4,
+ Name: "test_intarray.c", Mode: 0644,
Hash: plumbing.Hash{0xf5, 0xc3, 0xd9, 0xe4, 0x5, 0x9a, 0x16, 0x56, 0x7, 0x34, 0x7, 0xe4, 0x3a, 0x92, 0x11, 0x79, 0x99, 0x69, 0x7b, 0x93},
},
{
- Name: "test_intarray.h", Mode: 0x81a4,
+ Name: "test_intarray.h", Mode: 0644,
Hash: plumbing.Hash{0x69, 0x13, 0x37, 0xd1, 0xae, 0xd6, 0x37, 0x15, 0xd6, 0x2e, 0x76, 0x26, 0x6f, 0xf, 0x3b, 0x50, 0x8b, 0x1, 0xa, 0x34},
},
{
- Name: "test_journal.c", Mode: 0x81a4,
+ Name: "test_journal.c", Mode: 0644,
Hash: plumbing.Hash{0xe8, 0x70, 0x1a, 0x4e, 0xea, 0xdb, 0x8e, 0xad, 0x16, 0x9d, 0x60, 0x6, 0x40, 0x7d, 0x54, 0xa8, 0x98, 0x59, 0x2d, 0x70},
},
{
- Name: "test_loadext.c", Mode: 0x81a4,
+ Name: "test_loadext.c", Mode: 0644,
Hash: plumbing.Hash{0x11, 0x37, 0xe3, 0xa9, 0xaa, 0xe9, 0x29, 0x6, 0xb8, 0x28, 0x9f, 0x6c, 0x3d, 0xaa, 0x61, 0xf0, 0xd0, 0x70, 0xf5, 0x5a},
},
{
- Name: "test_malloc.c", Mode: 0x81a4,
+ Name: "test_malloc.c", Mode: 0644,
Hash: plumbing.Hash{0xcf, 0x98, 0xa8, 0xfb, 0x21, 0x82, 0xc0, 0xba, 0xf5, 0xa, 0xd5, 0x79, 0x79, 0xb6, 0x75, 0xbb, 0x70, 0x7a, 0x93, 0xb0},
},
{
- Name: "test_multiplex.c", Mode: 0x81a4,
+ Name: "test_multiplex.c", Mode: 0644,
Hash: plumbing.Hash{0x62, 0x45, 0x41, 0xb3, 0x2a, 0x10, 0xd2, 0x1a, 0x2f, 0xd1, 0xa, 0x35, 0xee, 0x66, 0x32, 0xbd, 0xac, 0x55, 0x2d, 0x41},
},
{
- Name: "test_multiplex.h", Mode: 0x81a4,
+ Name: "test_multiplex.h", Mode: 0644,
Hash: plumbing.Hash{0xb7, 0xe1, 0xaf, 0xea, 0x5f, 0xd7, 0x8b, 0x87, 0x58, 0x2, 0x65, 0xf8, 0x4c, 0x81, 0x61, 0x2c, 0xbd, 0x2, 0x5b, 0xaf},
},
{
- Name: "test_mutex.c", Mode: 0x81a4,
+ Name: "test_mutex.c", Mode: 0644,
Hash: plumbing.Hash{0xc9, 0xb4, 0xa2, 0x9a, 0xb7, 0x5c, 0x77, 0xea, 0x5f, 0x36, 0xb5, 0x19, 0x32, 0x56, 0xd7, 0xf, 0xe6, 0x58, 0xe, 0x95},
},
{
- Name: "test_onefile.c", Mode: 0x81a4,
+ Name: "test_onefile.c", Mode: 0644,
Hash: plumbing.Hash{0x69, 0x86, 0x74, 0x41, 0xb8, 0xcc, 0x9a, 0x62, 0x1a, 0xf3, 0x24, 0x13, 0xfc, 0x63, 0xda, 0x80, 0x99, 0x37, 0x64, 0xf4},
},
{
- Name: "test_osinst.c", Mode: 0x81a4,
+ Name: "test_osinst.c", Mode: 0644,
Hash: plumbing.Hash{0x53, 0x14, 0x33, 0x31, 0x3e, 0xe3, 0x6c, 0x7, 0xeb, 0x21, 0xc0, 0x2f, 0x31, 0x15, 0xcb, 0x7a, 0x37, 0x48, 0x6c, 0x79},
},
{
- Name: "test_pcache.c", Mode: 0x81a4,
+ Name: "test_pcache.c", Mode: 0644,
Hash: plumbing.Hash{0x8f, 0xcf, 0xe7, 0xe2, 0x6e, 0x3f, 0xf1, 0x74, 0x96, 0xb8, 0x40, 0xf5, 0xd6, 0x3c, 0x75, 0x78, 0x3a, 0xff, 0x81, 0x62},
},
- {Name: "test_quota.c", Mode: 0x81a4, Hash: plumbing.Hash{
- 0xe5, 0x90, 0x99, 0x6c, 0xa4, 0xb8, 0x57, 0x4a, 0xb1, 0xe4, 0x18, 0x5d, 0x57, 0x77, 0x56, 0x66, 0x4a, 0xd2, 0x49, 0x5f}}, {Name: "test_quota.h", Mode: 0x81a4,
+ {Name: "test_quota.c", Mode: 0644, Hash: plumbing.Hash{
+ 0xe5, 0x90, 0x99, 0x6c, 0xa4, 0xb8, 0x57, 0x4a, 0xb1, 0xe4, 0x18, 0x5d, 0x57, 0x77, 0x56, 0x66, 0x4a, 0xd2, 0x49, 0x5f}}, {Name: "test_quota.h", Mode: 0644,
Hash: plumbing.Hash{0x2d, 0x7, 0x67, 0xa1, 0x9a, 0xb7, 0xc3, 0xa4, 0x21, 0xcd, 0xba, 0x6a, 0x3, 0x49, 0x20, 0x43, 0x67, 0xc2, 0x2c, 0x81},
},
{
- Name: "test_rtree.c", Mode: 0x81a4,
+ Name: "test_rtree.c", Mode: 0644,
Hash: plumbing.Hash{0xf5, 0x4a, 0xe9, 0xb0, 0x63, 0xbb, 0x73, 0x71, 0x2f, 0xcf, 0xc1, 0xc6, 0x83, 0x2e, 0x2a, 0x50, 0xf6, 0x2a, 0x97, 0xe7},
},
{
- Name: "test_schema.c", Mode: 0x81a4,
+ Name: "test_schema.c", Mode: 0644,
Hash: plumbing.Hash{0x12, 0x64, 0x44, 0x67, 0x64, 0x7d, 0x51, 0x39, 0x4a, 0x1, 0xf9, 0xfa, 0x60, 0x37, 0x62, 0x98, 0x18, 0x54, 0x66, 0xfd},
},
{
- Name: "test_server.c", Mode: 0x81a4,
+ Name: "test_server.c", Mode: 0644,
Hash: plumbing.Hash{0xed, 0x8, 0x18, 0xe6, 0xf6, 0x5f, 0x27, 0x28, 0x2d, 0xc7, 0xb1, 0xc1, 0x90, 0xec, 0x18, 0x8c, 0x89, 0x33, 0x0, 0x2b},
},
{
- Name: "test_sqllog.c", Mode: 0x81a4,
+ Name: "test_sqllog.c", Mode: 0644,
Hash: plumbing.Hash{0x4a, 0xa6, 0x8b, 0x7c, 0x42, 0x93, 0x23, 0xb8, 0xee, 0xbe, 0x6c, 0x9c, 0x2d, 0x7, 0xfc, 0x66, 0xd, 0x8d, 0x47, 0xc9},
},
{
- Name: "test_stat.c", Mode: 0x81a4,
+ Name: "test_stat.c", Mode: 0644,
Hash: plumbing.Hash{0xd4, 0xc9, 0x2, 0xb5, 0xea, 0x11, 0x1a, 0xd5, 0x8a, 0x73, 0x71, 0x12, 0xc2, 0x8f, 0x0, 0x38, 0x43, 0x4c, 0x85, 0xc0},
},
{
- Name: "test_superlock.c", Mode: 0x81a4,
+ Name: "test_superlock.c", Mode: 0644,
Hash: plumbing.Hash{0x93, 0x6f, 0xca, 0xd0, 0xc5, 0x6f, 0x6b, 0xc8, 0x58, 0x9, 0x74, 0x2f, 0x6a, 0xe1, 0xc1, 0xee, 0xb8, 0xb7, 0xd2, 0xf1},
},
{
- Name: "test_syscall.c", Mode: 0x81a4,
+ Name: "test_syscall.c", Mode: 0644,
Hash: plumbing.Hash{0x7c, 0x8, 0x73, 0xc1, 0x6d, 0x84, 0x32, 0x2, 0xf3, 0xe, 0x2d, 0xb9, 0x45, 0x9f, 0xa2, 0x99, 0x75, 0xea, 0x5e, 0x68},
},
{
- Name: "test_tclvar.c", Mode: 0x81a4,
+ Name: "test_tclvar.c", Mode: 0644,
Hash: plumbing.Hash{0x12, 0x19, 0x19, 0xc, 0x3, 0x0, 0xfd, 0x5e, 0xc7, 0xa3, 0xc5, 0x84, 0x8, 0xf3, 0x38, 0x43, 0xd2, 0xe, 0xee, 0x15},
},
{
- Name: "test_thread.c", Mode: 0x81a4,
+ Name: "test_thread.c", Mode: 0644,
Hash: plumbing.Hash{0x2f, 0x93, 0x63, 0xb7, 0x50, 0x1e, 0x51, 0x19, 0x81, 0xfe, 0x32, 0x83, 0x1f, 0xf2, 0xe8, 0xfd, 0x2f, 0x30, 0xc4, 0x93},
},
{
- Name: "test_vfs.c", Mode: 0x81a4,
+ Name: "test_vfs.c", Mode: 0644,
Hash: plumbing.Hash{0xfc, 0xd5, 0x77, 0x43, 0x9c, 0xfd, 0x6c, 0x72, 0xdd, 0xe4, 0x83, 0x58, 0x92, 0x14, 0x20, 0xcf, 0x6e, 0xf1, 0xf8, 0x6d},
},
{
- Name: "test_vfstrace.c", Mode: 0x81a4,
+ Name: "test_vfstrace.c", Mode: 0644,
Hash: plumbing.Hash{0xa, 0xac, 0xc0, 0x1f, 0xe4, 0x2e, 0x77, 0xfe, 0xb8, 0x58, 0xe4, 0xbe, 0xd0, 0xcb, 0x7e, 0x4, 0xa4, 0x35, 0xb2, 0x10},
},
{
- Name: "test_wsd.c", Mode: 0x81a4,
+ Name: "test_wsd.c", Mode: 0644,
Hash: plumbing.Hash{0x99, 0xe4, 0xa0, 0x56, 0x58, 0x1f, 0x58, 0xf4, 0x53, 0x6f, 0xdb, 0x5a, 0x5d, 0xf7, 0x5c, 0x74, 0x69, 0x8a, 0x81, 0x62},
},
{
- Name: "tokenize.c", Mode: 0x81a4,
+ Name: "tokenize.c", Mode: 0644,
Hash: plumbing.Hash{0xfa, 0xea, 0x5f, 0x26, 0xc7, 0x9c, 0x5e, 0x18, 0x8f, 0xa8, 0x7f, 0x2f, 0xdf, 0x6f, 0xf7, 0x6a, 0x7a, 0x60, 0x6, 0xc5},
},
{
- Name: "trigger.c", Mode: 0x81a4,
+ Name: "trigger.c", Mode: 0644,
Hash: plumbing.Hash{0xf1, 0xff, 0x76, 0x6e, 0x20, 0x2a, 0x45, 0x18, 0xec, 0x10, 0xe5, 0x27, 0x12, 0xc, 0xd3, 0xe, 0x83, 0xfb, 0xd0, 0x34},
},
{
- Name: "update.c", Mode: 0x81a4,
+ Name: "update.c", Mode: 0644,
Hash: plumbing.Hash{0x3a, 0xb1, 0xab, 0x2a, 0x4b, 0x65, 0xda, 0x3f, 0x19, 0x8c, 0x15, 0x84, 0xd5, 0x4d, 0x36, 0xf1, 0x8c, 0xa1, 0x21, 0x4a},
},
{
- Name: "utf.c", Mode: 0x81a4,
+ Name: "utf.c", Mode: 0644,
Hash: plumbing.Hash{0x6d, 0x5b, 0x1b, 0xfe, 0x40, 0xc, 0x37, 0x48, 0xaa, 0x70, 0xa3, 0xb2, 0xfd, 0x5e, 0xe, 0xac, 0x5f, 0xc0, 0x4d, 0xe2},
},
{
- Name: "util.c", Mode: 0x81a4,
+ Name: "util.c", Mode: 0644,
Hash: plumbing.Hash{0xd8, 0x3a, 0x63, 0x1, 0x5f, 0xd8, 0x7d, 0xcc, 0x4f, 0xb4, 0x41, 0x66, 0xfa, 0xbf, 0x2e, 0x9b, 0xc9, 0x67, 0x1e, 0xb8},
},
{
- Name: "vacuum.c", Mode: 0x81a4,
+ Name: "vacuum.c", Mode: 0644,
Hash: plumbing.Hash{0x4a, 0xfb, 0x2c, 0xca, 0x64, 0xdd, 0x60, 0x76, 0x11, 0x22, 0x2c, 0x7, 0x93, 0x2d, 0x12, 0xea, 0xcf, 0xa, 0x2c, 0x22},
},
{
- Name: "vdbe.c", Mode: 0x81a4,
+ Name: "vdbe.c", Mode: 0644,
Hash: plumbing.Hash{0xf3, 0x43, 0xe1, 0x3d, 0x4e, 0x91, 0x78, 0x4b, 0x15, 0x88, 0x10, 0xc5, 0xb7, 0xd4, 0x46, 0x84, 0xdf, 0xbf, 0xa2, 0xa5},
},
{
- Name: "vdbe.h", Mode: 0x81a4,
+ Name: "vdbe.h", Mode: 0644,
Hash: plumbing.Hash{0xfa, 0x7b, 0x31, 0xb7, 0x27, 0xa, 0x90, 0xd4, 0xf6, 0x37, 0x36, 0x5a, 0xfc, 0xc9, 0xbd, 0xa1, 0xd1, 0xb1, 0xe1, 0xd6},
},
{
- Name: "vdbeInt.h", Mode: 0x81a4,
+ Name: "vdbeInt.h", Mode: 0644,
Hash: plumbing.Hash{0x3a, 0x5b, 0x40, 0x28, 0xbb, 0xd6, 0xc9, 0x56, 0x10, 0xd7, 0xc, 0xce, 0x3, 0x69, 0xdf, 0xcd, 0x60, 0x7a, 0xa9, 0x0},
},
{
- Name: "vdbeapi.c", Mode: 0x81a4,
+ Name: "vdbeapi.c", Mode: 0644,
Hash: plumbing.Hash{0x7c, 0x86, 0x1e, 0x2d, 0x47, 0x21, 0x8c, 0x91, 0x63, 0x31, 0x77, 0x77, 0xc3, 0x7, 0x21, 0x99, 0xe9, 0xb4, 0x2, 0x80},
},
{
- Name: "vdbeaux.c", Mode: 0x81a4,
+ Name: "vdbeaux.c", Mode: 0644,
Hash: plumbing.Hash{0x2c, 0x42, 0x69, 0xa5, 0x9e, 0x6d, 0xbc, 0xe8, 0x67, 0x1c, 0x47, 0x4f, 0x34, 0x61, 0x90, 0xbe, 0x2a, 0xe, 0x18, 0x51},
},
{
- Name: "vdbeblob.c", Mode: 0x81a4,
+ Name: "vdbeblob.c", Mode: 0644,
Hash: plumbing.Hash{0x2e, 0x8f, 0xd8, 0xee, 0x74, 0x47, 0xe6, 0x46, 0x46, 0xe3, 0x49, 0x4b, 0x4c, 0x4, 0x1d, 0x3a, 0x4a, 0xbb, 0x8, 0x85},
},
{
- Name: "vdbemem.c", Mode: 0x81a4,
+ Name: "vdbemem.c", Mode: 0644,
Hash: plumbing.Hash{0x8f, 0xc2, 0x22, 0xe2, 0xde, 0x20, 0x50, 0x14, 0x50, 0xec, 0xea, 0x9d, 0x4e, 0xbf, 0xaa, 0xc9, 0x81, 0x4a, 0xae, 0x59},
},
{
- Name: "vdbesort.c", Mode: 0x81a4,
+ Name: "vdbesort.c", Mode: 0644,
Hash: plumbing.Hash{0xfd, 0xfc, 0x4a, 0x79, 0xdd, 0xc9, 0x6e, 0x59, 0x9b, 0x1b, 0xe, 0xeb, 0xac, 0xbd, 0xb8, 0x45, 0xc6, 0x38, 0x13, 0xb2},
},
{
- Name: "vdbetrace.c", Mode: 0x81a4,
+ Name: "vdbetrace.c", Mode: 0644,
Hash: plumbing.Hash{0x35, 0x62, 0x77, 0xe8, 0xd2, 0x3b, 0xca, 0xdb, 0x67, 0x6b, 0x59, 0xd1, 0xa4, 0xdc, 0xf8, 0x42, 0xfd, 0xc4, 0xc9, 0x72},
},
{
- Name: "vtab.c", Mode: 0x81a4,
+ Name: "vtab.c", Mode: 0644,
Hash: plumbing.Hash{0x95, 0x82, 0x2, 0xc3, 0x1e, 0x24, 0x15, 0xb, 0x60, 0xf1, 0xa, 0x8a, 0xf, 0x74, 0x41, 0xaf, 0xac, 0x3f, 0xbb, 0x1c},
},
{
- Name: "wal.c", Mode: 0x81a4,
+ Name: "wal.c", Mode: 0644,
Hash: plumbing.Hash{0xe6, 0x42, 0xea, 0x21, 0x5, 0xb5, 0xc5, 0x4a, 0xf3, 0x5, 0x88, 0x9, 0x62, 0x69, 0xab, 0x75, 0xcb, 0xef, 0x8f, 0xf2},
},
{
- Name: "wal.h", Mode: 0x81a4,
+ Name: "wal.h", Mode: 0644,
Hash: plumbing.Hash{0x9, 0x25, 0x46, 0x35, 0x4b, 0x34, 0xc0, 0xab, 0x3d, 0x20, 0x5, 0x6a, 0x7f, 0x8a, 0x8a, 0x52, 0xe4, 0xd0, 0xb5, 0xf5},
},
{
- Name: "walker.c", Mode: 0x81a4,
+ Name: "walker.c", Mode: 0644,
Hash: plumbing.Hash{0xe7, 0x1e, 0xd2, 0xac, 0x48, 0x4c, 0x91, 0x6c, 0x1c, 0xc1, 0x0, 0x7e, 0x5e, 0x5, 0xda, 0x47, 0x1c, 0xb4, 0x95, 0x99},
},
{
- Name: "where.c", Mode: 0x81a4,
+ Name: "where.c", Mode: 0644,
Hash: plumbing.Hash{0xe6, 0x14, 0xf4, 0xa6, 0xd8, 0x64, 0xe7, 0xe, 0xc4, 0x32, 0x8d, 0xb, 0xdb, 0x25, 0x4e, 0x3a, 0xc9, 0xf0, 0xd2, 0x87},
},
},
diff --git a/plumbing/reference.go b/plumbing/reference.go
index 98516c7..b9f4a95 100644
--- a/plumbing/reference.go
+++ b/plumbing/reference.go
@@ -42,7 +42,8 @@ func (r ReferenceName) Short() string {
}
const (
- HEAD ReferenceName = "HEAD"
+ HEAD ReferenceName = "HEAD"
+ Master ReferenceName = "refs/heads/master"
)
// Reference is a representation of git reference
diff --git a/remote_test.go b/remote_test.go
index d3171e6..0fb1dc0 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -230,7 +230,7 @@ func (s *RemoteSuite) TestPushToEmptyRepository(c *C) {
dstSto, err := filesystem.NewStorage(dstFs)
c.Assert(err, IsNil)
- dstRepo, err := NewRepository(dstSto)
+ dstRepo, err := Open(dstSto, nil)
c.Assert(err, IsNil)
iter, err := sto.IterReferences()
diff --git a/repository.go b/repository.go
index 78e5b43..56fbc3c 100644
--- a/repository.go
+++ b/repository.go
@@ -3,55 +3,166 @@ package git
import (
"errors"
"fmt"
+ "os"
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
"gopkg.in/src-d/go-git.v4/storage/filesystem"
- "gopkg.in/src-d/go-git.v4/storage/memory"
+ "srcd.works/go-billy.v1"
osfs "srcd.works/go-billy.v1/os"
)
var (
- ErrObjectNotFound = errors.New("object not found")
- ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch")
- ErrRepositoryNonEmpty = errors.New("repository non empty")
- ErrRemoteNotFound = errors.New("remote not found")
- ErrRemoteExists = errors.New("remote already exists")
+ ErrObjectNotFound = errors.New("object not found")
+ ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch")
+ ErrRepositoryNotExists = errors.New("repository not exists")
+ ErrRepositoryAlreadyExists = errors.New("repository already exists")
+ ErrRemoteNotFound = errors.New("remote not found")
+ ErrRemoteExists = errors.New("remote already exists")
+ ErrWorktreeNotProvided = errors.New("worktree should be provided")
+ ErrIsBareRepository = errors.New("worktree not available in a bare repository")
)
// Repository giturl string, auth common.AuthMethod repository struct
type Repository struct {
- r map[string]*Remote
- s Storer
+ r map[string]*Remote
+ s Storer
+ wt billy.Filesystem
+}
+
+// Init creates an empty git repository, based on the given Storer and worktree.
+// The worktree Filesystem is optional, if nil a bare repository is created. If
+// the given storer is not empty ErrRepositoryAlreadyExists is returned
+func Init(s Storer, worktree billy.Filesystem) (*Repository, error) {
+ r := newRepository(s, worktree)
+ _, err := r.Reference(plumbing.HEAD, false)
+ switch err {
+ case plumbing.ErrReferenceNotFound:
+ case nil:
+ return nil, ErrRepositoryAlreadyExists
+ default:
+ return nil, err
+ }
+
+ h := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.Master)
+ if err := s.SetReference(h); err != nil {
+ return nil, err
+ }
+
+ if worktree == nil {
+ r.setIsBare(true)
+ }
+
+ return r, nil
+}
+
+// Open opens a git repository using the given Storer and worktree filesystem,
+// if the given storer is complete empty ErrRepositoryNotExists is returned.
+// The worktree can be nil when the repository being opened is bare, if the
+// repository is a normal one (not bare) and worktree is nil the err
+// ErrWorktreeNotProvided is returned
+func Open(s Storer, worktree billy.Filesystem) (*Repository, error) {
+ _, err := s.Reference(plumbing.HEAD)
+ if err == plumbing.ErrReferenceNotFound {
+ return nil, ErrRepositoryNotExists
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ cfg, err := s.Config()
+ if err != nil {
+ return nil, err
+ }
+
+ if !cfg.Core.IsBare && worktree == nil {
+ return nil, ErrWorktreeNotProvided
+ }
+
+ return newRepository(s, worktree), nil
}
-// NewMemoryRepository creates a new repository, backed by a memory.Storage
-func NewMemoryRepository() *Repository {
- r, _ := NewRepository(memory.NewStorage())
- return r
+// Clone a repository into the given Storer and worktree Filesystem with the
+// given options, if worktree is nil a bare repository is created. If the given
+// storer is not empty ErrRepositoryAlreadyExists is returned
+func Clone(s Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) {
+ r, err := Init(s, worktree)
+ if err != nil {
+ return nil, err
+ }
+
+ return r, r.clone(o)
}
-// NewFilesystemRepository creates a new repository, backed by a filesystem.Storage
-// based on a fs.OS, if you want to use a custom one you need to use the function
-// NewRepository and build you filesystem.Storage
-func NewFilesystemRepository(path string) (*Repository, error) {
- s, err := filesystem.NewStorage(osfs.New(path))
+// PlainInit create an empty git repository at the given path. isBare defines
+// if the repository will have worktree (non-bare) or not (bare), if the path
+// is not empty ErrRepositoryAlreadyExists is returned
+func PlainInit(path string, isBare bool) (*Repository, error) {
+ var wt, dot billy.Filesystem
+
+ if isBare {
+ dot = osfs.New(path)
+ } else {
+ wt = osfs.New(path)
+ dot = wt.Dir(".git")
+ }
+
+ s, err := filesystem.NewStorage(dot)
+ if err != nil {
+ return nil, err
+ }
+
+ return Init(s, wt)
+}
+
+// PlainOpen opens a git repository from the given path. It detects is the
+// repository is bare or a normal one. If the path doesn't contain a valid
+// repository ErrRepositoryNotExists is returned
+func PlainOpen(path string) (*Repository, error) {
+ var wt, dot billy.Filesystem
+
+ fs := osfs.New(path)
+ if _, err := fs.Stat(".git"); err != nil {
+ if !os.IsNotExist(err) {
+ return nil, err
+ }
+
+ dot = fs
+ } else {
+ wt = fs
+ dot = fs.Dir(".git")
+ }
+
+ s, err := filesystem.NewStorage(dot)
if err != nil {
return nil, err
}
- return NewRepository(s)
+ return Open(s, wt)
}
-// NewRepository creates a new repository with the given Storage
-func NewRepository(s Storer) (*Repository, error) {
+// PlainClone a repository into the path with the given options, isBare defines
+// if the new repository will be bare or normal. If the path is not empty
+// ErrRepositoryAlreadyExists is returned
+func PlainClone(path string, isBare bool, o *CloneOptions) (*Repository, error) {
+ r, err := PlainInit(path, isBare)
+ if err != nil {
+ return nil, err
+ }
+
+ return r, r.clone(o)
+}
+
+func newRepository(s Storer, worktree billy.Filesystem) *Repository {
return &Repository{
- s: s,
- r: make(map[string]*Remote, 0),
- }, nil
+ s: s,
+ wt: worktree,
+ r: make(map[string]*Remote, 0),
+ }
}
// Config return the repository config
@@ -129,16 +240,7 @@ func (r *Repository) DeleteRemote(name string) error {
}
// Clone clones a remote repository
-func (r *Repository) Clone(o *CloneOptions) error {
- empty, err := r.IsEmpty()
- if err != nil {
- return err
- }
-
- if !empty {
- return ErrRepositoryNonEmpty
- }
-
+func (r *Repository) clone(o *CloneOptions) error {
if err := o.Validate(); err != nil {
return err
}
@@ -178,6 +280,10 @@ func (r *Repository) Clone(o *CloneOptions) error {
return err
}
+ if err := r.updateWorktree(); err != nil {
+ return err
+ }
+
return r.updateRemoteConfig(remote, o, c, head)
}
@@ -310,20 +416,6 @@ func updateReferenceStorerIfNeeded(
return false, nil
}
-// IsEmpty returns true if the repository is empty
-func (r *Repository) IsEmpty() (bool, error) {
- iter, err := r.References()
- if err != nil {
- return false, err
- }
-
- var count int
- return count == 0, iter.ForEach(func(r *plumbing.Reference) error {
- count++
- return nil
- })
-}
-
// Pull incorporates changes from a remote repository into the current branch.
// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
// no changes to be fetched, or an error.
@@ -368,7 +460,25 @@ func (r *Repository) Pull(o *PullOptions) error {
return NoErrAlreadyUpToDate
}
- return nil
+ return r.updateWorktree()
+}
+
+func (r *Repository) updateWorktree() error {
+ if r.wt == nil {
+ return nil
+ }
+
+ w, err := r.Worktree()
+ if err != nil {
+ return err
+ }
+
+ h, err := r.Head()
+ if err != nil {
+ return err
+ }
+
+ return w.Checkout(h.Hash())
}
// Fetch fetches changes from a remote repository.
@@ -508,3 +618,13 @@ func (r *Repository) Reference(name plumbing.ReferenceName, resolved bool) (
func (r *Repository) References() (storer.ReferenceIter, error) {
return r.s.IterReferences()
}
+
+// Worktree returns a worktree based on the given fs, if nil the default
+// worktree will be used.
+func (r *Repository) Worktree() (*Worktree, error) {
+ if r.wt == nil {
+ return nil, ErrIsBareRepository
+ }
+
+ return &Worktree{r: r, fs: r.wt}, nil
+}
diff --git a/repository_test.go b/repository_test.go
index 336582e..1c43c6e 100644
--- a/repository_test.go
+++ b/repository_test.go
@@ -3,6 +3,7 @@ package git
import (
"bytes"
"fmt"
+ "io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -12,10 +13,11 @@ import (
"gopkg.in/src-d/go-git.v4/fixtures"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem"
"gopkg.in/src-d/go-git.v4/storage/memory"
. "gopkg.in/check.v1"
- "gopkg.in/src-d/go-git.v4/storage/filesystem"
+ memoryfs "srcd.works/go-billy.v1/memory"
)
type RepositorySuite struct {
@@ -24,13 +26,95 @@ type RepositorySuite struct {
var _ = Suite(&RepositorySuite{})
-func (s *RepositorySuite) TestNewRepository(c *C) {
- r := NewMemoryRepository()
+func (s *RepositorySuite) TestInit(c *C) {
+ r, err := Init(memory.NewStorage(), memoryfs.New())
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ cfg, err := r.Config()
+ c.Assert(err, IsNil)
+ c.Assert(cfg.Core.IsBare, Equals, false)
+}
+
+func (s *RepositorySuite) TestInitBare(c *C) {
+ r, err := Init(memory.NewStorage(), nil)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ cfg, err := r.Config()
+ c.Assert(err, IsNil)
+ c.Assert(cfg.Core.IsBare, Equals, true)
+
+}
+
+func (s *RepositorySuite) TestInitAlreadyExists(c *C) {
+ st := memory.NewStorage()
+
+ r, err := Init(st, nil)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = Init(st, nil)
+ c.Assert(err, Equals, ErrRepositoryAlreadyExists)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestOpen(c *C) {
+ st := memory.NewStorage()
+
+ r, err := Init(st, memoryfs.New())
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = Open(st, memoryfs.New())
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+}
+
+func (s *RepositorySuite) TestOpenBare(c *C) {
+ st := memory.NewStorage()
+
+ r, err := Init(st, nil)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = Open(st, nil)
+ c.Assert(err, IsNil)
c.Assert(r, NotNil)
}
+func (s *RepositorySuite) TestOpenMissingWorktree(c *C) {
+ st := memory.NewStorage()
+
+ r, err := Init(st, memoryfs.New())
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = Open(st, nil)
+ c.Assert(err, Equals, ErrWorktreeNotProvided)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestOpenNotExists(c *C) {
+ r, err := Open(memory.NewStorage(), nil)
+ c.Assert(err, Equals, ErrRepositoryNotExists)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestClone(c *C) {
+ r, err := Clone(memory.NewStorage(), nil, &CloneOptions{
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ c.Assert(err, IsNil)
+
+ remotes, err := r.Remotes()
+ c.Assert(err, IsNil)
+ c.Assert(remotes, HasLen, 1)
+}
+
func (s *RepositorySuite) TestCreateRemoteAndRemote(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
remote, err := r.CreateRemote(&config.RemoteConfig{
Name: "foo",
URL: "http://foo/foo.git",
@@ -46,7 +130,7 @@ func (s *RepositorySuite) TestCreateRemoteAndRemote(c *C) {
}
func (s *RepositorySuite) TestCreateRemoteInvalid(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
remote, err := r.CreateRemote(&config.RemoteConfig{})
c.Assert(err, Equals, config.ErrRemoteConfigEmptyName)
@@ -54,7 +138,7 @@ func (s *RepositorySuite) TestCreateRemoteInvalid(c *C) {
}
func (s *RepositorySuite) TestDeleteRemote(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
_, err := r.CreateRemote(&config.RemoteConfig{
Name: "foo",
URL: "http://foo/foo.git",
@@ -70,8 +154,100 @@ func (s *RepositorySuite) TestDeleteRemote(c *C) {
c.Assert(alt, IsNil)
}
+func (s *RepositorySuite) TestPlainInit(c *C) {
+ dir, err := ioutil.TempDir("", "plain-init")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ cfg, err := r.Config()
+ c.Assert(err, IsNil)
+ c.Assert(cfg.Core.IsBare, Equals, true)
+}
+
+func (s *RepositorySuite) TestPlainInitAlreadyExists(c *C) {
+ dir, err := ioutil.TempDir("", "plain-init")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = PlainInit(dir, true)
+ c.Assert(err, Equals, ErrRepositoryAlreadyExists)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestPlainOpen(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, false)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = PlainOpen(dir)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenBare(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, true)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = PlainOpen(dir)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenNotBare(c *C) {
+ dir, err := ioutil.TempDir("", "plain-open")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainInit(dir, false)
+ c.Assert(err, IsNil)
+ c.Assert(r, NotNil)
+
+ r, err = PlainOpen(filepath.Join(dir, ".git"))
+ c.Assert(err, Equals, ErrWorktreeNotProvided)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestPlainOpenNotExists(c *C) {
+ r, err := PlainOpen("/not-exists/")
+ c.Assert(err, Equals, ErrRepositoryNotExists)
+ c.Assert(r, IsNil)
+}
+
+func (s *RepositorySuite) TestPlainClone(c *C) {
+ dir, err := ioutil.TempDir("", "plain-clone")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(dir)
+
+ r, err := PlainClone(dir, false, &CloneOptions{
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ c.Assert(err, IsNil)
+
+ remotes, err := r.Remotes()
+ c.Assert(err, IsNil)
+ c.Assert(remotes, HasLen, 1)
+}
+
func (s *RepositorySuite) TestFetch(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
_, err := r.CreateRemote(&config.RemoteConfig{
Name: DefaultRemoteName,
URL: s.GetBasicLocalRepositoryURL(),
@@ -83,7 +259,7 @@ func (s *RepositorySuite) TestFetch(c *C) {
c.Assert(err, IsNil)
c.Assert(remotes, HasLen, 1)
- _, err = r.Reference(plumbing.HEAD, false)
+ _, err = r.Head()
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
branch, err := r.Reference("refs/remotes/origin/master", false)
@@ -93,14 +269,15 @@ func (s *RepositorySuite) TestFetch(c *C) {
c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
}
-func (s *RepositorySuite) TestClone(c *C) {
- r := NewMemoryRepository()
+func (s *RepositorySuite) TestCloneDeep(c *C) {
+ fs := memoryfs.New()
+ r, _ := Init(memory.NewStorage(), fs)
head, err := r.Head()
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
c.Assert(head, IsNil)
- err = r.Clone(&CloneOptions{
+ err = r.clone(&CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
})
@@ -126,16 +303,20 @@ func (s *RepositorySuite) TestClone(c *C) {
c.Assert(branch, NotNil)
c.Assert(branch.Type(), Equals, plumbing.HashReference)
c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
+
+ fi, err := fs.ReadDir("")
+ c.Assert(err, IsNil)
+ c.Assert(fi, HasLen, 8)
}
func (s *RepositorySuite) TestCloneConfig(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
head, err := r.Head()
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
c.Assert(head, IsNil)
- err = r.Clone(&CloneOptions{
+ err = r.clone(&CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
})
@@ -149,29 +330,15 @@ func (s *RepositorySuite) TestCloneConfig(c *C) {
c.Assert(cfg.Remotes["origin"].Name, Equals, "origin")
c.Assert(cfg.Remotes["origin"].URL, Not(Equals), "")
}
-func (s *RepositorySuite) TestCloneNonEmpty(c *C) {
- r := NewMemoryRepository()
-
- head, err := r.Head()
- c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
- c.Assert(head, IsNil)
-
- o := &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}
- err = r.Clone(o)
- c.Assert(err, IsNil)
-
- err = r.Clone(o)
- c.Assert(err, Equals, ErrRepositoryNonEmpty)
-}
func (s *RepositorySuite) TestCloneSingleBranchAndNonHEAD(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
head, err := r.Head()
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
c.Assert(head, IsNil)
- err = r.Clone(&CloneOptions{
+ err = r.clone(&CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
ReferenceName: plumbing.ReferenceName("refs/heads/branch"),
SingleBranch: true,
@@ -202,13 +369,13 @@ func (s *RepositorySuite) TestCloneSingleBranchAndNonHEAD(c *C) {
}
func (s *RepositorySuite) TestCloneSingleBranch(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
head, err := r.Head()
c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
c.Assert(head, IsNil)
- err = r.Clone(&CloneOptions{
+ err = r.clone(&CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
SingleBranch: true,
})
@@ -238,8 +405,8 @@ func (s *RepositorySuite) TestCloneSingleBranch(c *C) {
}
func (s *RepositorySuite) TestCloneDetachedHEAD(c *C) {
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
ReferenceName: plumbing.ReferenceName("refs/tags/v1.0.0"),
})
@@ -251,11 +418,27 @@ func (s *RepositorySuite) TestCloneDetachedHEAD(c *C) {
c.Assert(head.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
}
+func (s *RepositorySuite) TestPullCheckout(c *C) {
+ fs := memoryfs.New()
+ r, _ := Init(memory.NewStorage(), fs)
+ r.CreateRemote(&config.RemoteConfig{
+ Name: DefaultRemoteName,
+ URL: s.GetBasicLocalRepositoryURL(),
+ })
+
+ err := r.Pull(&PullOptions{})
+ c.Assert(err, IsNil)
+
+ fi, err := fs.ReadDir("")
+ c.Assert(err, IsNil)
+ c.Assert(fi, HasLen, 8)
+}
+
func (s *RepositorySuite) TestCloneWithProgress(c *C) {
- r := NewMemoryRepository()
+ fs := memoryfs.New()
buf := bytes.NewBuffer(nil)
- err := r.Clone(&CloneOptions{
+ _, err := Clone(memory.NewStorage(), fs, &CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
Progress: buf,
})
@@ -265,7 +448,7 @@ func (s *RepositorySuite) TestCloneWithProgress(c *C) {
}
func (s *RepositorySuite) TestPullUpdateReferencesIfNeeded(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
r.CreateRemote(&config.RemoteConfig{
Name: DefaultRemoteName,
URL: s.GetBasicLocalRepositoryURL(),
@@ -293,8 +476,8 @@ func (s *RepositorySuite) TestPullUpdateReferencesIfNeeded(c *C) {
}
func (s *RepositorySuite) TestPullSingleBranch(c *C) {
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
SingleBranch: true,
})
@@ -316,7 +499,7 @@ func (s *RepositorySuite) TestPullSingleBranch(c *C) {
}
func (s *RepositorySuite) TestPullProgress(c *C) {
- r := NewMemoryRepository()
+ r, _ := Init(memory.NewStorage(), nil)
r.CreateRemote(&config.RemoteConfig{
Name: DefaultRemoteName,
@@ -335,8 +518,8 @@ func (s *RepositorySuite) TestPullProgress(c *C) {
func (s *RepositorySuite) TestPullAdd(c *C) {
path := fixtures.Basic().One().Worktree().Base()
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{
URL: fmt.Sprintf("file://%s", filepath.Join(path, ".git")),
})
@@ -383,7 +566,7 @@ func (s *RepositorySuite) TestPushToEmptyRepository(c *C) {
dstFs := fixtures.ByTag("empty").One().DotGit()
url := fmt.Sprintf("file://%s", dstFs.Base())
- r, err := NewRepository(sto)
+ r, err := Open(sto, srcFs)
c.Assert(err, IsNil)
_, err = r.CreateRemote(&config.RemoteConfig{
@@ -397,7 +580,7 @@ func (s *RepositorySuite) TestPushToEmptyRepository(c *C) {
sto, err = filesystem.NewStorage(dstFs)
c.Assert(err, IsNil)
- dstRepo, err := NewRepository(sto)
+ dstRepo, err := Open(sto, nil)
c.Assert(err, IsNil)
iter, err := sto.IterReferences()
@@ -421,31 +604,16 @@ func (s *RepositorySuite) TestPushNonExistentRemote(c *C) {
sto, err := filesystem.NewStorage(srcFs)
c.Assert(err, IsNil)
- r, err := NewRepository(sto)
+ r, err := Open(sto, srcFs)
c.Assert(err, IsNil)
err = r.Push(&PushOptions{RemoteName: "myremote"})
c.Assert(err, ErrorMatches, ".*remote not found.*")
}
-func (s *RepositorySuite) TestIsEmpty(c *C) {
- r := NewMemoryRepository()
-
- empty, err := r.IsEmpty()
- c.Assert(err, IsNil)
- c.Assert(empty, Equals, true)
-
- err = r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
- c.Assert(err, IsNil)
-
- empty, err = r.IsEmpty()
- c.Assert(err, IsNil)
- c.Assert(empty, Equals, false)
-}
-
func (s *RepositorySuite) TestCommit(c *C) {
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{
URL: s.GetBasicLocalRepositoryURL(),
})
@@ -468,8 +636,8 @@ func (s *RepositorySuite) TestCommit(c *C) {
}
func (s *RepositorySuite) TestCommits(c *C) {
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
count := 0
@@ -495,8 +663,8 @@ func (s *RepositorySuite) TestTag(c *C) {
fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
)
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{URL: url})
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{URL: url})
c.Assert(err, IsNil)
hash := plumbing.NewHash("ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc")
@@ -513,8 +681,8 @@ func (s *RepositorySuite) TestTags(c *C) {
fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
)
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{URL: url})
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{URL: url})
c.Assert(err, IsNil)
count := 0
@@ -538,8 +706,8 @@ func (s *RepositorySuite) TestTags(c *C) {
}
func (s *RepositorySuite) TestCommitIterClosePanic(c *C) {
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
commits, err := r.Commits()
@@ -548,8 +716,8 @@ func (s *RepositorySuite) TestCommitIterClosePanic(c *C) {
}
func (s *RepositorySuite) TestRef(c *C) {
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
ref, err := r.Reference(plumbing.HEAD, false)
@@ -562,8 +730,8 @@ func (s *RepositorySuite) TestRef(c *C) {
}
func (s *RepositorySuite) TestRefs(c *C) {
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
c.Assert(err, IsNil)
@@ -574,8 +742,8 @@ func (s *RepositorySuite) TestRefs(c *C) {
}
func (s *RepositorySuite) TestObject(c *C) {
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
hash := plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
@@ -587,8 +755,8 @@ func (s *RepositorySuite) TestObject(c *C) {
}
func (s *RepositorySuite) TestObjectNotFound(c *C) {
- r := NewMemoryRepository()
- err := r.Clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
+ r, _ := Init(memory.NewStorage(), nil)
+ err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
c.Assert(err, IsNil)
hash := plumbing.NewHash("0a3fb06ff80156fb153bcdcc58b5e16c2d27625c")
@@ -597,6 +765,21 @@ func (s *RepositorySuite) TestObjectNotFound(c *C) {
c.Assert(tag, IsNil)
}
+func (s *RepositorySuite) TestWorktree(c *C) {
+ def := memoryfs.New()
+ r, _ := Init(memory.NewStorage(), def)
+ w, err := r.Worktree()
+ c.Assert(err, IsNil)
+ c.Assert(w.fs, Equals, def)
+}
+
+func (s *RepositorySuite) TestWorktreeBare(c *C) {
+ r, _ := Init(memory.NewStorage(), nil)
+ w, err := r.Worktree()
+ c.Assert(err, Equals, ErrIsBareRepository)
+ c.Assert(w, IsNil)
+}
+
func ExecuteOnPath(c *C, path string, cmds ...string) error {
for _, cmd := range cmds {
err := executeOnPath(path, cmd)
diff --git a/storage/filesystem/index.go b/storage/filesystem/index.go
new file mode 100644
index 0000000..456ef0b
--- /dev/null
+++ b/storage/filesystem/index.go
@@ -0,0 +1,44 @@
+package filesystem
+
+import (
+ "os"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/format/index"
+ "gopkg.in/src-d/go-git.v4/storage/filesystem/internal/dotgit"
+)
+
+type IndexStorage struct {
+ dir *dotgit.DotGit
+}
+
+func (s *IndexStorage) SetIndex(idx *index.Index) error {
+ f, err := s.dir.IndexWriter()
+ if err != nil {
+ return err
+ }
+
+ defer f.Close()
+
+ e := index.NewEncoder(f)
+ return e.Encode(idx)
+}
+
+func (s *IndexStorage) Index() (*index.Index, error) {
+ idx := &index.Index{
+ Version: 2,
+ }
+
+ f, err := s.dir.Index()
+ if err != nil {
+ if os.IsNotExist(err) {
+ return idx, nil
+ }
+
+ return nil, err
+ }
+
+ defer f.Close()
+
+ d := index.NewDecoder(f)
+ return idx, d.Decode(idx)
+}
diff --git a/storage/filesystem/internal/dotgit/dotgit.go b/storage/filesystem/internal/dotgit/dotgit.go
index f9763d1..1281b7e 100644
--- a/storage/filesystem/internal/dotgit/dotgit.go
+++ b/storage/filesystem/internal/dotgit/dotgit.go
@@ -18,6 +18,7 @@ const (
suffix = ".git"
packedRefsPath = "packed-refs"
configPath = "config"
+ indexPath = "index"
shallowPath = "shallow"
objectsPath = "objects"
@@ -72,6 +73,16 @@ func (d *DotGit) Config() (billy.File, error) {
return d.fs.Open(configPath)
}
+// IndexWriter returns a file pointer for write to the index file
+func (d *DotGit) IndexWriter() (billy.File, error) {
+ return d.fs.Create(indexPath)
+}
+
+// Index returns a file pointer for read to the index file
+func (d *DotGit) Index() (billy.File, error) {
+ return d.fs.Open(indexPath)
+}
+
// ShallowWriter returns a file pointer for write to the shallow file
func (d *DotGit) ShallowWriter() (billy.File, error) {
return d.fs.Create(shallowPath)
diff --git a/storage/filesystem/internal/dotgit/dotgit_test.go b/storage/filesystem/internal/dotgit/dotgit_test.go
index 48f02f0..bff90af 100644
--- a/storage/filesystem/internal/dotgit/dotgit_test.go
+++ b/storage/filesystem/internal/dotgit/dotgit_test.go
@@ -154,6 +154,38 @@ func (s *SuiteDotGit) TestConfigWriteAndConfig(c *C) {
c.Assert(string(cnt), Equals, "foo")
}
+func (s *SuiteDotGit) TestIndex(c *C) {
+ fs := fixtures.Basic().ByTag(".git").One().DotGit()
+ dir := New(fs)
+
+ idx, err := dir.Index()
+ c.Assert(err, IsNil)
+ c.Assert(idx, NotNil)
+}
+
+func (s *SuiteDotGit) TestIndexWriteAndIndex(c *C) {
+ tmp, err := ioutil.TempDir("", "dot-git")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(tmp)
+
+ fs := osfs.New(tmp)
+ dir := New(fs)
+
+ f, err := dir.IndexWriter()
+ c.Assert(err, IsNil)
+
+ _, err = f.Write([]byte("foo"))
+ c.Assert(err, IsNil)
+
+ f, err = dir.Index()
+ c.Assert(err, IsNil)
+
+ cnt, err := ioutil.ReadAll(f)
+ c.Assert(err, IsNil)
+
+ c.Assert(string(cnt), Equals, "foo")
+}
+
func (s *SuiteDotGit) TestShallow(c *C) {
fs := fixtures.Basic().ByTag(".git").One().DotGit()
dir := New(fs)
diff --git a/storage/filesystem/object.go b/storage/filesystem/object.go
index 1001032..b82ca30 100644
--- a/storage/filesystem/object.go
+++ b/storage/filesystem/object.go
@@ -17,13 +17,13 @@ import (
type ObjectStorage struct {
dir *dotgit.DotGit
- index map[plumbing.Hash]index
+ index map[plumbing.Hash]idx
}
func newObjectStorage(dir *dotgit.DotGit) (ObjectStorage, error) {
s := ObjectStorage{
dir: dir,
- index: make(map[plumbing.Hash]index, 0),
+ index: make(map[plumbing.Hash]idx, 0),
}
return s, s.loadIdxFiles()
@@ -45,13 +45,13 @@ func (s *ObjectStorage) loadIdxFiles() error {
}
func (s *ObjectStorage) loadIdxFile(h plumbing.Hash) error {
- idx, err := s.dir.ObjectPackIdx(h)
+ idxfile, err := s.dir.ObjectPackIdx(h)
if err != nil {
return err
}
- s.index[h] = make(index)
- return s.index[h].Decode(idx)
+ s.index[h] = make(idx)
+ return s.index[h].Decode(idxfile)
}
func (s *ObjectStorage) NewEncodedObject() plumbing.EncodedObject {
@@ -64,9 +64,9 @@ func (s *ObjectStorage) PackfileWriter() (io.WriteCloser, error) {
return nil, err
}
- w.Notify = func(h plumbing.Hash, idx idxfile.Idxfile) {
- s.index[h] = make(index)
- for _, e := range idx.Entries {
+ w.Notify = func(h plumbing.Hash, idxfile idxfile.Idxfile) {
+ s.index[h] = make(idx)
+ for _, e := range idxfile.Entries {
s.index[h][e.Hash] = int64(e.Offset)
}
}
@@ -244,9 +244,9 @@ func (s *ObjectStorage) buildPackfileIters(
return iters, nil
}
-type index map[plumbing.Hash]int64
+type idx map[plumbing.Hash]int64
-func (i index) Decode(r io.Reader) error {
+func (i idx) Decode(r io.Reader) error {
idx := &idxfile.Idxfile{}
d := idxfile.NewDecoder(r)
diff --git a/storage/filesystem/storage.go b/storage/filesystem/storage.go
index a60d0f4..56f84b8 100644
--- a/storage/filesystem/storage.go
+++ b/storage/filesystem/storage.go
@@ -13,6 +13,7 @@ import (
type Storage struct {
ObjectStorage
ReferenceStorage
+ IndexStorage
ShallowStorage
ConfigStorage
}
@@ -28,6 +29,7 @@ func NewStorage(fs billy.Filesystem) (*Storage, error) {
return &Storage{
ObjectStorage: o,
ReferenceStorage: ReferenceStorage{dir: dir},
+ IndexStorage: IndexStorage{dir: dir},
ShallowStorage: ShallowStorage{dir: dir},
ConfigStorage: ConfigStorage{dir: dir},
}, nil
diff --git a/storage/memory/storage.go b/storage/memory/storage.go
index ecf2efa..5d0bff1 100644
--- a/storage/memory/storage.go
+++ b/storage/memory/storage.go
@@ -6,6 +6,7 @@ import (
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/index"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
)
@@ -19,6 +20,7 @@ type Storage struct {
ConfigStorage
ObjectStorage
ShallowStorage
+ IndexStorage
ReferenceStorage
}
@@ -59,6 +61,23 @@ func (c *ConfigStorage) Config() (*config.Config, error) {
return c.config, nil
}
+type IndexStorage struct {
+ index *index.Index
+}
+
+func (c *IndexStorage) SetIndex(idx *index.Index) error {
+ c.index = idx
+ return nil
+}
+
+func (c *IndexStorage) Index() (*index.Index, error) {
+ if c.index == nil {
+ c.index = &index.Index{Version: 2}
+ }
+
+ return c.index, nil
+}
+
type ObjectStorage struct {
Objects map[plumbing.Hash]plumbing.EncodedObject
Commits map[plumbing.Hash]plumbing.EncodedObject
diff --git a/storage/test/storage_suite.go b/storage/test/storage_suite.go
index bf0d10d..ca2f783 100644
--- a/storage/test/storage_suite.go
+++ b/storage/test/storage_suite.go
@@ -9,6 +9,7 @@ import (
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/index"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
. "gopkg.in/check.v1"
@@ -18,6 +19,7 @@ type Storer interface {
storer.EncodedObjectStorer
storer.ReferenceStorer
storer.ShallowStorer
+ storer.IndexStorer
config.ConfigStorer
}
@@ -295,6 +297,27 @@ func (s *BaseStorageSuite) TestSetConfigAndConfig(c *C) {
c.Assert(cfg, DeepEquals, expected)
}
+func (s *BaseStorageSuite) TestIndex(c *C) {
+ expected := &index.Index{}
+ expected.Version = 2
+
+ idx, err := s.Storer.Index()
+ c.Assert(err, IsNil)
+ c.Assert(idx, DeepEquals, expected)
+}
+
+func (s *BaseStorageSuite) TestSetIndexAndIndex(c *C) {
+ expected := &index.Index{}
+ expected.Version = 2
+
+ err := s.Storer.SetIndex(expected)
+ c.Assert(err, IsNil)
+
+ idx, err := s.Storer.Index()
+ c.Assert(err, IsNil)
+ c.Assert(idx, DeepEquals, expected)
+}
+
func (s *BaseStorageSuite) TestSetConfigInvalid(c *C) {
cfg := config.NewConfig()
cfg.Remotes["foo"] = &config.RemoteConfig{}
diff --git a/worktree.go b/worktree.go
new file mode 100644
index 0000000..8637f7b
--- /dev/null
+++ b/worktree.go
@@ -0,0 +1,311 @@
+package git
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "syscall"
+ "time"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/index"
+ "gopkg.in/src-d/go-git.v4/plumbing/object"
+
+ "srcd.works/go-billy.v1"
+)
+
+var ErrWorktreeNotClean = errors.New("worktree is not clean")
+
+type Worktree struct {
+ r *Repository
+ fs billy.Filesystem
+}
+
+func (w *Worktree) Checkout(commit plumbing.Hash) error {
+ s, err := w.Status()
+ if err != nil {
+ return err
+ }
+
+ if !s.IsClean() {
+ return ErrWorktreeNotClean
+ }
+
+ c, err := w.r.Commit(commit)
+ if err != nil {
+ return err
+ }
+
+ files, err := c.Files()
+ if err != nil {
+ return err
+ }
+
+ idx := &index.Index{Version: 2}
+ if err := files.ForEach(func(f *object.File) error {
+ return w.checkoutFile(f, idx)
+ }); err != nil {
+ return err
+ }
+
+ return w.r.s.SetIndex(idx)
+}
+
+func (w *Worktree) checkoutFile(f *object.File, idx *index.Index) error {
+ from, err := f.Reader()
+ if err != nil {
+ return err
+ }
+
+ defer from.Close()
+ to, err := w.fs.OpenFile(f.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode.Perm())
+ if err != nil {
+ return err
+ }
+
+ if _, err := io.Copy(to, from); err != nil {
+ return err
+ }
+
+ defer to.Close()
+ return w.indexFile(f, idx)
+}
+
+func (w *Worktree) indexFile(f *object.File, idx *index.Index) error {
+ fi, err := w.fs.Stat(f.Name)
+ if err != nil {
+ return err
+ }
+
+ e := index.Entry{
+ Hash: f.Hash,
+ Name: f.Name,
+ Mode: w.getMode(fi),
+ ModifiedAt: fi.ModTime(),
+ Size: uint32(fi.Size()),
+ }
+
+ // if the FileInfo.Sys() comes from os the ctime, dev, inode, uid and gid
+ // can be retrieved, otherwise this doesn't apply
+ os, ok := fi.Sys().(*syscall.Stat_t)
+ if ok {
+ e.CreatedAt = time.Unix(int64(os.Ctim.Sec), int64(os.Ctim.Nsec))
+ e.Dev = uint32(os.Dev)
+ e.Inode = uint32(os.Ino)
+ e.GID = os.Gid
+ e.UID = os.Uid
+ }
+
+ idx.Entries = append(idx.Entries, e)
+ return nil
+}
+
+func (w *Worktree) Status() (Status, error) {
+ idx, err := w.r.s.Index()
+ if err != nil {
+ return nil, err
+ }
+
+ files, err := readDirAll(w.fs)
+ if err != nil {
+ return nil, err
+ }
+
+ s := make(Status, 0)
+ for _, e := range idx.Entries {
+ fi, ok := files[e.Name]
+ delete(files, e.Name)
+
+ if !ok {
+ s.File(e.Name).Worktree = Deleted
+ continue
+ }
+
+ status, err := w.compareFileWithEntry(fi, &e)
+ if err != nil {
+ return nil, err
+ }
+
+ s.File(e.Name).Worktree = status
+ }
+
+ for f := range files {
+ s.File(f).Worktree = Untracked
+ }
+
+ return s, nil
+}
+
+func (w *Worktree) compareFileWithEntry(fi billy.FileInfo, e *index.Entry) (StatusCode, error) {
+ if fi.Size() != int64(e.Size) {
+ return Modified, nil
+ }
+
+ if w.getMode(fi) != e.Mode {
+ return Modified, nil
+ }
+
+ h, err := calcSHA1(w.fs, e.Name)
+ if h != e.Hash || err != nil {
+ return Modified, err
+
+ }
+
+ return Unmodified, nil
+}
+
+func (w *Worktree) getMode(fi billy.FileInfo) os.FileMode {
+ if fi.Mode().IsDir() {
+ return object.TreeMode
+ }
+
+ if fi.Mode()&os.ModeSymlink != 0 {
+ return object.SymlinkMode
+ }
+
+ const modeExec = 0111
+ if fi.Mode()&modeExec != 0 {
+ return object.ExecutableMode
+ }
+
+ return object.FileMode
+}
+
+// Status current status of a Worktree
+type Status map[string]*FileStatus
+
+func (s Status) File(filename string) *FileStatus {
+ if _, ok := (s)[filename]; !ok {
+ s[filename] = &FileStatus{}
+ }
+
+ return s[filename]
+
+}
+
+func (s Status) IsClean() bool {
+ for _, status := range s {
+ if status.Worktree != Unmodified || status.Staging != Unmodified {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (s Status) String() string {
+ var names []string
+ for name := range s {
+ names = append(names, name)
+ }
+
+ var output string
+ for _, name := range names {
+ status := s[name]
+ if status.Staging == 0 && status.Worktree == 0 {
+ continue
+ }
+
+ if status.Staging == Renamed {
+ name = fmt.Sprintf("%s -> %s", name, status.Extra)
+ }
+
+ output += fmt.Sprintf("%s%s %s\n", status.Staging, status.Worktree, name)
+ }
+
+ return output
+}
+
+// FileStatus status of a file in the Worktree
+type FileStatus struct {
+ Staging StatusCode
+ Worktree StatusCode
+ Extra string
+}
+
+// StatusCode status code of a file in the Worktree
+type StatusCode int8
+
+const (
+ Unmodified StatusCode = iota
+ Untracked
+ Modified
+ Added
+ Deleted
+ Renamed
+ Copied
+ UpdatedButUnmerged
+)
+
+func (c StatusCode) String() string {
+ switch c {
+ case Unmodified:
+ return " "
+ case Modified:
+ return "M"
+ case Added:
+ return "A"
+ case Deleted:
+ return "D"
+ case Renamed:
+ return "R"
+ case Copied:
+ return "C"
+ case UpdatedButUnmerged:
+ return "U"
+ case Untracked:
+ return "?"
+ default:
+ return "-"
+ }
+}
+
+func calcSHA1(fs billy.Filesystem, filename string) (plumbing.Hash, error) {
+ file, err := fs.Open(filename)
+ if err != nil {
+ return plumbing.ZeroHash, err
+ }
+
+ stat, err := fs.Stat(filename)
+ if err != nil {
+ return plumbing.ZeroHash, err
+ }
+
+ h := plumbing.NewHasher(plumbing.BlobObject, stat.Size())
+ if _, err := io.Copy(h, file); err != nil {
+ return plumbing.ZeroHash, err
+ }
+
+ return h.Sum(), nil
+}
+
+func readDirAll(filesystem billy.Filesystem) (map[string]billy.FileInfo, error) {
+ all := make(map[string]billy.FileInfo, 0)
+ return all, doReadDirAll(filesystem, "", all)
+}
+
+func doReadDirAll(fs billy.Filesystem, path string, files map[string]billy.FileInfo) error {
+ if path == ".git" {
+ return nil
+ }
+
+ l, err := fs.ReadDir(path)
+ if err != nil {
+ return err
+ }
+
+ for _, info := range l {
+ file := fs.Join(path, info.Name())
+ if !info.IsDir() {
+ files[file] = info
+ continue
+ }
+
+ if err := doReadDirAll(fs, file, files); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/worktree_test.go b/worktree_test.go
new file mode 100644
index 0000000..55c0078
--- /dev/null
+++ b/worktree_test.go
@@ -0,0 +1,166 @@
+package git
+
+import (
+ "io/ioutil"
+ "os"
+
+ "gopkg.in/src-d/go-git.v4/plumbing/format/index"
+ "gopkg.in/src-d/go-git.v4/plumbing/object"
+
+ . "gopkg.in/check.v1"
+ "srcd.works/go-billy.v1/memory"
+ osfs "srcd.works/go-billy.v1/os"
+)
+
+type WorktreeSuite struct {
+ BaseSuite
+}
+
+var _ = Suite(&WorktreeSuite{})
+
+func (s *WorktreeSuite) SetUpTest(c *C) {
+ s.buildBasicRepository(c)
+ // the index is removed if not the Repository will be not clean
+ c.Assert(s.Repository.s.SetIndex(&index.Index{Version: 2}), IsNil)
+}
+
+func (s *WorktreeSuite) TestCheckout(c *C) {
+ h, err := s.Repository.Head()
+ c.Assert(err, IsNil)
+
+ fs := memory.New()
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err = w.Checkout(h.Hash())
+ c.Assert(err, IsNil)
+
+ entries, err := fs.ReadDir("/")
+ c.Assert(err, IsNil)
+
+ c.Assert(entries, HasLen, 8)
+ ch, err := fs.Open("CHANGELOG")
+ c.Assert(err, IsNil)
+
+ content, err := ioutil.ReadAll(ch)
+ c.Assert(err, IsNil)
+ c.Assert(string(content), Equals, "Initial changelog\n")
+
+ idx, err := s.Repository.s.Index()
+ c.Assert(err, IsNil)
+ c.Assert(idx.Entries, HasLen, 9)
+}
+
+func (s *WorktreeSuite) TestCheckoutIndexMemory(c *C) {
+ h, err := s.Repository.Head()
+ c.Assert(err, IsNil)
+
+ fs := memory.New()
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err = w.Checkout(h.Hash())
+ c.Assert(err, IsNil)
+
+ idx, err := s.Repository.s.Index()
+ c.Assert(err, IsNil)
+ c.Assert(idx.Entries, HasLen, 9)
+ c.Assert(idx.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88")
+ c.Assert(idx.Entries[0].Name, Equals, ".gitignore")
+ c.Assert(idx.Entries[0].Mode, Equals, object.FileMode)
+ c.Assert(idx.Entries[0].ModifiedAt.IsZero(), Equals, false)
+ c.Assert(idx.Entries[0].Size, Equals, uint32(189))
+
+ // ctime, dev, inode, uid and gid are not supported on memory fs
+ c.Assert(idx.Entries[0].CreatedAt.IsZero(), Equals, true)
+ c.Assert(idx.Entries[0].Dev, Equals, uint32(0))
+ c.Assert(idx.Entries[0].Inode, Equals, uint32(0))
+ c.Assert(idx.Entries[0].UID, Equals, uint32(0))
+ c.Assert(idx.Entries[0].GID, Equals, uint32(0))
+}
+
+func (s *WorktreeSuite) TestCheckoutIndexOS(c *C) {
+ h, err := s.Repository.Head()
+ c.Assert(err, IsNil)
+
+ dir, err := ioutil.TempDir("", "checkout")
+ defer os.RemoveAll(dir)
+
+ fs := osfs.New(dir)
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err = w.Checkout(h.Hash())
+ c.Assert(err, IsNil)
+
+ idx, err := s.Repository.s.Index()
+ c.Assert(err, IsNil)
+ c.Assert(idx.Entries, HasLen, 9)
+ c.Assert(idx.Entries[0].Hash.String(), Equals, "32858aad3c383ed1ff0a0f9bdf231d54a00c9e88")
+ c.Assert(idx.Entries[0].Name, Equals, ".gitignore")
+ c.Assert(idx.Entries[0].Mode, Equals, object.FileMode)
+ c.Assert(idx.Entries[0].ModifiedAt.IsZero(), Equals, false)
+ c.Assert(idx.Entries[0].Size, Equals, uint32(189))
+
+ c.Assert(idx.Entries[0].CreatedAt.IsZero(), Equals, false)
+ c.Assert(idx.Entries[0].Dev, Not(Equals), uint32(0))
+ c.Assert(idx.Entries[0].Inode, Not(Equals), uint32(0))
+ c.Assert(idx.Entries[0].UID, Not(Equals), uint32(0))
+ c.Assert(idx.Entries[0].GID, Not(Equals), uint32(0))
+}
+
+func (s *WorktreeSuite) TestStatus(c *C) {
+
+ h, err := s.Repository.Head()
+ c.Assert(err, IsNil)
+
+ fs := memory.New()
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err = w.Checkout(h.Hash())
+ c.Assert(err, IsNil)
+
+ status, err := w.Status()
+ c.Assert(err, IsNil)
+
+ c.Assert(status.IsClean(), Equals, true)
+}
+
+func (s *WorktreeSuite) TestStatusModified(c *C) {
+ c.Assert(s.Repository.s.SetIndex(&index.Index{Version: 2}), IsNil)
+
+ h, err := s.Repository.Head()
+ c.Assert(err, IsNil)
+
+ dir, err := ioutil.TempDir("", "status")
+ defer os.RemoveAll(dir)
+
+ fs := osfs.New(dir)
+ w := &Worktree{
+ r: s.Repository,
+ fs: fs,
+ }
+
+ err = w.Checkout(h.Hash())
+ c.Assert(err, IsNil)
+
+ f, err := fs.Create(".gitignore")
+ c.Assert(err, IsNil)
+ _, err = f.Write([]byte("foo"))
+ c.Assert(err, IsNil)
+ err = f.Close()
+ c.Assert(err, IsNil)
+
+ status, err := w.Status()
+ c.Assert(err, IsNil)
+ c.Assert(status.IsClean(), Equals, false)
+}