194 lines
7.1 KiB
PowerShell
194 lines
7.1 KiB
PowerShell
# PowerShell script to find binary files NOT tracked by Git LFS
|
|
# Run this from your repository root
|
|
|
|
param(
|
|
[string]$RepoPath = ".",
|
|
[switch]$ShowLFSStatus = $false
|
|
)
|
|
|
|
$ErrorActionPreference = "Continue"
|
|
|
|
# Common binary file extensions that should be in LFS
|
|
$binaryExtensions = @(
|
|
# Images
|
|
'bmp', 'exr', 'gif', 'hdr', 'jpg', 'jpeg', 'png', 'tga', 'tif', 'tiff', 'webp', 'heic', 'HEIC',
|
|
'ico', 'cur', 'ani', 'xcf', 'psd', 'ai', 'af', 'eps', 'raw', 'cr2', 'nef', 'dng',
|
|
# 3D/CG
|
|
'3ds', 'abc', 'blend', 'blend1', 'blend2', 'bvh', 'c4d', 'dae', 'fbx', 'fbxkey',
|
|
'ma', 'max', 'mb', 'obj', 'usd', 'usdz', 'vdb', 'bphys', 'bobj', 'bvel', 'bpointcache', 'blob',
|
|
'unitypackage', 'skp', 'stl', 'ply', 'glb', 'gltf', '3mf', 'lwo', 'lws',
|
|
# Character Creator / Reallusion
|
|
'ccAvatar', 'ccProject', 'ccCloth', 'ccProp', 'ccScene', 'ccHair',
|
|
'rlMotion', 'rlPose', 'iAvatar', 'iMotion', 'iProp', 'iMaterial', 'rlHead', 'rlHair', 'rlScene', 'rlSkintex',
|
|
'3dxProfile',
|
|
# Houdini
|
|
'hiplc', 'bgeo', 'bgeo.sc', 'hdanc', 'otllc', 'otlnc',
|
|
# Video
|
|
'avi', 'mkv', 'mov', 'MOV', 'mp4', 'webm', 'm4v', 'mpg', 'mpeg', 'wmv', 'flv', 'f4v',
|
|
# Audio
|
|
'mp3', 'wav', 'm4a', 'aac', 'ogg', 'flac', 'wma', 'aiff', 'au', 'ra', 'ram', 'mid', 'midi', 'ac3', 'dts',
|
|
# Archives
|
|
'7z', 'bz2', 'gz', 'gzip', 'lzma', 'rar', 'tar', 'xz', 'z', 'zip', 'whl', 'npz',
|
|
'pkl', 'pickle', 'joblib', 'parquet', 'feather',
|
|
# Documents
|
|
'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'odt', 'ods', 'odp', 'rtf',
|
|
# Adobe
|
|
'aegraphic', 'aep', 'prel', 'prin', 'prmf', 'prproj', 'ai', 'psd', 'pk', 'pkf',
|
|
# Data/Cache
|
|
'h5', 'hdf5', 'dat', 'db', 'sqlite', 'sqlite3', 'mdb', 'ldb', 'frm', 'ibd',
|
|
'npy', 'pkl', 'pickle', 'joblib', 'caffemodel', 'pth', 'onnx', 'pb', 'tflite',
|
|
# Binaries
|
|
'exe', 'dll', 'so', 'dylib', 'pyd', 'bin', 'o', 'a', 'lib', 'obj', 'app',
|
|
'msi', 'msp', 'msm', 'dmg', 'pkg', 'deb', 'rpm', 'snap', 'flatpak', 'AppImage',
|
|
# Fonts
|
|
'ttf', 'otf', 'woff', 'woff2', 'eot', 'pfm', 'afm', 'tfm',
|
|
# Other
|
|
'uni', 'pdn', 'pur', 'lnk', 'swf', 'fla', 'wasm', 'loom', 'mat', 'fbxkey',
|
|
'ffp3', 'data', 'sqlite3', 'db', 'raw', 'dpx', 'drp', 'lut', 'cube', 'spi1d', 'spi3d', 'clf',
|
|
# Substance
|
|
'sbs', 'sbsar', 'spp', 'sbsprs', 'sbscfg',
|
|
# Flip Fluids
|
|
'bphys', 'bobj', 'bvel', 'bpointcache', 'blob', 'ffp3',
|
|
# Game engines
|
|
'uasset', 'umap', 'upk', 'udk', 'pak', 'unitypackage', 'prefab', 'asset', 'meta'
|
|
)
|
|
|
|
function Test-IsBinaryExtension {
|
|
param([string]$extension)
|
|
return $binaryExtensions -contains $extension
|
|
}
|
|
|
|
function Get-FileSize {
|
|
param([string]$path)
|
|
try {
|
|
$file = Get-Item $path -ErrorAction SilentlyContinue
|
|
if ($file) {
|
|
if ($file.Length -gt 1MB) {
|
|
return "{0:N2} MB" -f ($file.Length / 1MB)
|
|
} elseif ($file.Length -gt 1KB) {
|
|
return "{0:N2} KB" -f ($file.Length / 1KB)
|
|
} else {
|
|
return "$($file.Length) B"
|
|
}
|
|
}
|
|
} catch {
|
|
return "Unknown"
|
|
}
|
|
return "Unknown"
|
|
}
|
|
|
|
# Get absolute path
|
|
$absPath = Resolve-Path $RepoPath
|
|
Write-Host "Scanning repository for untracked binaries: $absPath" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
|
|
# Get list of ALL files tracked by Git
|
|
Write-Host "Getting files tracked by Git..." -ForegroundColor Yellow
|
|
$allGitFiles = git ls-files 2>$null
|
|
|
|
# Get list of files tracked by LFS
|
|
Write-Host "Getting files tracked by LFS..." -ForegroundColor Yellow
|
|
$lfsFiles = git lfs ls-files 2>$null | ForEach-Object {
|
|
# Parse the LFS output format: <oid> * <filename> or <oid> - <filename>
|
|
if ($_ -match '^[a-f0-9]+\s+[-*]\s+(.+)$') {
|
|
$matches[1]
|
|
}
|
|
}
|
|
|
|
# Create a hashtable for fast lookup of LFS files
|
|
$lfsFileLookup = @{}
|
|
foreach ($file in $lfsFiles) {
|
|
$lfsFileLookup[$file] = $true
|
|
}
|
|
|
|
Write-Host "Total Git-tracked files: $($allGitFiles.Count)" -ForegroundColor Gray
|
|
Write-Host "Total LFS-tracked files: $($lfsFiles.Count)" -ForegroundColor Gray
|
|
Write-Host ""
|
|
|
|
# Find binary files that are NOT in LFS
|
|
$untrackedBinaries = @()
|
|
$extensionSummary = @{}
|
|
|
|
foreach ($file in $allGitFiles) {
|
|
# Skip if empty
|
|
if ([string]::IsNullOrWhiteSpace($file)) { continue }
|
|
|
|
# Skip if already in LFS
|
|
if ($lfsFileLookup.ContainsKey($file)) { continue }
|
|
|
|
# Get extension (with error handling for weird filenames)
|
|
try {
|
|
$extension = [System.IO.Path]::GetExtension($file).TrimStart('.')
|
|
} catch {
|
|
continue # Skip files with problematic names
|
|
}
|
|
|
|
# Check if it's a known binary extension
|
|
if (Test-IsBinaryExtension -extension $extension) {
|
|
$fullPath = Join-Path $absPath $file
|
|
$fileSize = Get-FileSize -path $fullPath
|
|
|
|
$untrackedBinaries += [PSCustomObject]@{
|
|
File = $file
|
|
Extension = $extension
|
|
Size = $fileSize
|
|
}
|
|
|
|
# Track summary
|
|
if (-not $extensionSummary.ContainsKey($extension)) {
|
|
$extensionSummary[$extension] = 0
|
|
}
|
|
$extensionSummary[$extension]++
|
|
}
|
|
}
|
|
|
|
# Display results
|
|
if ($untrackedBinaries.Count -eq 0) {
|
|
Write-Host "No untracked binary files found!" -ForegroundColor Green
|
|
Write-Host "All binary files are properly tracked by Git LFS." -ForegroundColor Green
|
|
} else {
|
|
Write-Host "Found $($untrackedBinaries.Count) binary files tracked by Git (not LFS):" -ForegroundColor Red
|
|
Write-Host ""
|
|
|
|
# Group by extension for display
|
|
$groupedByExt = $untrackedBinaries | Group-Object -Property Extension | Sort-Object Count -Descending
|
|
|
|
foreach ($group in $groupedByExt) {
|
|
$groupName = $group.Name
|
|
$groupCount = $group.Count
|
|
Write-Host " .$groupName ($groupCount files):" -ForegroundColor Yellow
|
|
foreach ($item in $group.Group | Select-Object -First 10) {
|
|
Write-Host " - $($item.File) ($($item.Size))" -ForegroundColor Gray
|
|
}
|
|
if ($groupCount -gt 10) {
|
|
Write-Host " ... and $($groupCount - 10) more" -ForegroundColor DarkGray
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "========================================" -ForegroundColor Red
|
|
Write-Host "SUGGESTED .gitattributes ADDITIONS:" -ForegroundColor Cyan
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
|
|
# Generate suggested gitattributes entries
|
|
$sortedExts = $extensionSummary.GetEnumerator() | Sort-Object Value -Descending
|
|
foreach ($ext in $sortedExts) {
|
|
$extName = $ext.Key
|
|
Write-Host "*.$extName filter=lfs diff=lfs merge=lfs -text" -ForegroundColor White
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "To fix these files, add the above lines to your .gitattributes file," -ForegroundColor Yellow
|
|
Write-Host "then run: git add --renormalize ." -ForegroundColor Yellow
|
|
}
|
|
|
|
Write-Host ""
|
|
|
|
# Optional: Show full LFS status
|
|
if ($ShowLFSStatus) {
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
Write-Host "Current LFS Status:" -ForegroundColor Cyan
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
git lfs status 2>&1 | ForEach-Object { Write-Host $_ }
|
|
}
|