1
+ // https://github.com/git/git/blob/master/Documentation/gitrepository-layout.txt
1
2
package dotgit
2
3
3
4
import (
5
+ "crypto/sha1"
4
6
"errors"
7
+ "fmt"
8
+ "io"
5
9
"os"
6
10
"strings"
11
+ "time"
7
12
8
13
"gopkg.in/src-d/go-git.v4/core"
14
+ "gopkg.in/src-d/go-git.v4/storage/filesystem/internal/index"
9
15
"gopkg.in/src-d/go-git.v4/utils/fs"
10
16
)
11
17
12
18
const (
13
19
suffix = ".git"
14
20
packedRefsPath = "packed-refs"
15
21
configPath = "config"
22
+
23
+ objectsPath = "objects"
24
+ packPath = "pack"
25
+
26
+ packExt = ".pack"
27
+ idxExt = ".idx"
16
28
)
17
29
18
30
var (
@@ -31,23 +43,19 @@ var (
31
43
// The DotGit type represents a local git repository on disk. This
32
44
// type is not zero-value-safe, use the New function to initialize it.
33
45
type DotGit struct {
34
- fs fs.FS
35
- path string
46
+ fs fs.Filesystem
36
47
}
37
48
38
49
// New returns a DotGit value ready to be used. The path argument must
39
50
// be the absolute path of a git repository directory (e.g.
40
51
// "/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
+ }
49
55
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 )
51
59
}
52
60
53
61
// Refs scans the git directory collecting references, which it returns.
@@ -69,96 +77,97 @@ func (d *DotGit) Refs() ([]*core.Reference, error) {
69
77
return refs , nil
70
78
}
71
79
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 )
77
87
files , err := d .fs .ReadDir (packDir )
78
88
if err != nil {
79
89
if os .IsNotExist (err ) {
80
- return nil , "" , ErrPackfileNotFound
90
+ return nil , nil
81
91
}
82
92
83
- return nil , "" , err
93
+ return nil , err
84
94
}
85
95
96
+ var packs []fs.FileInfo
86
97
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 )
89
100
}
90
101
}
91
102
92
- return nil , "" , ErrPackfileNotFound
103
+ return packs , nil
93
104
}
94
105
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 ))
101
113
if err != nil {
102
114
if os .IsNotExist (err ) {
103
- return nil , "" , ErrIdxNotFound
115
+ return nil , nil , ErrPackfileNotFound
104
116
}
105
117
106
- return nil , "" , err
118
+ return
107
119
}
108
120
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 {
122
125
if os .IsNotExist (err ) {
123
- return nil , "" , ErrNotFound
126
+ return nil , nil , ErrIdxNotFound
124
127
}
125
128
126
- return nil , "" , err
129
+ return
127
130
}
128
131
129
- return d . fs , configFile , nil
132
+ return
130
133
}
131
134
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
133
136
// .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 )
138
139
if err != nil {
139
140
if os .IsNotExist (err ) {
140
- return nil , nil , ErrObjfileNotFound
141
+ return nil , nil
141
142
}
142
143
143
- return nil , nil , err
144
+ return nil , err
144
145
}
145
146
146
147
var objects []core.Hash
147
148
for _ , f := range files {
148
149
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 ))
151
152
if err != nil {
152
- return nil , nil , err
153
+ return nil , err
153
154
}
154
155
155
156
for _ , o := range d {
156
- objects = append (objects , core .NewHash (objDir + o .Name ()))
157
+ objects = append (objects , core .NewHash (base + o .Name ()))
157
158
}
158
159
}
159
160
}
160
161
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 )
162
171
}
163
172
164
173
func isHex (s string ) bool {
@@ -184,19 +193,78 @@ func isHexAlpha(b byte) bool {
184
193
return b >= 'a' && b <= 'f' || b >= 'A' && b <= 'F'
185
194
}
186
195
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
+ }
192
206
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
+ }
197
217
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 ),
199
225
}
200
226
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 ))
202
270
}
0 commit comments