@@ -60,18 +60,39 @@ var (
6060// The DotGit type represents a local git repository on disk. This
6161// type is not zero-value-safe, use the New function to initialize it.
6262type DotGit struct {
63+ DotGitOptions
6364 fs billy.Filesystem
6465
6566 // incoming object directory information
6667 incomingChecked bool
6768 incomingDirName string
69+
70+ objectList []plumbing.Hash
71+ objectMap map [plumbing.Hash ]struct {}
72+ packList []plumbing.Hash
73+ packMap map [plumbing.Hash ]struct {}
74+ }
75+
76+ // DotGitOptions holds configuration options for new DotGit objects.
77+ type DotGitOptions struct {
78+ // Static means that the filesystem won't be changed while the repo is open.
79+ Static bool
6880}
6981
7082// New returns a DotGit value ready to be used. The path argument must
7183// be the absolute path of a git repository directory (e.g.
7284// "/foo/bar/.git").
7385func New (fs billy.Filesystem ) * DotGit {
74- return & DotGit {fs : fs }
86+ return NewWithOptions (fs , DotGitOptions {})
87+ }
88+
89+ // NewWithOptions creates a new DotGit and sets non default configuration
90+ // options. See New for complete help.
91+ func NewWithOptions (fs billy.Filesystem , o DotGitOptions ) * DotGit {
92+ return & DotGit {
93+ DotGitOptions : o ,
94+ fs : fs ,
95+ }
7596}
7697
7798// Initialize creates all the folder scaffolding.
@@ -143,11 +164,25 @@ func (d *DotGit) Shallow() (billy.File, error) {
143164// NewObjectPack return a writer for a new packfile, it saves the packfile to
144165// disk and also generates and save the index for the given packfile.
145166func (d * DotGit ) NewObjectPack () (* PackWriter , error ) {
167+ d .cleanPackList ()
146168 return newPackWrite (d .fs )
147169}
148170
149171// ObjectPacks returns the list of availables packfiles
150172func (d * DotGit ) ObjectPacks () ([]plumbing.Hash , error ) {
173+ if ! d .Static {
174+ return d .objectPacks ()
175+ }
176+
177+ err := d .genPackList ()
178+ if err != nil {
179+ return nil , err
180+ }
181+
182+ return d .packList , nil
183+ }
184+
185+ func (d * DotGit ) objectPacks () ([]plumbing.Hash , error ) {
151186 packDir := d .fs .Join (objectsPath , packPath )
152187 files , err := d .fs .ReadDir (packDir )
153188 if err != nil {
@@ -181,6 +216,11 @@ func (d *DotGit) objectPackPath(hash plumbing.Hash, extension string) string {
181216}
182217
183218func (d * DotGit ) objectPackOpen (hash plumbing.Hash , extension string ) (billy.File , error ) {
219+ err := d .hasPack (hash )
220+ if err != nil {
221+ return nil , err
222+ }
223+
184224 pack , err := d .fs .Open (d .objectPackPath (hash , extension ))
185225 if err != nil {
186226 if os .IsNotExist (err ) {
@@ -195,15 +235,27 @@ func (d *DotGit) objectPackOpen(hash plumbing.Hash, extension string) (billy.Fil
195235
196236// ObjectPack returns a fs.File of the given packfile
197237func (d * DotGit ) ObjectPack (hash plumbing.Hash ) (billy.File , error ) {
238+ err := d .hasPack (hash )
239+ if err != nil {
240+ return nil , err
241+ }
242+
198243 return d .objectPackOpen (hash , `pack` )
199244}
200245
201246// ObjectPackIdx returns a fs.File of the index file for a given packfile
202247func (d * DotGit ) ObjectPackIdx (hash plumbing.Hash ) (billy.File , error ) {
248+ err := d .hasPack (hash )
249+ if err != nil {
250+ return nil , err
251+ }
252+
203253 return d .objectPackOpen (hash , `idx` )
204254}
205255
206256func (d * DotGit ) DeleteOldObjectPackAndIndex (hash plumbing.Hash , t time.Time ) error {
257+ d .cleanPackList ()
258+
207259 path := d .objectPackPath (hash , `pack` )
208260 if ! t .IsZero () {
209261 fi , err := d .fs .Stat (path )
@@ -224,12 +276,23 @@ func (d *DotGit) DeleteOldObjectPackAndIndex(hash plumbing.Hash, t time.Time) er
224276
225277// NewObject return a writer for a new object file.
226278func (d * DotGit ) NewObject () (* ObjectWriter , error ) {
279+ d .cleanObjectList ()
280+
227281 return newObjectWriter (d .fs )
228282}
229283
230284// Objects returns a slice with the hashes of objects found under the
231285// .git/objects/ directory.
232286func (d * DotGit ) Objects () ([]plumbing.Hash , error ) {
287+ if d .Static {
288+ err := d .genObjectList ()
289+ if err != nil {
290+ return nil , err
291+ }
292+
293+ return d .objectList , nil
294+ }
295+
233296 var objects []plumbing.Hash
234297 err := d .ForEachObjectHash (func (hash plumbing.Hash ) error {
235298 objects = append (objects , hash )
@@ -241,9 +304,29 @@ func (d *DotGit) Objects() ([]plumbing.Hash, error) {
241304 return objects , nil
242305}
243306
244- // Objects returns a slice with the hashes of objects found under the
245- // .git/objects/ directory.
307+ // ForEachObjectHash iterates over the hashes of objects found under the
308+ // .git/objects/ directory and executes the provided .
246309func (d * DotGit ) ForEachObjectHash (fun func (plumbing.Hash ) error ) error {
310+ if ! d .Static {
311+ return d .forEachObjectHash (fun )
312+ }
313+
314+ err := d .genObjectList ()
315+ if err != nil {
316+ return err
317+ }
318+
319+ for _ , h := range d .objectList {
320+ err := fun (h )
321+ if err != nil {
322+ return err
323+ }
324+ }
325+
326+ return nil
327+ }
328+
329+ func (d * DotGit ) forEachObjectHash (fun func (plumbing.Hash ) error ) error {
247330 files , err := d .fs .ReadDir (objectsPath )
248331 if err != nil {
249332 if os .IsNotExist (err ) {
@@ -278,6 +361,87 @@ func (d *DotGit) ForEachObjectHash(fun func(plumbing.Hash) error) error {
278361 return nil
279362}
280363
364+ func (d * DotGit ) cleanObjectList () {
365+ d .objectMap = nil
366+ d .objectList = nil
367+ }
368+
369+ func (d * DotGit ) genObjectList () error {
370+ if d .objectMap != nil {
371+ return nil
372+ }
373+
374+ d .objectMap = make (map [plumbing.Hash ]struct {})
375+ return d .forEachObjectHash (func (h plumbing.Hash ) error {
376+ d .objectList = append (d .objectList , h )
377+ d .objectMap [h ] = struct {}{}
378+
379+ return nil
380+ })
381+ }
382+
383+ func (d * DotGit ) hasObject (h plumbing.Hash ) error {
384+ if ! d .Static {
385+ return nil
386+ }
387+
388+ err := d .genObjectList ()
389+ if err != nil {
390+ return err
391+ }
392+
393+ _ , ok := d .objectMap [h ]
394+ if ! ok {
395+ return plumbing .ErrObjectNotFound
396+ }
397+
398+ return nil
399+ }
400+
401+ func (d * DotGit ) cleanPackList () {
402+ d .packMap = nil
403+ d .packList = nil
404+ }
405+
406+ func (d * DotGit ) genPackList () error {
407+ if d .packMap != nil {
408+ return nil
409+ }
410+
411+ op , err := d .objectPacks ()
412+ if err != nil {
413+ return err
414+ }
415+
416+ d .packMap = make (map [plumbing.Hash ]struct {})
417+ d .packList = nil
418+
419+ for _ , h := range op {
420+ d .packList = append (d .packList , h )
421+ d .packMap [h ] = struct {}{}
422+ }
423+
424+ return nil
425+ }
426+
427+ func (d * DotGit ) hasPack (h plumbing.Hash ) error {
428+ if ! d .Static {
429+ return nil
430+ }
431+
432+ err := d .genPackList ()
433+ if err != nil {
434+ return err
435+ }
436+
437+ _ , ok := d .packMap [h ]
438+ if ! ok {
439+ return ErrPackfileNotFound
440+ }
441+
442+ return nil
443+ }
444+
281445func (d * DotGit ) objectPath (h plumbing.Hash ) string {
282446 hash := h .String ()
283447 return d .fs .Join (objectsPath , hash [0 :2 ], hash [2 :40 ])
@@ -322,6 +486,11 @@ func (d *DotGit) hasIncomingObjects() bool {
322486
323487// Object returns a fs.File pointing the object file, if exists
324488func (d * DotGit ) Object (h plumbing.Hash ) (billy.File , error ) {
489+ err := d .hasObject (h )
490+ if err != nil {
491+ return nil , err
492+ }
493+
325494 obj1 , err1 := d .fs .Open (d .objectPath (h ))
326495 if os .IsNotExist (err1 ) && d .hasIncomingObjects () {
327496 obj2 , err2 := d .fs .Open (d .incomingObjectPath (h ))
@@ -335,6 +504,11 @@ func (d *DotGit) Object(h plumbing.Hash) (billy.File, error) {
335504
336505// ObjectStat returns a os.FileInfo pointing the object file, if exists
337506func (d * DotGit ) ObjectStat (h plumbing.Hash ) (os.FileInfo , error ) {
507+ err := d .hasObject (h )
508+ if err != nil {
509+ return nil , err
510+ }
511+
338512 obj1 , err1 := d .fs .Stat (d .objectPath (h ))
339513 if os .IsNotExist (err1 ) && d .hasIncomingObjects () {
340514 obj2 , err2 := d .fs .Stat (d .incomingObjectPath (h ))
@@ -348,6 +522,8 @@ func (d *DotGit) ObjectStat(h plumbing.Hash) (os.FileInfo, error) {
348522
349523// ObjectDelete removes the object file, if exists
350524func (d * DotGit ) ObjectDelete (h plumbing.Hash ) error {
525+ d .cleanObjectList ()
526+
351527 err1 := d .fs .Remove (d .objectPath (h ))
352528 if os .IsNotExist (err1 ) && d .hasIncomingObjects () {
353529 err2 := d .fs .Remove (d .incomingObjectPath (h ))
0 commit comments