diff options
31 files changed, 1320 insertions, 345 deletions
@@ -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) +} |