Functions/GenXdev.Git/PermanentlyDeleteGitFolders.ps1
################################################################################ <# .SYNOPSIS Permanently deletes specified folders from all branches in a Git repository. .DESCRIPTION Clones a repository, removes specified folders from all branches and commits, then force pushes the changes back to origin. This permanently removes the folders from Git history. .PARAMETER RepoUri The URI of the Git repository to clean. .PARAMETER Folders Array of folder paths to permanently remove from the repository history. .EXAMPLE PermanentlyDeleteGitFolders -RepoUri "https://github.com/user/repo.git" ` -Folders "bin", "obj" #> function PermanentlyDeleteGitFolders { [CmdletBinding()] param( ####################################################################### [parameter( Position = 0, Mandatory = $true, HelpMessage = "The URI of the Git repository to clean" )] [string] $RepoUri, ####################################################################### [parameter( Position = 1, Mandatory = $true, HelpMessage = "Array of folder paths to permanently remove" )] [string[]] $Folders ####################################################################### ) begin { # create a unique temporary directory using utc ticks $tempPath = "$Env:TEMP\$([datetime]::UtcNow.Ticks)" Write-Verbose "Using temp directory: $tempPath" # ensure temp directory exists [System.IO.Directory]::CreateDirectory($tempPath) # store current location to restore later Push-Location } process { try { # change to temp directory Set-Location $tempPath Write-Verbose "Changed to temp directory" # clone the repository Write-Verbose "Cloning repository: $RepoUri" $null = git clone $RepoUri repo # change to repo directory Set-Location repo Write-Verbose "Changed to repository directory" # create tracking branches for all remote branches except HEAD Write-Verbose "Creating tracking branches" git branch -r | ForEach-Object { if (-not $PSItem.Contains("/HEAD")) { $null = git checkout --track $PSItem.Trim() } # process each folder to remove foreach ($folder in $Folders) { # normalize folder path to use forward slashes $folderFixed = $folder.replace("\", "/") if ($folderFixed.endswith("/")) { $folderFixed = $folderFixed.Substring(0, $folderFixed.Length - 1) } # remove folder from git history Write-Verbose "Removing $folderFixed from history" git "filter-branch" "--index-filter" ` "'git rm -rf --cached --ignore-unmatch $folderFixed/'" ` "--prune-empty" "--tag-name-filter" "cat" "--" "--all" # clean up refs git "for-each-ref" "--format=`"%(refname)`"" "refs/original/" | Select-Object -First 1 | ForEach-Object { git update-ref -d } } try { # remove old refs and logs Get-ChildItem @(".git/logs", ".git/refs/original") ` -ErrorAction SilentlyContinue -Directory | ForEach-Object -ErrorAction SilentlyContinue { Remove-Item -LiteralPath $PSItem.FullName -Force -Recurse ` -ErrorAction SilentlyContinue } } catch { Write-Verbose "Error cleaning up refs (non-critical): $_" } # garbage collect to remove unreferenced commits Write-Verbose "Running garbage collection" $null = git gc "--prune=all" "--aggressive" # force push changes to remote Write-Verbose "Force pushing changes to remote" $null = git push origin "--all" "--force" $null = git push origin "--tags" "--force" } } finally { # restore original location Pop-Location Write-Verbose "Restored original location" } } end { } } |