in internal/git/stats/repository_info.go [427:524]
func PackfilesInfoForRepository(ctx context.Context, repo *localrepo.Repo) (PackfilesInfo, error) {
repoPath, err := repo.Path(ctx)
if err != nil {
return PackfilesInfo{}, fmt.Errorf("getting repository path: %w", err)
}
packfilesPath := filepath.Join(repoPath, "objects", "pack")
entries, err := os.ReadDir(packfilesPath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return PackfilesInfo{}, nil
}
return PackfilesInfo{}, err
}
packfilesMetadata := classifyPackfiles(entries)
var info PackfilesInfo
for _, entry := range entries {
entryName := entry.Name()
switch {
case hasPrefixAndSuffix(entryName, "pack-", ".pack"):
size, err := entrySize(entry)
if err != nil {
return PackfilesInfo{}, fmt.Errorf("getting packfile size: %w", err)
}
info.Count++
info.Size += size
metadata := packfilesMetadata[entryName]
switch {
case metadata.hasKeep:
info.KeepCount++
info.KeepSize += size
case metadata.hasMtimes:
info.CruftCount++
info.CruftSize += size
}
case hasPrefixAndSuffix(entryName, "pack-", ".idx"):
// We ignore normal indices as every packfile would have one anyway, or
// otherwise the repository would be corrupted.
case hasPrefixAndSuffix(entryName, "pack-", ".keep"):
// We classify .keep files above.
case hasPrefixAndSuffix(entryName, "pack-", ".mtimes"):
// We classify .mtimes files above.
case hasPrefixAndSuffix(entryName, "pack-", ".rev"):
info.ReverseIndexCount++
case hasPrefixAndSuffix(entryName, "pack-", ".bitmap"):
bitmap, err := BitmapInfoForPath(filepath.Join(packfilesPath, entryName))
if err != nil {
return PackfilesInfo{}, fmt.Errorf("reading bitmap info: %w", err)
}
info.Bitmap = bitmap
case entryName == "multi-pack-index":
midxInfo, err := MultiPackIndexInfoForPath(filepath.Join(packfilesPath, entryName))
if err != nil {
return PackfilesInfo{}, fmt.Errorf("reading multi-pack-index: %w", err)
}
info.MultiPackIndex = midxInfo
case hasPrefixAndSuffix(entryName, "multi-pack-index-", ".bitmap"):
bitmap, err := BitmapInfoForPath(filepath.Join(packfilesPath, entryName))
if err != nil {
return PackfilesInfo{}, fmt.Errorf("reading multi-pack-index bitmap info: %w", err)
}
info.MultiPackIndexBitmap = bitmap
default:
size, err := entrySize(entry)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
// Unrecognized files may easily be temporary files written
// by Git. It is expected that these may get concurrently
// removed, so we just ignore the case where they've gone
// missing.
continue
}
return PackfilesInfo{}, fmt.Errorf("getting garbage size: %w", err)
}
info.GarbageCount++
info.GarbageSize += size
}
}
lastFullRepack, err := FullRepackTimestamp(repoPath)
if err != nil {
return PackfilesInfo{}, fmt.Errorf("reading full-repack timestamp: %w", err)
}
info.LastFullRepack = lastFullRepack
return info, nil
}