@@ -2,25 +2,32 @@ package main
22
33import (
44 "crypto"
5+ "errors"
56 "fmt"
67 "io"
78 "log/slog"
89 "os"
9- "path/filepath"
1010 "sort"
1111 "strings"
1212
13- "github.com/go-git/go-billy/v6/osfs"
13+ "github.com/go-git/go-billy/v6"
14+ fixtures "github.com/go-git/go-git-fixtures/v5"
1415 "github.com/go-git/go-git/v6/plumbing"
1516 "github.com/go-git/go-git/v6/plumbing/format/idxfile"
1617 "github.com/go-git/go-git/v6/plumbing/format/packfile"
1718 "github.com/spf13/cobra"
1819)
1920
20- var verifyPackVerbose bool
21+ var (
22+ verifyPackVerbose bool
23+ verifyPackFixtureUrl bool
24+ verifyPackFixtureTag bool
25+ )
2126
2227func init () {
2328 verifyPackCmd .Flags ().BoolVarP (& verifyPackVerbose , "verbose" , "v" , false , "Show detailed object information" )
29+ verifyPackCmd .Flags ().BoolVarP (& verifyPackFixtureUrl , "fixture-url" , "" , false , "Use <file> as go-git-fixture url" )
30+ verifyPackCmd .Flags ().BoolVarP (& verifyPackFixtureTag , "fixture-tag" , "" , false , "Use <file> as go-git-fixture tag" )
2431 rootCmd .AddCommand (verifyPackCmd )
2532}
2633
@@ -46,59 +53,45 @@ type objectInfo struct {
4653}
4754
4855func verifyPack (path string , verbose bool ) error {
49- idxPath := path
50- packPath := path
51-
52- if strings .HasSuffix (path , ".idx" ) {
53- packPath = strings .TrimSuffix (path , ".idx" ) + ".pack"
54- } else if strings .HasSuffix (path , ".pack" ) {
55- idxPath = strings .TrimSuffix (path , ".pack" ) + ".idx"
56- } else {
57- return fmt .Errorf ("file must have .idx or .pack extension" )
58- }
59-
60- idxFile , err := os .Open (idxPath )
56+ idxFile , packFile , err := openPack (path )
6157 if err != nil {
62- return fmt . Errorf ( "failed to open index file: %w" , err )
58+ return err
6359 }
60+
6461 defer func () {
6562 err = idxFile .Close ()
6663 if err != nil {
6764 slog .Debug ("failed to close idx file" , "error" , err )
6865 }
6966 }()
7067
71- idx := idxfile .NewMemoryIndex (crypto .SHA1 .Size ())
72- dec := idxfile .NewDecoder (idxFile )
73- if err := dec .Decode (idx ); err != nil {
74- return fmt .Errorf ("failed to decode index file: %w" , err )
75- }
76-
77- fs := osfs .New (filepath .Dir (packPath ))
78- packFile , err := fs .Open (filepath .Base (packPath ))
79- if err != nil {
80- return fmt .Errorf ("failed to open pack file: %w" , err )
81- }
8268 defer func () {
8369 err = packFile .Close ()
8470 if err != nil {
8571 slog .Debug ("failed to close pack file" , "error" , err )
8672 }
8773 }()
8874
75+ idx := idxfile .NewMemoryIndex (crypto .SHA1 .Size ())
76+
77+ dec := idxfile .NewDecoder (idxFile )
78+ if err := dec .Decode (idx ); err != nil {
79+ return fmt .Errorf ("failed to decode index file: %w" , err )
80+ }
81+
8982 pf := packfile .NewPackfile (
9083 packFile ,
9184 packfile .WithIdx (idx ),
92- packfile .WithFs (fs ),
9385 )
86+
9487 defer func () {
95- err = pf .Close ()
88+ err : = pf .Close ()
9689 if err != nil {
9790 slog .Debug ("failed to close Packfile object" , "error" , err )
9891 }
9992 }()
10093
101- scanner , err := pf .Scanner () //nolint:staticcheck
94+ scanner , err := pf .Scanner ()
10295 if err != nil {
10396 return fmt .Errorf ("failed to get scanner: %w" , err )
10497 }
@@ -112,9 +105,10 @@ func verifyPack(path string, verbose bool) error {
112105
113106 for {
114107 entry , err := entries .Next ()
115- if err == io .EOF {
108+ if errors . Is ( err , io .EOF ) {
116109 break
117110 }
111+
118112 if err != nil {
119113 return fmt .Errorf ("failed to read entry: %w" , err )
120114 }
@@ -164,6 +158,7 @@ func verifyPack(path string, verbose bool) error {
164158 if err != nil {
165159 return fmt .Errorf ("failed to get object at offset %d: %w" , objects [i ].offset , err )
166160 }
161+
167162 objects [i ].typ = obj .Type ()
168163 }
169164
@@ -196,6 +191,7 @@ func verifyPack(path string, verbose bool) error {
196191
197192 // Calculate delta chain depth.
198193 depth := 1
194+
199195 var baseHash plumbing.Hash
200196
201197 switch header .Type {
@@ -227,9 +223,11 @@ func verifyPack(path string, verbose bool) error {
227223 if err != nil {
228224 break
229225 }
226+
230227 if ! scanner .Scan () {
231228 break
232229 }
230+
233231 baseHeader := scanner .Data ().Value ().(packfile.ObjectHeader )
234232
235233 depth ++
@@ -294,19 +292,64 @@ func verifyPack(path string, verbose bool) error {
294292 for length := range chainLengths {
295293 lengths = append (lengths , length )
296294 }
295+
297296 sort .Ints (lengths )
298297
299298 for _ , length := range lengths {
300299 count := chainLengths [length ]
300+
301301 objWord := "objects"
302302 if count == 1 {
303303 objWord = "object"
304304 }
305+
305306 fmt .Printf ("chain length = %d: %d %s\n " , length , count , objWord )
306307 }
307308 }
308309
309- fmt .Printf ("%s: ok\n " , packPath )
310+ fmt .Printf ("%s: ok\n " , path )
310311
311312 return nil
312313}
314+
315+ func openPack (path string ) (billy.File , billy.File , error ) {
316+ if verifyPackFixtureUrl || verifyPackFixtureTag {
317+ var f fixtures.Fixtures
318+ if verifyPackFixtureUrl {
319+ f = fixtures .ByURL (path )
320+ }
321+ if verifyPackFixtureTag {
322+ f = fixtures .ByTag (path )
323+ }
324+
325+ if len (f ) == 0 {
326+ return nil , nil , fmt .Errorf ("no fixture found for %q" , path )
327+ }
328+
329+ fixture := f .One ()
330+ return fixture .Idx (), fixture .Packfile (), nil
331+ }
332+
333+ idxPath := path
334+ packPath := path
335+
336+ if before , ok := strings .CutSuffix (path , ".idx" ); ok {
337+ packPath = before + ".pack"
338+ } else if before , ok := strings .CutSuffix (path , ".pack" ); ok {
339+ idxPath = before + ".idx"
340+ } else {
341+ return nil , nil , errors .New ("file must have .idx or .pack extension" )
342+ }
343+
344+ idxFile , err := os .Open (idxPath )
345+ if err != nil {
346+ return nil , nil , fmt .Errorf ("failed to open index file: %w" , err )
347+ }
348+
349+ packFile , err := os .Open (packPath )
350+ if err != nil {
351+ return nil , nil , fmt .Errorf ("failed to open pack file: %w" , err )
352+ }
353+
354+ return idxFile , packFile , nil
355+ }
0 commit comments