Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit 49a8238

Browse files
committed
storage: filesystem idx generation (wip)
1 parent 490027a commit 49a8238

File tree

9 files changed

+341
-427
lines changed

9 files changed

+341
-427
lines changed

storage/filesystem/config.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,15 @@ func (c *ConfigStorage) DeleteRemote(name string) error {
5252
}
5353

5454
func (c *ConfigStorage) read() (*ConfigFile, error) {
55-
fs, path, err := c.dir.Config()
55+
f, err := c.dir.Config()
5656
if err != nil {
5757
return nil, err
5858
}
5959

60-
r, err := fs.Open(path)
61-
if err != nil {
62-
return nil, err
63-
}
60+
defer f.Close()
6461

65-
f := &ConfigFile{}
66-
return f, f.Decode(r)
62+
config := &ConfigFile{}
63+
return config, config.Decode(f)
6764
}
6865

6966
type ConfigFile struct {
Lines changed: 136 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
1+
// https://github.com/git/git/blob/master/Documentation/gitrepository-layout.txt
12
package dotgit
23

34
import (
5+
"crypto/sha1"
46
"errors"
7+
"fmt"
8+
"io"
59
"os"
610
"strings"
11+
"time"
712

813
"gopkg.in/src-d/go-git.v4/core"
14+
"gopkg.in/src-d/go-git.v4/storage/filesystem/internal/index"
915
"gopkg.in/src-d/go-git.v4/utils/fs"
1016
)
1117

1218
const (
1319
suffix = ".git"
1420
packedRefsPath = "packed-refs"
1521
configPath = "config"
22+
23+
objectsPath = "objects"
24+
packPath = "pack"
25+
26+
packExt = ".pack"
27+
idxExt = ".idx"
1628
)
1729

1830
var (
@@ -31,23 +43,19 @@ var (
3143
// The DotGit type represents a local git repository on disk. This
3244
// type is not zero-value-safe, use the New function to initialize it.
3345
type DotGit struct {
34-
fs fs.FS
35-
path string
46+
fs fs.Filesystem
3647
}
3748

3849
// New returns a DotGit value ready to be used. The path argument must
3950
// be the absolute path of a git repository directory (e.g.
4051
// "/foo/bar/.git").
41-
func New(fs fs.FS, path string) (*DotGit, error) {
42-
d := &DotGit{fs: fs, path: path}
43-
if _, err := fs.Stat(path); err != nil {
44-
if os.IsNotExist(err) {
45-
return nil, ErrNotFound
46-
}
47-
return nil, err
48-
}
52+
func New(fs fs.Filesystem) *DotGit {
53+
return &DotGit{fs: fs}
54+
}
4955

50-
return d, nil
56+
// Config returns the path of the config file
57+
func (d *DotGit) Config() (fs.File, error) {
58+
return d.fs.Open(configPath)
5159
}
5260

5361
// Refs scans the git directory collecting references, which it returns.
@@ -69,96 +77,97 @@ func (d *DotGit) Refs() ([]*core.Reference, error) {
6977
return refs, nil
7078
}
7179

72-
// Packfile returns the path of the packfile (really, it returns the
73-
// path of the first file in the "objects/pack/" directory with a
74-
// ".pack" extension.
75-
func (d *DotGit) Packfile() (fs.FS, string, error) {
76-
packDir := d.fs.Join(d.path, "objects", "pack")
80+
func (d *DotGit) NewObjectPack() (*PackWriter, error) {
81+
return newPackWrite(d.fs)
82+
}
83+
84+
// ObjectsPacks returns the list of availables packfiles
85+
func (d *DotGit) ObjectsPacks() ([]fs.FileInfo, error) {
86+
packDir := d.fs.Join(objectsPath, packPath)
7787
files, err := d.fs.ReadDir(packDir)
7888
if err != nil {
7989
if os.IsNotExist(err) {
80-
return nil, "", ErrPackfileNotFound
90+
return nil, nil
8191
}
8292

83-
return nil, "", err
93+
return nil, err
8494
}
8595

96+
var packs []fs.FileInfo
8697
for _, f := range files {
87-
if strings.HasSuffix(f.Name(), ".pack") {
88-
return d.fs, d.fs.Join(packDir, f.Name()), nil
98+
if strings.HasSuffix(f.Name(), packExt) {
99+
packs = append(packs, f)
89100
}
90101
}
91102

92-
return nil, "", ErrPackfileNotFound
103+
return packs, nil
93104
}
94105

95-
// Idxfile returns the path of the idx file (really, it returns the
96-
// path of the first file in the "objects/pack/" directory with an
97-
// ".idx" extension.
98-
func (d *DotGit) Idxfile() (fs.FS, string, error) {
99-
packDir := d.fs.Join(d.path, "objects", "pack")
100-
files, err := d.fs.ReadDir(packDir)
106+
// ObjectPack returns the requested packfile and his idx
107+
func (d *DotGit) ObjectPack(filename string) (pack, idx fs.File, err error) {
108+
if !strings.HasSuffix(filename, packExt) {
109+
return nil, nil, fmt.Errorf("a .pack file should be provided")
110+
}
111+
112+
pack, err = d.fs.Open(d.fs.Join(objectsPath, packPath, filename))
101113
if err != nil {
102114
if os.IsNotExist(err) {
103-
return nil, "", ErrIdxNotFound
115+
return nil, nil, ErrPackfileNotFound
104116
}
105117

106-
return nil, "", err
118+
return
107119
}
108120

109-
for _, f := range files {
110-
if strings.HasSuffix(f.Name(), ".idx") {
111-
return d.fs, d.fs.Join(packDir, f.Name()), nil
112-
}
113-
}
114-
115-
return nil, "", ErrIdxNotFound
116-
}
117-
118-
// Config returns the path of the config file
119-
func (d *DotGit) Config() (fs.FS, string, error) {
120-
configFile := d.fs.Join(d.path, configPath)
121-
if _, err := d.fs.Stat(configFile); err != nil {
121+
idxfile := filename[0:len(filename)-len(packExt)] + idxExt
122+
idxpath := d.fs.Join(objectsPath, packPath, idxfile)
123+
idx, err = d.fs.Open(idxpath)
124+
if err != nil {
122125
if os.IsNotExist(err) {
123-
return nil, "", ErrNotFound
126+
return nil, nil, ErrIdxNotFound
124127
}
125128

126-
return nil, "", err
129+
return
127130
}
128131

129-
return d.fs, configFile, nil
132+
return
130133
}
131134

132-
// Objectfiles returns a slice with the hashes of objects found under the
135+
// Objects returns a slice with the hashes of objects found under the
133136
// .git/objects/ directory.
134-
func (dg *DotGit) Objectfiles() (fs.FS, []core.Hash, error) {
135-
objsDir := dg.fs.Join(dg.path, "objects")
136-
137-
files, err := dg.fs.ReadDir(objsDir)
137+
func (d *DotGit) Objects() ([]core.Hash, error) {
138+
files, err := d.fs.ReadDir(objectsPath)
138139
if err != nil {
139140
if os.IsNotExist(err) {
140-
return nil, nil, ErrObjfileNotFound
141+
return nil, nil
141142
}
142143

143-
return nil, nil, err
144+
return nil, err
144145
}
145146

146147
var objects []core.Hash
147148
for _, f := range files {
148149
if f.IsDir() && len(f.Name()) == 2 && isHex(f.Name()) {
149-
objDir := f.Name()
150-
d, err := dg.fs.ReadDir(dg.fs.Join(objsDir, objDir))
150+
base := f.Name()
151+
d, err := d.fs.ReadDir(d.fs.Join(objectsPath, base))
151152
if err != nil {
152-
return nil, nil, err
153+
return nil, err
153154
}
154155

155156
for _, o := range d {
156-
objects = append(objects, core.NewHash(objDir+o.Name()))
157+
objects = append(objects, core.NewHash(base+o.Name()))
157158
}
158159
}
159160
}
160161

161-
return dg.fs, objects, nil
162+
return objects, nil
163+
}
164+
165+
// Object return a fs.File poiting the object file, if exists
166+
func (d *DotGit) Object(h core.Hash) (fs.File, error) {
167+
hash := h.String()
168+
file := d.fs.Join(objectsPath, hash[0:2], hash[2:40])
169+
170+
return d.fs.Open(file)
162171
}
163172

164173
func isHex(s string) bool {
@@ -184,19 +193,78 @@ func isHexAlpha(b byte) bool {
184193
return b >= 'a' && b <= 'f' || b >= 'A' && b <= 'F'
185194
}
186195

187-
// Objectfile returns the path of the object file for a given hash
188-
// *if the file exists*, otherwise returns an ErrObjfileNotFound error.
189-
func (d *DotGit) Objectfile(h core.Hash) (fs.FS, string, error) {
190-
hash := h.String()
191-
objFile := d.fs.Join(d.path, "objects", hash[0:2], hash[2:40])
196+
type PackWriter struct {
197+
fs fs.Filesystem
198+
file fs.File
199+
writer io.Writer
200+
pipeReader io.ReadCloser
201+
pipeWriter io.WriteCloser
202+
hash core.Hash
203+
index index.Index
204+
result chan error
205+
}
192206

193-
if _, err := d.fs.Stat(objFile); err != nil {
194-
if os.IsNotExist(err) {
195-
return nil, "", ErrObjfileNotFound
196-
}
207+
func newPackWrite(fs fs.Filesystem) (*PackWriter, error) {
208+
r, w := io.Pipe()
209+
210+
temp := sha1.Sum([]byte(time.Now().String()))
211+
filename := fmt.Sprintf(".%x", temp)
212+
213+
file, err := fs.Create(fs.Join(objectsPath, packPath, filename))
214+
if err != nil {
215+
return nil, err
216+
}
197217

198-
return nil, "", err
218+
writer := &PackWriter{
219+
fs: fs,
220+
file: file,
221+
writer: io.MultiWriter(w, file),
222+
pipeReader: r,
223+
pipeWriter: w,
224+
result: make(chan error),
199225
}
200226

201-
return d.fs, objFile, nil
227+
go writer.buildIndex()
228+
return writer, nil
229+
}
230+
231+
func (w *PackWriter) buildIndex() {
232+
defer w.pipeReader.Close()
233+
index, hash, err := index.NewFromPackfileInMemory(w.pipeReader)
234+
w.index = index
235+
w.hash = hash
236+
237+
w.result <- err
238+
}
239+
240+
func (w *PackWriter) Write(p []byte) (int, error) {
241+
return w.writer.Write(p)
242+
}
243+
244+
func (w *PackWriter) Close() error {
245+
defer func() {
246+
close(w.result)
247+
}()
248+
249+
if err := w.file.Close(); err != nil {
250+
return err
251+
}
252+
253+
if err := w.pipeWriter.Close(); err != nil {
254+
return err
255+
}
256+
257+
if err := <-w.result; err != nil {
258+
return err
259+
}
260+
261+
return w.save()
262+
}
263+
264+
func (w *PackWriter) save() error {
265+
base := w.fs.Join(objectsPath, packPath, fmt.Sprintf("pack-%s", w.hash))
266+
267+
//idx, err := w.fs.Create(fmt.Sprintf("%s.idx", base))
268+
269+
return w.fs.Rename(w.file.Filename(), fmt.Sprintf("%s.pack", base))
202270
}

0 commit comments

Comments
 (0)