Skip to content

Instantly share code, notes, and snippets.

@perXautomatik
Forked from mhagger/README.md
Last active August 7, 2023 10:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save perXautomatik/42bb0a6d1bdd0adf75ad5638905268fd to your computer and use it in GitHub Desktop.
Save perXautomatik/42bb0a6d1bdd0adf75ad5638905268fd to your computer and use it in GitHub Desktop.
Tools for repository repair

Ideas for git-fix project

We would like to have a way to fix up repositories' history; e.g., to remove corruption that may have happened some time in the past.

The standard tool for mass-rewriting of Git history is git filter-branch. But it is awkward to use and has a number of limitations:

  • It is a shell script, and correspondingly slow
  • It cannot deal with some kinds of corruption, because it tries to check out all historical revisions into the index and/or working tree
  • It can make "grafts" and "replace references" permanent, but only at the commit level. It cannot make "replace references" for trees or blobs permanent.

Fixing repository corruption has three steps: detection and characterization of the corruption, creation of replacement objects, and history rewriting to make the repairs permanent. Here is some brainstorming about how each of these stages could be improved:

  1. Detection of problems
    • Improve git fsck output (or maybe build this into another program, like git rev-list --objects).
      • Support a traversal-based check, whose range can be defined (ideally, also at object granularity).
      • Display the paths that led to broken objects.
    • Add new validity tests (to the extent that they don't already exist):
      • Prohibit empty filenames in trees.
      • Require filenames in trees to be distinct and sorted correctly.
      • Optionally require tree filenames to be unique even modulo Unicode normalization.
      • Optionally require tree filenames to be valid UTF-8 and normalized in form NFC.
    • git fsck --interactive: allow objects to be fixed while git fsck is running, to avoid having to restart all of the time (see below).
  2. Creation of replacement objects
    • Make git hash-object stricter by default. It should run all of the object tests that git fsck would usually run. Add a --no-strict mode for backwards compatibility.
    • Add git hash-object --overwrite command, which writes a new loose object regardless of whether an object with that name already exists. (This could be used for replacing corrupt objects.)
    • Add git hash-object --batch, for efficiency. Its input should be similar to the output of git cat-file --batch.
    • Use git ls-tree -z and git mktree -z for trees instead of git cat-file and git hash-object.
    • Add git ls-tree --batch, for efficiency.
  3. git-fix: Rewrite history, incorporating replace objects
    • Allow fixing all of the history reachable from a single tip
    • Allow references to be fixed
    • Handle fixing of only part of the history (e.g., via <rev-list options> like those of git filter-branch).
    • Handle tags. Replace signed tags with unsigned tags where necessary.

git fsck --interactive

Another idea was to implement git fsck --interactive, in which every time it found a problem, it would report the bad object in some machine-readable format, and wait for commands on stdin. The commands could be things like

replace SP <SHA-1> LF

to use as a replacement for the bad object (fsck would create a replace record but not do healing up the tree).

add SP <type> SP <size> LF
<contents> LF

to add a new object to the DB.

replace SP <type> SP <size> LF
<contents> LF

to add a new object to the DB and use it as a replacement for the bad object.

continue LF

to just go on without repairing the broken object.

The point of all this would be to avoid the "fsck; fix reported problem; repeat" loop that is so expensive when there is a chain of bad objects.

begin
{
Push-Location
# Validate the arguments
if (-not (Test-Path -LiteralPath $modules)) {
Write-Error "Invalid modules path: $modules"
exit 1
}
if (-not (Test-Path -LiteralPath $folder)) {
Write-Error "Invalid folder path: $folder"
exit 1
}
# Redirect the standard error output of git commands to the standard output stream
$env:GIT_REDIRECT_STDERR = '2>&1'
# Initialize a counter variable
$i = 0;
# Define parameters for Write-Progress cmdlet
$progressParams = @{
Activity = "Processing files"
Status = "Starting"
PercentComplete = 0
}
}
process {
# Get all the subdirectories in $folder, excluding any .git files or folders
$subdirs = Get-ChildItem -LiteralPath $folder -Directory -Recurse -Filter "*"
# Loop through each subdirectory
foreach ($subdir in $subdirs) {
# Increment the counter
$i++;
# Change the current directory to the subdirectory
Set-Location $subdir.FullName
# Run git status and capture the output
$output = git status
# Check if the output is fatal
if($output -like "fatal*")
{
# Print a message indicating fatal status
Write-Output "fatal status for $subdir"
# Get the .git file or folder in that subdirectory
$gitFile = Get-ChildItem -LiteralPath "$subdir\*" -Force | Where-Object { $_.Name -eq ".git" }
# Check if it is a file or a folder
if( $gitFile -is [System.IO.FileInfo] )
{
# Define parameters for Move-Item cmdlet
$moveParams = @{
Path = Join-Path -Path $modules -ChildPath $gitFile.Directory.Name
Destination = $gitFile
Force = $true
PassThru = $true
}
# Move the module folder to replace the .git file and return the moved item
$movedItem = Move-Item @moveParams
# Print a message indicating successful move
Write-Output "moved $($movedItem.Name) to $($movedItem.DirectoryName)"
}
elseif( $gitFile -is [System.IO.DirectoryInfo] )
{
# Get the path to the git config file
$configFile = Join-Path -Path $gitFile -ChildPath "\config"
# Check if it exists
if (-not (Test-Path -LiteralPath $configFile)) {
Write-Error "Invalid folder path: $gitFile"
}
else
{
# Read the config file content as a single string
$configContent = Get-Content -LiteralPath $configFile -Raw
# Remove any line that contains worktree and store the new content in a variable
$newConfigContent = $configContent -Replace "(?m)^.*worktree.*$\r?\n?"
# Check if there are any lines to remove
if ($configContent -ne $newConfigContent)
{
# Write the new config file content
$newConfigContent | Set-Content -LiteralPath $configFile -Force
# Print a message indicating successful removal
Write-Output "removed worktree from $configFile"
}
}
}
else
{
# Print an error message if it is not a file or a folder
Write-Error "not a .git file or folder: $gitFile"
}
}
# Calculate the percentage of directories processed
$percentComplete = ($i / ($subdirs.count+$i) ) * 100
# Update the progress bar
$progressParams.PercentComplete = $percentComplete
Write-Progress @progressParams
}
}
end {
# Restore the original location
Pop-Location
# Complete the progress bar
$progressParams.Status = "Finished"
Write-Progress @progressParams
}
# A function to validate the arguments
function Validate-Arguments ($modules, $folder) {
if (-not (Test-Path $modules)) {
Write-Error "Invalid modules path: $modules"
exit 1
}
if (-not (Test-Path $folder)) {
Write-Error "Invalid folder path: $folder"
exit 1
}
}
# A function to check the git status of a folder
function Check-GitStatus ($folder) {
# Change the current directory to the folder
Set-Location $folder.FullName
Write-Output "checking $folder"
if ((Get-ChildItem -force | ?{ $_.name -eq ".git" } ))
{
# Run git status and capture the output
$output = git status
if(($output -like "fatal*"))
{
Write-Output "fatal status for $folder"
Repair-GitFolder $folder
}
else
{
Write-Output @($output)[0]
}
}
else
{
Write-Output "$folder not yet initialized"
}
}
# A function to repair a corrupted git folder
function Repair-GitFolder ($folder) {
$folder | Get-ChildItem -force | ?{ $_.name -eq ".git" } | % {
$toRepair = $_
if( $toRepair -is [System.IO.FileInfo] )
{
Move-GitFile $toRepair
}
elseif( $toRepair -is [System.IO.DirectoryInfo] )
{
Fix-GitConfig $toRepair
}
else
{
Write-Error "not a .git file or folder: $toRepair"
}
}
}
# A function to move a .git file to the corresponding module folder
function Move-GitFile ($file) {
global:$modules | Get-ChildItem -Directory | ?{ $_.name -eq $file.Directory.Name } | select -First 1 | % {
# Move the folder to the target folder
rm $file -force ; Move-Item -Path $_.fullname -Destination $file -force
}
}
# A function to fix the worktree setting in a .git config file
function Fix-GitConfig ($folder) {
# Get the path to the git config file
$configFile = Join-Path -Path $folder -ChildPath "\config"
if (-not (Test-Path $configFile)) {
Write-Error "Invalid folder path: $folder"
}
else
{
# Read the config file content as an array of lines
$configLines = Get-Content -Path $configFile
# Filter out the lines that contain worktree
$newConfigLines = $configLines | Where-Object { $_ -notmatch "worktree" }
if (($configLines | Where-Object { $_ -match "worktree" }))
{
# Write the new config file content
Set-Content -Path $configFile -Value $newConfigLines -Force
}
}
}
# The main function that calls the other functions
function fix-CorruptedGitModules ($folder = "C:\ProgramData\scoop\persist", global:$modules = "C:\ProgramData\scoop\persist\.git\modules")
{
begin {
Push-Location
# Validate the arguments
Validate-Arguments $modules $folder
# Set the environment variable for git error redirection
$env:GIT_REDIRECT_STDERR = '2>&1'
}
process {
# Get the list of folders in $folder
$folders = Get-ChildItem -Path $folder -Directory
# Loop through each folder and check the git status
foreach ($f in $folders) {
Check-GitStatus $f
}
}
end {
Pop-Location
}
}

# Define a function that moves the module folder to the repository path, replacing the .git file
function Move-ModuleFolder {
param (
[System.IO.FileInfo]$GitFile,
[string]$ModulesPath
)
# Get the corresponding module folder from the modules path
$moduleFolder = Get-ChildItem -Path $ModulesPath -Directory | Where-Object { $_.Name -eq $GitFile.Directory.Name } | Select-Object -First 1
# Move the module folder to the repository path, replacing the .git file
Remove-Item -Path $GitFile -Force
Move-Item -Path $moduleFolder.FullName -Destination $GitFile -Force
}
# Define a function that removes the worktree lines from the git config file
function Remove-WorktreeLines {
param (
[System.IO.DirectoryInfo]$GitFolder
)
# Get the path to the git config file
$configFile = Join-Path -Path $GitFolder -ChildPath "\config"
# Check if the config file exists
if (-not (Test-Path $configFile)) {
Write-Error "Invalid folder path: $GitFolder"
}
else {
# Read the config file content as an array of lines
$configLines = Get-Content -Path $configFile
# Filter out the lines that contain worktree, which is a setting that can cause problems with scoop
$newConfigLines = $configLines | Where-Object { $_ -notmatch "worktree" }
# Check if there are any lines that contain worktree
if ($configLines | Where-Object { $_ -match "worktree" }) {
# Write the new config file content, removing the worktree lines
Set-Content -Path $configFile -Value $newConfigLines -Force
}
}
}
# Define a function that checks the status of a git repository and repairs it if needed
function Repair-ScoopGitRepository {
param (
[string]$RepositoryPath,
[string]$ModulesPath
)
# Change the current directory to the repository path
Set-Location $RepositoryPath
# Run git status and capture the output
$output = git status
# Check if the output is fatal, meaning the repository is corrupted
if ($output -like "fatal*") {
Write-Output "fatal status for $RepositoryPath"
# Get the .git file or folder in the repository path
$toRepair = Get-ChildItem -Path $RepositoryPath -Force | Where-Object { $_.Name -eq ".git" }
# Check if the .git item is a file
if ($toRepair -is [System.IO.FileInfo]) {
Move-ModuleFolder -GitFile $toRepair -ModulesPath $ModulesPath
}
else {
Write-Error "not a .git file: $toRepair"
}
# Check if the .git item is a folder
if ($toRepair -is [System.IO.DirectoryInfo]) {
Remove-WorktreeLines -GitFolder $toRepair
}
else {
Write-Error "not a .git folder: $toRepair"
}
}
else {
Write-Output @($output)[0]
}
}
# Define a function that validates the paths, sets the error redirection, and repairs the git repositories in the given folder
function Repair-ScoopGit {
param (
# Validate that the modules path exists
[ValidateScript({Test-Path $_})]
[string]$ModulesPath,
# Validate that the folder path exists
[ValidateScript({Test-Path $_})]
[string]$FolderPath
)
# Redirect the standard error output of git commands to the standard output stream
$env:GIT_REDIRECT_STDERR = '2>&1'
# Get the list of subfolders in the folder path
$subfolders = Get-ChildItem -Path $FolderPath -Directory
# Loop through each subfolder and repair its git repository
foreach ($subfolder in $subfolders) {
Write-Output "checking $subfolder"
# Check if the subfolder has a .git file or folder
if (Get-ChildItem -Path $subfolder.FullName -Force | Where-Object { $_.Name -eq ".git" }) {
Repair-ScoopGitRepository -RepositoryPath $subfolder.FullName -ModulesPath $ModulesPath
}
else {
Write-Output "$subfolder not yet initialized"
}
}
}
# Call the main function with the modules and folder paths as arguments
Initialize-ScoopGitRepair -ModulesPath "C:\ProgramData\scoop\persist\.git\modules" -FolderPath "C:\ProgramData\scoop\persist"
Repair-ScoopGitRepositories -FolderPath "C:\ProgramData\scoop\persist" -ModulesPath "C:\ProgramData\scoop\persist\.git\modules"
<#
========================================================================================================================
Name : <Name>.ps1
Description : This script ............................
Created Date : %Date%
Created By : %UserName%
Dependencies : 1) Windows PowerShell 5.1
2) .................
Revision History
Date Release Change By Description
%Date% 1.0 %UserName% Initial Release
========================================================================================================================
#>
# Define a function to check if a .git file exists in a directory
<#
.SYNOPSIS
Checks if a .git file exists in a directory and returns true or false.
.PARAMETER Directory
The directory path to check.
.EXAMPLE
Test-GitFile -Directory "B:\PF\NoteTakingProjectFolder"
Output: True
#>
function Test-GitFile {
param(
# The directory path to check
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Directory
)
# Get the full path of the .git file in the directory
$gitFile = Join-Path $Directory ".git"
# Test if the .git file exists and return the result
return Test-Path $gitFile
}
# Define a function to get the name of the work folder from a directory path
<#
.SYNOPSIS
Gets the name of the work folder from a directory path by removing the drive letter and any trailing backslashes.
.PARAMETER Directory
The directory path to get the name from.
.EXAMPLE
Get-WorkFolderName -Directory "B:\PF\NoteTakingProjectFolder"
Output: PF\NoteTakingProjectFolder
#>
function Get-WorkFolderName {
param(
# The directory path to get the name from
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Directory
)
# Remove the drive letter and any trailing backslashes from the directory path and return the result
return $Directory.TrimStart("B:\").TrimEnd("\")
}
# Define a function to search with void tools everything for the folder name and "child:config" and get the first result
<#
.SYNOPSIS
Searches with void tools everything for the folder name and "child:config" and returns the first result as an object with properties Name, Path, and FullPath.
.PARAMETER FolderName
The folder name to search with.
.EXAMPLE
Search-Everything -FolderName "PF\NoteTakingProjectFolder"
Output: @{Name=config; Path=B:\PF\NoteTakingProjectFolder\.git\modules\NoteTakingProjectFolder; FullPath=B:\PF\NoteTakingProjectFolder\.git\modules\NoteTakingProjectFolder\config}
#>
function Ensure-Everything {
param(
# The folder name to search with
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$folderPath,
# The filter to apply to the search
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$filter
)
# Install and import the pseverything module if not already loaded
if (-not (Get-Module -Name pseverything)) {
Install-Module pseverything -Scope CurrentUser -Force
Import-Module pseverything
}
$folderPath = $folderPath.trim("\\")
$filter = """" + ($folderPath | Split-Path -Leaf) + """" + " " + $filter
# Use Everything to find all folders in the folder path that match the filter
$results = Search-Everything -Filter $filter -global
# If there are any results, then return the first one as an object with properties Name, Path, and FullPath
if ($results) {
$firstResult = $results
return [PSCustomObject]@{
Path = $firstResult
}
}
else {
# Throw an error if no results are found
throw "No results found for folder path '$folderPath' and filter '$filter'"
}
}
# Define a function to overwrite git file content with "gitdir:" followed by a path
<#
.SYNOPSIS
Overwrites git file content with "gitdir:" followed by a path.
.PARAMETER GitFile
The git file path to overwrite.
.PARAMETER Path
The path to append after "gitdir:".
.EXAMPLE
Overwrite-GitFile -GitFile "B:\PF\NoteTakingProjectFolder\.git" -Path "B:\PF\NoteTakingProjectFolder\.git\modules\NoteTakingProjectFolder"
Output: The content of B:\PF\NoteTakingProjectFolder\.git is overwritten with "gitdir: B:\PF\NoteTakingProjectFolder\.git\modules\NoteTakingProjectFolder"
#>
function Overwrite-GitFile {
param(
# The git file path to overwrite
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$GitFile,
# The path to append after "gitdir:"
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Path
)
# Create a new content string with "gitdir:" followed by the path
$newContent = "gitdir: $Path"
# Overwrite the git file content with the new content string using Set-Content cmdlet
Set-Content -Path $GitFile -Value $newContent -Force
}
function FIX {
param (
$directory = "B:\PF\chris\autohotkey\script\fork\MACRO RECORDER"
)
# Get the directory path from the user input and store it in a variable
# Check if a .git file exists in the directory using Test-GitFile function and store the result in a variable
$gitFileExists = Test-GitFile -Directory $directory
# If the result is true, then proceed with the rest of the script
if ($gitFileExists) {
# Get the name of the work folder from the directory path using Get-WorkFolderName function and store it in a variable
$workFolderName = Get-WorkFolderName -Directory $directory
# Search with void tools everything for the folder name and "child:config" and get the first result using Search-Everything function and store it in a variable
$firstResult = Ensure-Everything -folderPath $workFolderName -filter 'child:config count:1'
# If there is a first result, then proceed with the rest of the script
if ($firstResult) {
# Get the full path of the .git file in the directory and store it in a variable
$gitFile = Join-Path $directory ".git"
# Get the path property of the first result and store it in a variable
$path = $firstResult.Path
# Overwrite git file content with "gitdir:" followed by the path using Overwrite-GitFile function
Overwrite-GitFile -GitFile $gitFile -Path $path
}
}
}
$patj = "B:\PF\chris\autohotkey\script\fork\"
$q = Get-ChildItem -Path $patj
$q | % {
cd $_.FullName -PassThru
$t = invoke-expression "git status 2>&1"
if($t -like "fatal: not a git repository:*" )
{fix -directory $_.FullName}
}
# \fix-CorruptedGitModules.ps1
<#
This code is a PowerShell script that checks the status of git repositories in a given folder and repairs
them if they are corrupted. It does the following steps:
It defines a begin block that runs once before processing any input. In this block, it sets some variables
for the modules and folder paths, validates them, and redirects the standard error output of git commands
to the standard output stream.
It defines a process block that runs for each input object. In this block, it loops through each subfolder
in the folder path and runs git status on it. If the output is fatal, it means the repository is corrupted
and needs to be repaired. To do that, it moves the corresponding module folder from the modules path to the
subfolder, replacing the existing .git file or folder. Then, it reads the config file of the repository and
removes any line that contains worktree, which is a setting that can cause problems with scoop. It prints
the output of each step to the console.
It defines an end block that runs once after processing all input. In this block, it restores the original
location of the script.#>
. $PSScriptRoot\Invoke-Git.ps1
. $PSScriptRoot\Split-TextByRegex.ps1
. $PSScriptRoot\git-GetSubmodulePathsUrls.ps1
. $PSScriptRoot\config-to-gitmodules.ps1
# \fix-CorruptedGitModulesCombinedWithQue.ps1
function Validate-Path {
param (
[Parameter(Mandatory=$true)]
[string]$Path
)
if (-not (Test-Path $Path)) {
Write-Error "Invalid path: $Path"
exit 1
}
}
# \GetWorktreeSubmodules.ps1
# Define a function to convert key-value pairs to custom objects
# Define a function to get the URL of a submodule
function byPath-RepoUrl {
param(
[string]$Path # The path of the submodule directory
)
# Change the current location to the submodule directory
Push-Location -Path $Path -ErrorAction Stop
# Get the URL of the origin remote
$url = invoke-git "config remote.origin.url" -ErrorAction Stop
# Parse the URL to get the part after the colon
$parsedUrl = ($url -split ':')[1]
# Return to the previous location
Pop-Location -ErrorAction Stop
# Return the parsed URL as output
return $parsedUrl
}
function get-gitUnhide ($Path)
{
Get-ChildItem -Path "$Path\*" -Force | Where-Object { $_.Name -eq ".git" }
}
# requries gitmodulesfile
# \GitSyncSubmoduleWithConfig.ps1
<#
.SYNOPSIS
Synchronizes the submodules with the config file.
.DESCRIPTION
This function synchronizes the submodules with the config file, using the Git-helper and ini-helper modules. The function checks the remote URLs of the submodules and updates them if they are empty or local paths. The function also handles conflicts and errors.
.PARAMETER GitDirPath
The path of the git directory where the config file is located.
.PARAMETER GitRootPath
The path of the git root directory where the submodules are located.
.PARAMETER FlagConfigDecides
A switch parameter that indicates whether to use the config file as the source of truth in case of conflicting URLs.
#>
# A function to move a .git Folder into the current directory and remove any gitfiles present
function unearthiffind ()
{
param (
[Parameter(Mandatory=$true)]
[string]$toRepair,
[Parameter(Mandatory=$true)]
[string]$Modules
)
# Get the module folder that matches the name of the parent directory
Get-ChildItem -Path $Modules -Directory | Where-Object { $_.Name -eq $toRepair.Directory.Name } | Select-Object -First 1 | % {
# Move the module folder to replace the .git file
Remove-Item -Path $toRepair -Force
Move-Item -Path $_.FullName -Destination $toRepair -Force
}
}
# A function to check the git status of a folder
function Check-GitStatus ($folder) {
# Change the current directory to the folder
Set-Location $folder.FullName
Write-Output "checking $folder"
if ((Get-ChildItem -force | ?{ $_.name -eq ".git" } ))
{
# Run git status and capture the output
$output = Invoke-Git "git status"
if(($output -like "fatal*"))
{
Write-Output "fatal status for $folder"
#UnabosrbeOrRmWorktree $folder
}
else
{
Write-Output @($output)[0]
}
}
else
{
Write-Output "$folder not yet initialized"
}
}
# Define a function to remove the worktree from a config file
function Remove-Worktree {
param(
[string]$ConfigPath # The path of the config file
)
if(Test-Package "Get-IniContent")
{
# Get the content of the config file as an ini object
$iniContent = Get-IniContent -FilePath $ConfigPath
# Remove the worktree property from the core section
$iniContent.core.Remove("worktree")
# Write the ini object back to the config file
$iniContent | Out-IniFile -FilePath $ConfigPath -Force
}
else
{
# Read the config file content as an array of lines
$configLines = Get-Content -Path $ConfigPath
# Filter out the lines that contain worktree
$newConfigLines = $configLines | Where-Object { $_ -notmatch "worktree" }
if (($configLines | Where-Object { $_ -match "worktree" }))
{
# Write the new config file content
Set-Content -Path $ConfigPath -Value $newConfigLines -Force
}
}
}
function Remove-WorktreeHere {
param(
[string]$ConfigPath, # The path of the config file
[alias]$folder,$toRepair
)
# Get the path to the git config file
$configFile = Join-Path -Path $toRepair -ChildPath "\config"
# Check if it exists
if (-not (Test-Path $configFile)) {
Write-Error "Invalid folder path: $toRepair"
}
else
{
Remove-Worktree -ConfigPath $toRepair
}
}
# A function to repair a corrupted git folder
# param folder: where to look for replacement module to unearth with
function UnabosrbeOrRmWorktree ($folder) {
get-gitUnhide $folder | % {
if( $_ -is [System.IO.FileInfo] )
{
unearthIffind $_ $folder
}
elseif( $_ -is [System.IO.DirectoryInfo] )
{
Remove-WorktreeHere $_
}
else
{
Write-Error "not a .git file or folder: $_"
}
}
}
# A function to repair a fatal git status
function UnabosrbeOrRmWorktree {
param (
[Parameter(Mandatory=$true)]
[string]$Path,
[Parameter(Mandatory=$true)]
[string]$Modules
)
# Print a message indicating fatal status
write-verbos "fatal status for $Path, atempting repair"
cd $Path
UnabosrbeOrRmWorktree -folder $Modules
}
function get-Origin
{
# Get the remote url of the git repository
$ref = (git remote get-url origin)
# Write some information to the console
Write-Verbos '************************** ref *****************************'
Write-Verbos $ref.ToString()
Write-Verbos '************************** ref *****************************'
return $ref
}
function get-Relative {
param (
$path
,$targetFolder
)
Set-Location $path
$gitRoot = Get-GitRoot
# Get the relative path of the target folder from the root of the git repository
return (Resolve-Path -Path $targetFolder.FullName -Relative).TrimStart('.\').Replace('\', '/')
# Write some information to the console
Write-Verbos '******************************* bout to read as submodule ****************************************'
Write-Verbos $relative.ToString()
Write-Verbos $ref.ToString()
Write-Verbos '****************************** relative path ****************************************************'
}
# Define a function to get the root of the git repository
function Get-GitRoot {
(git rev-parse --show-toplevel)
}
function git-root {
$gitrootdir = (git rev-parse --show-toplevel)
if ($gitrootdir) {
Set-Location $gitrootdir
}
}
# Define a function to move a folder to a new destination
function Move-Folder {
param (
[Parameter(Mandatory=$true)][string]$Source,
[ValidateScript({Test-Path $_})]
# Check if the destination already exists
[Parameter(Mandatory=$true, HelpMessage="Enter A empty path to move to")]
[ValidateScript({!(Test-Path $_)})]
[string]$Destination
)
try {
Move-Item -Path $Source -Destination $Destination -ErrorAction Stop
Write-Verbos "Moved $Source to $Destination"
}
catch {
Write-Warning "Failed to move $Source to $Destination"
Write-Warning $_.Exception.Message
}
}
# Define a function to add and absorb a submodule
function Add-AbsorbSubmodule {
param (
[Parameter(Mandatory=$true)]
[string]$Ref,
[Parameter(Mandatory=$true)]
[string]$Relative
)
try {
Git submodule add $Ref $Relative
git commit -m "as submodule $Relative"
Git submodule absorbgitdirs $Relative
Write-Verbos "Added and absorbed submodule $Relative"
}
catch {
Write-Warning "Failed to add and absorb submodule $Relative"
Write-Warning $_.Exception.Message
}
}
function index-Remove ($name,$path)
{
try {
# Change to the parent path and forget about the files in the target folder
Set-Location $path
# Check if the files in the target folder are already ignored by git
if ((git ls-files --error-unmatch --others --exclude-standard --directory --no-empty-directory -- "$name") -eq "") {
Write-Warning "The files in $name are already ignored by git"
}
else {
git rm -r --cached $name
git commit -m "forgot about $name"
}
}
catch {
Write-Warning "Failed to forget about files in $name"
Write-Warning $_.Exception.Message
}
}
function about-Repo()
{
$vb = ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true)
# Write some information to the console
Write-Verbos '************************************************************' -Verbose: $vb
Write-Verbos $targetFolder.ToString() -Verbose: $vb
Write-Verbos $name.ToString() -Verbose: $vb
Write-Verbos $path.ToString() -Verbose: $vb
Write-Verbos $configFile.ToString() -Verbose: $vb
Write-Verbos '************************************************************'-Verbose: $vb
}
<#
.SYNOPSIS
Gets the paths of all submodules in a git repository.
.DESCRIPTION
Gets the paths of all submodules in a git repository by parsing the output of git ls-files --stage.
.OUTPUTS
System.String[]
#>
function Get-SubmodulePaths {
# run git ls-files --stage and filter by mode 160000
git ls-files --stage | Select-String -Pattern "^160000"
# loop through each line of output
foreach ($Line in $Input) {
# split the line by whitespace and get the last element as the path
$Line -split "\s+" | Select-Object -Last 1
}
}
<#
.SYNOPSIS
Gets the absolute path of the .git directory for a submodule.
.DESCRIPTION
Gets the absolute path of the .git directory for a submodule by reading the .git file and running git rev-parse --absolute-git-dir.
.PARAMETER Path
The path of the submodule.
.OUTPUTS
System.String
#>
function Get-GitDir {
param (
[Parameter(Mandatory)]
[string]$Path
)
# read the .git file and get the value after "gitdir: "
$GitFile = Get-Content -Path "$Path/.git"
$GitDir = $GitFile -replace "^gitdir: "
# run git rev-parse --absolute-git-dir to get the absolute path of the .git directory
git -C $Path rev-parse --absolute-git-dir | Select-Object -First 1
}
<#
.SYNOPSIS
Unsets the core.worktree configuration for a submodule.
.DESCRIPTION
Unsets the core.worktree configuration for a submodule by running git config --local --path --unset core.worktree.
.PARAMETER Path
The path of the submodule.
#>
function Unset-CoreWorktree {
param (
[Parameter(Mandatory)]
[string]$Path
)
# run git config --local --path --unset core.worktree for the submodule
git --work-tree=$Path --git-dir="$Path/.git" config --local --path --unset core.worktree
}
<#
.SYNOPSIS
Hides the .git directory on Windows.
.DESCRIPTION
Hides the .git directory on Windows by running attrib.exe +H /D.
.PARAMETER Path
The path of the submodule.
#>
function Hide-GitDir {
param (
[Parameter(Mandatory)]
[string]$Path
)
# check if attrib.exe is available on Windows
if (Get-Command attrib.exe) {
# run attrib.exe +H /D to hide the .git directory
MSYS2_ARG_CONV_EXCL="*" attrib.exe "+H" "/D" "$Path/.git"
}
}
<# .SYNOPSIS
Lists all the ignored files that are cached in the index.
.DESCRIPTION
This function uses git ls-files with the -c, --ignored and --exclude-standard options to list all the files that are ignored by
Use git ls-files with the -c, --ignored and --exclude-standard options
.gitignore or other exclude files, and also have their content cached in the index. #>
function Get-IgnoredFiles {
git ls-files -s --ignored --exclude-standard -c }
<# .SYNOPSIS
Removes files from the index and the working tree.
.DESCRIPTION
This function takes an array of file names as input and uses git rm to remove them from the index and the working tree.
Define a function that removes files from the index and the working tree
.PARAMETER
Files An array of file names to be removed. #>
function Remove-Files { param( # Accept an array of file names as input
[string[]]$Files )
#Use git rm with the file names as arguments
git rm $Files --ignore-unmatch }
<# .SYNOPSIS
Rewrites the history of the current branch by removing all the ignored files.
.DESCRIPTION
This function uses git filter-branch with the -f, --index-filter and --prune-empty options to rewrite the history of the current branch by removing all the ignored files from each revision, and also removing any empty commits that result from this operation. It does this by modifying only the index, not the working tree, of each revision.
#Define a function that rewrites the history of the current branch by removing all the ignored files
#Use git filter-branch with the -f, --index-filter and --prune-empty options#>
function Rewrite-History { # Call the Get-IgnoredFiles function and pipe the output to Remove-Files function
git filter-branch -f --index-filter {
Get-IgnoredFiles | Remove-Files
} --prune-empty }
#Call the Rewrite-History function
<# .SYNOPSIS
Lists all the ignored files that are cached in the index.
.DESCRIPTION
This function uses git ls-files with the -c, --ignored and --exclude-standard options to list all the files that are ignored by
Use git ls-files with the -c, --ignored and --exclude-standard options
.gitignore or other exclude files, and also have their content cached in the index. #>
function Get-IgnoredFiles {
git ls-files -s --ignored --exclude-standard -c }
<# .SYNOPSIS
Removes files from the index and the working tree.
.DESCRIPTION
This function takes an array of file names as input and uses git rm to remove them from the index and the working tree.
Define a function that removes files from the index and the working tree
.PARAMETER
Files An array of file names to be removed. #>
function Remove-Files { param( # Accept an array of file names as input
[string[]]$Files )
#Use git rm with the file names as arguments
git rm $Files --ignore-unmatch }
<# .SYNOPSIS
Rewrites the history of the current branch by removing all the ignored files.
.DESCRIPTION
This function uses git filter-branch with the -f, --index-filter and --prune-empty options to rewrite the history of the current branch by removing all the ignored files from each revision, and also removing any empty commits that result from this operation. It does this by modifying only the index, not the working tree, of each revision.
#Define a function that rewrites the history of the current branch by removing all the ignored files
#Use git filter-branch with the -f, --index-filter and --prune-empty options#>
function Rewrite-History { # Call the Get-IgnoredFiles function and pipe the output to Remove-Files function
git filter-branch -f --index-filter {
Get-IgnoredFiles | Remove-Files
} --prune-empty }
#Call the Rewrite-History function Rewrite-History
# A function that parses the output of git ls-tree command and returns a custom object with properties
function Parse-GitLsTreeOutput
{
[CmdletBinding()]
param(
# The script or file path to parse
[Parameter(Mandatory, ValueFromPipeline)]
[string[]]$LsTreeOutput
)
process {
# Extract the blob type from the output line
$blobType = $_.substring(7,4)
# Set the hash start position based on the blob type
$hashStartPos = 12
if ($blobType -ne 'blob') { $hashStartPos+=2 }
# Set the relative path start position based on the blob type
$relativePathStartPos = 53
if ($blobType -ne 'blob') { $relativePathStartPos+=2 }
# Create a custom object with properties for unknown, blob, hash and relative path
[pscustomobject]@{unknown=$_.substring(0,6);blob=$blobType; hash=$_.substring($hashStartPos,40);relativePath=$_.substring($relativePathStartPos)}
}
}
# A function that resolves the absolute path of a file from its relative path
function Resolve-AbsolutePath
{
param(
[Parameter(Mandatory)] [string]$RelativePath
)
# Escape the backslash character for regex matching
$backslash = [regex]::escape('\')
# Define a regex pattern for matching octal escape sequences in the relative path
$octalPattern = $backslash+'\d{3}'+$backslash+'\d{3}'
# Trim the double quotes from the relative path
$relativePath = $RelativePath.Trim('"')
# Try to resolve the relative path to an absolute path
$absolutePath = Resolve-Path $relativePath -ErrorAction SilentlyContinue
# If the absolute path is not found and the relative path contains octal escape sequences, try to resolve it with wildcard matching
if(!$absolutePath -and $relativePath -match ($octalPattern))
{
$absolutePath = Resolve-Path (($relativePath -split($octalPattern) ) -join('*'))
}
# Return the absolute path or null if not found
return $absolutePath
}
# A function that takes a collection of parsed git ls-tree output objects and adds more properties to them such as absolute path, file name and parent folder
function Add-MorePropertiesToGitLsTreeOutput
{
param(
[Parameter(Mandatory)]
[psobject[]]$GitLsTreeOutputObjects
)
# For each object in the collection, add more properties using calculated expressions
$GitLsTreeOutputObjects | Select-Object -Property *,@{Name = 'absolute'; Expression = {Resolve-AbsolutePath $_.relativePath}},@{Name = 'FileName'; Expression = {$path = $_.absolute;$filename = [System.IO.Path]::GetFileNameWithoutExtension("$path");if(!($filename)) { $filename = [System.IO.Path]::GetFileName("$path") };$filename}},@{Name = 'Parent'; Expression = {Split-Path -Path $_.relativePath}}
}
# A function that joins two collections of parsed git ls-tree output objects based on their file names and returns a custom object with properties for hash and absolute paths of both collections
function Join-GitLsTreeOutputCollectionsByFileName
{
param(
[Parameter(Mandatory)]
[psobject[]]$Collection1,
[Parameter(Mandatory)]
[psobject[]]$Collection2
)
# Define a delegate function that returns the file name of an object as the join key
$KeyDelegate = [System.Func[Object,string]] {$args[0].FileName}
# Define a delegate function that returns a custom object with properties for hash and absolute paths of both collections as the join result
$resultDelegate = [System.Func[Object,Object,Object]]{
param ($x,$y);
New-Object -TypeName PSObject -Property @{
Hash = $x.hash;
AbsoluteX = $x.absolute;
AbsoluteY = $y.absolute
}
}
# Use LINQ Join method to join the two collections by file name and return an array of custom objects as the result
$joinedDataset = [System.Linq.Enumerable]::Join( $Collection1, $Collection2, #tableReference
$KeyDelegate,$KeyDelegate, #onClause
$resultDelegate
)
$OutputArray = [System.Linq.Enumerable]::ToArray($joinedDataset)
return $OutputArray
}
# A function that creates a lookup table from a collection of parsed git ls-tree output objects based on their hash values
function Create-LookupTableByHash
{
param(
[Parameter(Mandatory)]
[psobject[]]$GitLsTreeOutputObjects
)
# Define a delegate function that returns the hash value of an object as the lookup key
$HashDelegate = [system.Func[Object,String]] { $args[0].hash }
# Define a delegate function that returns the object itself as the lookup element
$ElementDelegate = [system.Func[Object]] { $args[0] }
# Use LINQ ToLookup method to create a lookup table from the collection by hash value and return an array of lookup groups as the result
$lookup = [system.Linq.Enumerable]::ToLookup($GitLsTreeOutputObjects, $HashDelegate,$ElementDelegate)
return [Linq.Enumerable]::ToArray($lookup)
}
# This function takes an array of objects and splits it into smaller chunks of a given size
# It also executes a script block on each chunk if provided
function Split-Array
{
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)] [object[]] $InputObject,
[Parameter()] [scriptblock] $Process,
[Parameter()] [int] $ChunkSize
)
Begin { #run once
# Initialize an empty array to store the chunks
$cache = @();
# Initialize an index to keep track of the chunk size
$index = 0;
}
Process { #run each entry
if($cache.Length -eq $ChunkSize) {
# if the cache array is full, send it out to the pipe line
write-host '{' –NoNewline
write-host $cache –NoNewline
write-host '}'
# Then we add the current pipe line object to the cache array and reset the index
$cache = @($_);
$index = 1;
}
else {
# Otherwise, we append the current pipe line object to the cache array and increment the index
$cache += $_;
$index++;
}
}
End { #run once
# Here we check if there are any remaining objects in the cache array, if so, send them out to pipe line
if($cache) {
Write-Output ($cache );
}
}
}
# This function parses the output of git ls-tree and converts it into a custom object with properties
function Parse-LsTree
{
[CmdletBinding()]
param(
# The script or file path to parse
[Parameter(Mandatory, ValueFromPipeline)]
[string[]]$LsTree
)
process {
# Extract the blob type from the input string
$blobType = $_.substring(7,4)
# Set the starting positions of the hash and relative path based on the blob type
$hashStartPos = 12
$relativePathStartPos = 53
if ($blobType -ne 'blob')
{
$hashStartPos+=2
$relativePathStartPos+=2
}
# Create a custom object with properties for unknown, blob, hash and relative path
[pscustomobject]@{unkown=$_.substring(0,6);blob=$blobType; hash=$_.substring($hashStartPos,40);relativePath=$_.substring($relativePathStartPos)}
}
}
# This function lists the duplicate object hashes in a git repository using git ls-tree and Parse-LsTree functions
function List-Git-DuplicateHashes
{
param([string]$path)
# Save the current working directory
$current = $PWD
# Change to the given path
cd $path
# Use git ls-tree to list all the objects in the HEAD revision
git ls-tree -r HEAD |
# Parse the output using Parse-LsTree function
Parse-LsTree |
# Group the objects by hash and filter out the ones that have only one occurrence
Group-Object -Property hash |
? { $_.count -ne 1 } |
# Sort the groups by count in descending order
Sort-Object -Property count -Descending
# Change back to the original working directory
cd $current
}
# This function adds an index property to each object in an array using a counter variable
function Add-Index { #https://stackoverflow.com/questions/33718168/exclude-index-in-powershell
begin {
# Initialize the counter variable as -1
$i=-1
}
process {
if($_ -ne $null) {
# Increment the counter variable and add it as an index property to the input object
Add-Member Index (++$i) -InputObject $_ -PassThru
}
}
}
# This function displays the indexed groups of duplicate hashes in a clear format
function Show-Duplicates
{
[cmdletbinding()]
param(
[parameter(ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[object[]] $input
)
Clear-Host
Write-Host "================ k for keep all ================"
# Add an index property to each group using Add-Index function
$indexed = ( $input | %{$_.group} | Add-Index )
# Display the index and relative path of each group and store the output in a variable
$indexed | Tee-Object -variable re |
% {
$index = $_.index
$relativePath = $_.relativePath
Write-Host "$index $relativePath"
}
# Return the output variable
$re
}
# This function allows the user to choose which duplicate hashes to keep or delete
function Choose-Duplicates
{
[cmdletbinding()]
param(
[parameter(ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[object[]] $input
)
# Split the input array into smaller chunks using Split-Array function
$options = $input | %{$_.index} | Split-Array
# Prompt the user to choose from the alternatives and store the input in a variable
$selection = Read-Host "choose from the alternativs " ($input | measure-object).count
# If the user chooses to keep all, return nothing
if ($selection -eq 'k' ) {
return
}
else {
# Otherwise, filter out the objects that have the same index as the selection and store them in a variable
$q = $input | ?{ $_.index -ne $selection }
}
# Return the filtered variable
$q
}
# This function deletes the chosen duplicate hashes using git rm command
function Delete-Duplicates
{
[cmdletbinding()]
param(
[parameter(ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[object[]] $input
)
if($input -ne $null)
{
# Split the input array into smaller chunks using Split-Array function
$toDelete = $input | %{$_.relativepath} | Split-Array
# For each chunk, use git rm to delete the files
$toDelete | % { git rm $_ }
# Wait for 2 seconds before proceeding
sleep 2
}
}
function Get-Commits {
param (
# The date parameter specifies the cut-off date for the commits
[Parameter(Mandatory=$true)]
[string]$Date
)
# Use git log to get all commit hashes before the date in a table format
$commits = git log --all --before="$Date" --pretty=format:"%H"
# Return the table of commits
return $commits
}
# Define a function to search for a string in a commit using git grep
function Search-Commit {
param (
# The commit parameter specifies the commit hash to search in
[Parameter(Mandatory=$true)]
[string]$Commit,
# The string parameter specifies the string to search for
[Parameter(Mandatory=$true)]
[string]$String
)
# Use git grep to search for the string in the commit and return a boolean value
$result = git grep --ignore-case --word-regexp --fixed-strings -o $String -- $Commit
return $result
}
# Define a function to search for a string in a commit using git log and regex
function Search-Commit-Regex {
param (
# The commit parameter specifies the commit hash to search in
[Parameter(Mandatory=$true)]
[string]$Commit,
# The regex parameter specifies the regex pattern to search for
[Parameter(Mandatory=$true)]
[string]$Regex
)
# Use git log to search for the regex pattern in the commit and return a boolean value
$result = git log -G $Regex -- $Commit
return $result
}
# Define a function to create a hash table of commits and their frequencies of matching the search string
function Get-HashTable {
param (
# The commits parameter specifies the table of commits to process
[Parameter(Mandatory=$true)]
[array]$Commits,
# The string parameter specifies the string to search for in each commit
[Parameter(Mandatory=$true)]
[string]$String,
# The regex parameter specifies whether to use regex or not for searching (default is false)
[Parameter(Mandatory=$false)]
[bool]$Regex = $false
)
# Create an empty hash table to store the results
$hashTable = @{}
# Loop through each commit in the table of commits
foreach ($commit in $commits) {
# If regex is true, use Search-Commit-Regex function, otherwise use Search-Commit function
if ($Regex) {
$match = Search-Commit-Regex -Commit $commit -Regex $String
}
else {
$match = Search-Commit -Commit $commit -String $String
}
# If there is a match, increment the frequency of the commit in the hash table, otherwise set it to zero
if ($match) {
$hashTable[$commit]++
}
else {
$hashTable[$commit] = 0
}
}
# Return the hash table of commits and frequencies
return $hashTable
}
# Define a function to display a progress bar while processing a table of commits
function Show-Progress {
param (
[Parameter(Mandatory=$true)]
[array]$Commits,
# The activity parameter specifies the activity name for the progress bar (default is "Searching Events")
[Parameter(Mandatory=$false)]
[string]$Activity = "Searching Events",
# The status parameter specifies the status name for the progress bar (default is "Progress:")
[Parameter(Mandatory=$false)]
[string]$Status = "Progress:"
)
# Set the counter variable to zero
$i = 0
# Loop through each commit in the table of commits
foreach ($commit in $commits) {
# Increment the counter variable
$i = $i + 1
# Determine the completion percentage
$Completed = ($i / $commits.count * 100)
# Use Write-Progress to output a progress bar with the activity and status parameters
Write-Progress -Activity $Activity -Status $Status -PercentComplete $Completed
}
}
function GitGrep {
param ([string]$range, [string]$grepThis)
git log --pretty=format:"%H" $range --no-merges --grep="$grepThis" | ForEach-Object {
$Body = git log -1 --pretty=format:"%b" $_ | Select-String "$grepThis"
if($Body) {
git log -1 --pretty=format:"%H,%s" $_
Write-Host $Body
}
}
}
function Git-LsTree {
param ([string]$range, [string]$grepThis)
$Body = git ls-tree $range -r
$body | % {
$spl = $_ -split ' ',3
[pscustomobject]@{
hash = $range
q = $spl[0].trim()
type = $spl[1].trim()
objectID = $spl[2].Substring(0,40).trim()
relative = $spl[2].Substring(40).trim()
}
}
}

# \fix-CorruptedGitModules.ps1
<#
This code is a PowerShell script that checks the status of git repositories in a given folder and repairs
them if they are corrupted. It does the following steps:
It defines a begin block that runs once before processing any input. In this block, it sets some variables
for the modules and folder paths, validates them, and redirects the standard error output of git commands
to the standard output stream.
It defines a process block that runs for each input object. In this block, it loops through each subfolder
in the folder path and runs git status on it. If the output is fatal, it means the repository is corrupted
and needs to be repaired. To do that, it moves the corresponding module folder from the modules path to the
subfolder, replacing the existing .git file or folder. Then, it reads the config file of the repository and
removes any line that contains worktree, which is a setting that can cause problems with scoop. It prints
the output of each step to the console.
It defines an end block that runs once after processing all input. In this block, it restores the original
location of the script.#>
# A function to run git commands and check the exit code
function Invoke-Git {
param(
[Parameter(Mandatory=$true)]
[string]$Command # The git command to run
)
# Run the command and capture the output
$output = Invoke-Expression -Command "git $Command" -ErrorAction Stop
# return the output to the host
$output
# Check the exit code and throw an exception if not zero
if ($LASTEXITCODE -ne 0) {
throw "Git command failed: git $Command"
}
}
# \fix-CorruptedGitModulesCombinedWithQue.ps1
# Define a function to split text by a regular expression
# Define a function to split text by a regular expression
function Split-TextByRegex {
<#
.SYNOPSIS
Splits text by a regular expression and returns an array of objects with the start index, end index, and value of each match.
.DESCRIPTION
This function takes a path to a text file and a regular expression as parameters, and returns an array of objects with the start index, end index, and value of each match. The function uses the Select-String cmdlet to find the matches, and then creates custom objects with the properties of each match.
.PARAMETER Path
The path to the text file to be split.
.PARAMETER Regx
The regular expression to use for splitting.
.EXAMPLE
Split-TextByRegex -Path ".\test.txt" -Regx "submodule"
This example splits the text in the test.txt file by the word "submodule" and returns an array of objects with the start index, end index, and value of each match.
#>
# Validate the parameters
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path $_})]
[string]$Path,
[Parameter(Mandatory=$true)]
[string]$Regx
)
# Try to read the text from the file
try {
$Content = Get-Content $Path -Raw
}
catch {
Write-Error "Could not read the file: $_"
return
}
# Try to split the content by the regular expression
try {
$Matchez = [regex]::Matches($Content, $Regx)
$NonMatches = [regex]::Split($Content, $Regx)
$single = Select-String -InputObject $Content -Pattern $Regx -AllMatches | Select-Object -ExpandProperty Matches
}
catch {
Write-Error "Could not split the content by $Regx"
return
}
# Create an array to store the results
$Results = @()
if($IncNonmatches)
{
# Loop through the matches and non-matches and create custom objects with the index and value properties
for ($i = 0; $i -lt $Matchez.Count; $i++) {
$Results += [PSCustomObject]@{
index = $Matchez[$i].Index
value = $Matchez[$i].Value
}
$Results += [PSCustomObject]@{
index = $Matchez[$i].Index + $Matches[$i].Length
value = $NonMatches[$i + 1]
}
}
}
else {
# Loop through each match and create a custom object with its properties
foreach ($match in $single) {
$result = [PSCustomObject]@{
StartIndex = $match.Index
EndIndex = $match.Index + $match.Length - 1
Value = $match.Value
}
# Add the result to the array
$results += $result
}
}
# Return the results
return $Results
}
function Validate-Path {
param (
[Parameter(Mandatory=$true)]
[string]$Path
)
if (-not (Test-Path $Path)) {
Write-Error "Invalid path: $Path"
exit 1
}
}
# \GetWorktreeSubmodules.ps1
# Define a function to convert key-value pairs to custom objects
function keyPairTo-PsCustom {
<#
.SYNOPSIS
Converts key-value pairs to custom objects with properties corresponding to the keys and values.
.DESCRIPTION
This function takes an array of strings containing key-value pairs as a parameter, and returns an array of custom objects with properties corresponding to the keys and values. The function uses the ConvertFrom-StringData cmdlet to parse the key-value pairs, and then creates custom objects with the properties.
.PARAMETER KeyPairStrings
The array of strings containing key-value pairs.
.EXAMPLE
keyPairTo-PsCustom -KeyPairStrings @("name=John", "age=25")
This example converts the key-value pairs in the array to custom objects with properties name and age.
#>
# Validate the parameter
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string[]]$KeyPairStrings
)
# Create an array to store the results
$results = @()
# Loop through each string in the array
foreach ($string in $KeyPairStrings) {
# Try to parse the key-value pairs using ConvertFrom-StringData cmdlet
try {
$data = ConvertFrom-StringData $string
}
catch {
Write-Error "Could not parse the string: $_"
continue
}
# Create a custom object with properties from the data hashtable
$result = New-Object -TypeName PSObject -Property $data
# Add the result to the array
$results += $result
}
# Return the array of results
return $results
}
# Define a function to get the URL of a submodule
function byPath-RepoUrl {
param(
[string]$Path # The path of the submodule directory
)
# Change the current location to the submodule directory
Push-Location -Path $Path -ErrorAction Stop
# Get the URL of the origin remote
$url = invoke-git "config remote.origin.url" -ErrorAction Stop
# Parse the URL to get the part after the colon
$parsedUrl = ($url -split ':')[1]
# Return to the previous location
Pop-Location -ErrorAction Stop
# Return the parsed URL as output
return $parsedUrl
}
function get-gitUnhide ($Path)
{
Get-ChildItem -Path "$Path\*" -Force | Where-Object { $_.Name -eq ".git" }
}
# requries gitmodulesfile
function git-GetSubmodulePathsUrls
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path -Path "$_\.gitmodules"})]
[string]
$RepoPath
)
try {
if(validGitRepo)
{
$zz = (git config -f .gitmodules --get-regexp '^submodule\..*\.path$')
}
else{
$rgx = "submodule" # Set the regular expression to use for splitting
# Change the current directory to the working path
Set-Location $RepoPath
# Set the path to the .gitmodules file
$p = Join-Path $RepoPath ".gitmodules"
# Split the text in the .gitmodules file by the regular expression and store the results in a variable
$TextRanges = Split-TextByRegex -Path $p -Regx $rgx
$zz = $TextRanges | keyPairTo-PsCustom
if(! ($zz))
{
# Convert the key-value pairs in the text ranges to custom objects and store the results in a variable
$zz = $TextRanges | ForEach-Object {
try {
# Trim and join the values in each text range
$q = $_.value.trim() -join ","
}
catch {
# If trimming fails, just join the values
$q = $_.value -join ","
}
try {
# Split the string by commas and equal signs and create a hashtable with the path and url keys
$t = @{
path = $q.Split(',')[0].Split('=')[1].trim()
url = $q.Split(',')[1].Split('=')[1].trim()
}
}
catch {
# If splitting fails, just use the string as it is
$t = $q
}
# Convert the hashtable to a JSON string and then back to a custom object
$t | ConvertTo-Json | ConvertFrom-Json
}
}
}
$zz| % {
$path_key, $path = $_.split(" ")
$prop = [ordered]@{
Path = $path
Url = git config -f .gitmodules --get ("$path_key" -replace "\.path",".url")
NonRelative = Join-Path $RepoPath $path
}
return New-Object –TypeName PSObject -Property $prop
}
}
catch{
Throw "$($_.Exception.Message)"
}
}
# \GitSyncSubmoduleWithConfig.ps1
<#
.SYNOPSIS
Synchronizes the submodules with the config file.
.DESCRIPTION
This function synchronizes the submodules with the config file, using the Git-helper and ini-helper modules. The function checks the remote URLs of the submodules and updates them if they are empty or local paths. The function also handles conflicts and errors.
.PARAMETER GitDirPath
The path of the git directory where the config file is located.
.PARAMETER GitRootPath
The path of the git root directory where the submodules are located.
.PARAMETER FlagConfigDecides
A switch parameter that indicates whether to use the config file as the source of truth in case of conflicting URLs.
#>
function config-to-gitmodules {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]
$GitDirPath,
[Parameter(Mandatory = $true)]
[string]
$GitRootPath,
[Parameter(Mandatory = $false)]
[switch]
$FlagConfigDecides
)
# Import the helper modules
Import-Module Git-helper
Import-Module ini-helper
$configPath = (Join-Path -Path $GitDirPath -ChildPath "config")
# Get the config file content and select the submodule section
$rootKnowledge = Get-IniContent -Path $configPath | Select-Object -Property submodule
# Change the current location to the git root directory
Set-Location -Path $GitRootPath
# Loop through each submodule in the config file
foreach ($rootx in $rootKnowledge) {
try {
# Change the current location to the submodule path
Set-Location -Path $rootx.path
# Get submodule name and path from ini object properties $submoduleName = $submodule.submodule.Name
if(Import-Module PsIni)
{
# Import the PsIni module
$submodules = Get-IniContent -Path ".gitmodules" | Select-Object -Property submodule
}
if(Import-Module PsIni)
{
# Import the PsIni module
$submodulePath = Join-Path -Path (Split-Path -Path ".gitmodules") -ChildPath ($submodule.submodule.path)
}
# Get the remote URL of the submodule
$q = Get-GitRemoteUrl
# Check if the remote URL is a local path or empty
$isPath = Test-Path -Path $q -IsValid
$isEmpty = [string]::IsNullOrEmpty($q)
if ($isPath -or $isEmpty) {
# Set the remote URL to the one in the config file and overwrite it
Set-GitRemote -Url $rootx.url -Overwrite
}
else {
# Check if the URL in the config file is a local path or empty
$isConfigPath = Test-Path -Path $rootx.url -IsValid
$isConfigEmpty = [string]::IsNullOrEmpty($rootx.url)
if ($isConfigEmpty) {
# Append the submodule to the config file
$rootx | Add-IniElement -Path $configPath
}
elseif ($isConfigPath) {
# Append the remote URL to the submodule and replace it in the config file
# ($rootx + AppendProperty($q)) | Set-IniElement -Path $configPath -OldElement $rootx
}
elseif ($rootx.url -notin $q.url) {
# Handle conflicting URLs
if ($FlagConfigDecides) {
# Use the config file as the source of truth and replace it in the submodule path
$rootx | Set-IniElement -Path (Join-Path -Path $rootx.path -ChildPath ".gitmodules") -OldElement $q
}
else {
# Throw an error for conflicting URLs
throw "Conflicting URLs: $($rootx.url) and $($q.url)"
}
}
}
}
catch {
# Handle errors based on their messages
switch ($_.Exception.Message) {
"path not existing" {
return "uninitialized"
}
"path existing but no subroot present" {
return "already in index"
}
"path existing, git root existing, but git not recognized" {
return "corrupted"
}
default {
return $_.Exception.Message
}
}
}
}
}
# A function to move a .git Folder into the current directory and remove any gitfiles present
function unearthiffind ()
{
param (
[Parameter(Mandatory=$true)]
[string]$toRepair,
[Parameter(Mandatory=$true)]
[string]$Modules
)
# Get the module folder that matches the name of the parent directory
Get-ChildItem -Path $Modules -Directory | Where-Object { $_.Name -eq $toRepair.Directory.Name } | Select-Object -First 1 | % {
# Move the module folder to replace the .git file
Remove-Item -Path $toRepair -Force
Move-Item -Path $_.FullName -Destination $toRepair -Force
}
}
# A function to check the git status of a folder
function Check-GitStatus ($folder) {
# Change the current directory to the folder
Set-Location $folder.FullName
Write-Output "checking $folder"
if ((Get-ChildItem -force | ?{ $_.name -eq ".git" } ))
{
# Run git status and capture the output
$output = Invoke-Git "git status"
if(($output -like "fatal*"))
{
Write-Output "fatal status for $folder"
#UnabosrbeOrRmWorktree $folder
}
else
{
Write-Output @($output)[0]
}
}
else
{
Write-Output "$folder not yet initialized"
}
}
# Define a function to remove the worktree from a config file
function Remove-Worktree {
param(
[string]$ConfigPath # The path of the config file
)
if(Test-Package "Get-IniContent")
{
# Get the content of the config file as an ini object
$iniContent = Get-IniContent -FilePath $ConfigPath
# Remove the worktree property from the core section
$iniContent.core.Remove("worktree")
# Write the ini object back to the config file
$iniContent | Out-IniFile -FilePath $ConfigPath -Force
}
else
{
# Read the config file content as an array of lines
$configLines = Get-Content -Path $ConfigPath
# Filter out the lines that contain worktree
$newConfigLines = $configLines | Where-Object { $_ -notmatch "worktree" }
if (($configLines | Where-Object { $_ -match "worktree" }))
{
# Write the new config file content
Set-Content -Path $ConfigPath -Value $newConfigLines -Force
}
}
}
function Remove-WorktreeHere {
param(
[string]$ConfigPath, # The path of the config file
[alias]$folder,$toRepair
)
# Get the path to the git config file
$configFile = Join-Path -Path $toRepair -ChildPath "\config"
# Check if it exists
if (-not (Test-Path $configFile)) {
Write-Error "Invalid folder path: $toRepair"
}
else
{
Remove-Worktree -ConfigPath $toRepair
}
}
# A function to repair a corrupted git folder
# param folder: where to look for replacement module to unearth with
function UnabosrbeOrRmWorktree ($folder) {
get-gitUnhide $folder | % {
if( $_ -is [System.IO.FileInfo] )
{
unearthIffind $_ $folder
}
elseif( $_ -is [System.IO.DirectoryInfo] )
{
Remove-WorktreeHere $_
}
else
{
Write-Error "not a .git file or folder: $_"
}
}
}
# A function to repair a fatal git status
function UnabosrbeOrRmWorktree {
param (
[Parameter(Mandatory=$true)]
[string]$Path,
[Parameter(Mandatory=$true)]
[string]$Modules
)
# Print a message indicating fatal status
write-verbos "fatal status for $Path, atempting repair"
cd $Path
UnabosrbeOrRmWorktree -folder $Modules
}
function RepairWithQue-N-RepairFolder {
# Synopsis: A script to process files with git status and repair them if needed
# Parameter: Start - The start path to process
# Parameter: Modules - The path to the modules folder
param (
[Parameter(Mandatory=$true)]
[string]$Start,
[Parameter(Mandatory=$true)]
[string]$Modules
)
begin {
Push-Location
# Validate the arguments
Validate-Path -Path $Start
Validate-Path -Path $Modules
# Redirect the standard error output of git commands to the standard output stream
$env:GIT_REDIRECT_STDERR = '2>&1'
Write-Progress -Activity "Processing files" -Status "Starting" -PercentComplete 0
# Create a queue to store the paths
$que = New-Object System.Collections.Queue
# Enqueue the start path
$Start | % { $que.Enqueue($_) }
# Initialize a counter variable
$i = 0;
}
process {
# Loop through the queue until it is empty
do
{
# Increment the counter
$i++;
# Dequeue a path from the queue
$path = $que.Dequeue()
# Change the current directory to the path
Set-Location $path;
# Run git status and capture the output
$output = Check-GitStatus $path
# Check if the output is fatal
if($output -like "fatal*")
{
ActOnError -Path $path -Modules $modules
# Get the subdirectories of the path and enqueue them, excluding any .git folders
if ($continueOnError)
{
$toEnque = Get-ChildItem -Path "$path\*" -Directory -Exclude "*.git*"
}
}
else
{
$toEnque = git-GetSubmodulePathsUrls
}
$toEnque | % { $que.Enqueue($_.FullName) }
# Calculate the percentage of directories processed
$percentComplete = ($i / ($que.count+$i) ) * 100
# Update the progress bar
Write-Progress -Activity "Processing files" -PercentComplete $percentComplete
} while ($que.Count -gt 0)
}
end {
# Restore the original location
Pop-Location
Write-Progress -Activity "Processing files" -Status "Finished" -PercentComplete 100
}
}
function ActOnError {
<#todo
fallback solutions
* if everything fails,
set git dir path to abbsolute value and edit work tree in place
* if comsumption fails,
due to modulefolder exsisting, revert move and trye to use exsisting folder instead,
if this ressults in error, re revert to initial
move inplace module to x prefixed
atempt to consume again
* if no module is provided, utelyse everything to find possible folders
use hamming distance like priorit order
where
1. exact parrentmatch rekative to root
order resukts by total exact
take first precedance
2. predefined patterns taken
and finaly sort rest by hamming
#>
# This script converts a git folder into a submodule and absorbs its git directory
[CmdletBinding()]
param ( # Validate the arguments
$folder = "C:\ProgramData\scoop\persist",
$repairAlternatives = "C:\ProgramData\scoop\persist\.git\modules")
begin {
Get-ChildItem -path B:\GitPs1Module\* -Filter '*.ps1' | % { . $_.FullName }
Validate-Path $repairAlternatives
Validate-Path $folder
Push-Location
$pastE = $error #past error saved for later
$error.Clear()
# Save the previous error action preference
$previousErrorAction = $ErrorActionPreference
$ErrorActionPreference = "Stop"
# Set the environment variable for git error redirection
$env:GIT_REDIRECT_STDERR = '2>&1'
}
process {
# Get the list of folders in $folder # Loop through each folder and run git status
foreach ($f in (git-GetSubmodulePathsUrls)) {
# Change the current directory to the folder
Set-Location $f.FullName
Write-Verbos "checking $f"
if (!(Get-ChildItem -force | ?{ $_.name -eq ".git" } )) { Write-Verbos "$f not yet initialized" }
else {
# Run git status and capture the output
$output = Check-GitStatus $f.FullName
if(!($output -like "fatal*")) {Write-Output @($output)[0] }
else {
Write-Output "fatal status for $f"
$f | Get-ChildItem -force | ?{ $_.name -eq ".git" } | % {
$toRepair = $_
if( $toRepair -is [System.IO.FileInfo] )
{
$repairAlternatives | Get-ChildItem -Directory | ?{ $_.name -eq $toRepair.Directory.Name } | select -First 1 |
% {
# Move the folder to the target folder Move-Folder -Source $GitFolder -Destination (Join-Path $targetFolder 'x.git')
rm $toRepair -force ;
# Move the submodule folder to replace the git folder
Move-Item -Path $_.fullname -Destination $toRepair -force
}
}
else-if( $toRepair -is [System.IO.DirectoryInfo] )
{
# Remove the worktree line from the config file (Get-Content -Path $configFile | Where-Object { ! ($_ -match 'worktree') }) | Set-Content -Path $configFile
Remove-Worktree "$toRepair/config"
}
else
{
Write-Error "not a .git folder: $toRepair"
Write-Error "not a .git file: $toRepair"
}
removeAtPathReadToIndex
}
}
}
}
} end { Pop-Location }
}
<#
.SYNOPSIS
#This script adds git submodules to a working path based on the .gitmodules file
.PARAMETER WorkPath
The working path where the .gitmodules file is located.
.EXAMPLE
GitInitializeBySubmodule -WorkPath 'B:\ToGit\Projectfolder\NewWindows\scoopbucket-1'
#>
# requires functional git repo
# A function to get the submodules recursively for a given repo path
# should return the submodules in reverse order, deepest first, when not providing flag?
function Get-SubmoduleDeep {
param(
[Parameter(Mandatory=$true)]
[string]$RepoPath # The path to the repo
)
begin {
# Validate the repo path
Validate-PathW -Path $RepoPath
# Change the current directory to the repo path
Set-Location $RepoPath
# Initialize an empty array for the result
$result = @()
}
process {
# Run git submodule foreach and capture the output as an array of lines
$list = @(Invoke-Git "submodule foreach --recursive 'git rev-parse --git-dir'")
# Loop through the list and skip the last line (which is "Entering '...'")
foreach ($i in 0.. ($list.count-2)) {
# Check if the index is even, which means it is a relative path line
if ($i % 2 -eq 0)
{
# Create a custom object with the base, relative and gitDir properties and add it to the result array
$result += , [PSCustomObject]@{
base = $RepoPath
relative = $list[$i]
gitDir = $list[$i+1]
}
}
}
}
end {
# Return the result array
$result
}
}
Function Git-InitializeSubmodules {
[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High')]
Param(
# File to Create
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path $_})]
[string]
$RepoPath
)
begin{
# Validate the parameter
# Set the execution policy to bypass for the current process
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
Write-Verbose "[Add Git Submodule from .gitmodules]"
}
process{
# Filter out the custom objects that have a path property and loop through them
Get-SubmoduleDeep | Where-Object {($_.path)} |
%{
$url = $_.url
$path = $_.path
try {
if( New-Item -ItemType dir -Name $path -WhatIf -ErrorAction SilentlyContinue)
{
if($PSCmdlet.ShouldProcess($path,"clone $url -->")){
}
else
{
invoke-git "submodule add $url $path"
}
}
else
{
if($PSCmdlet.ShouldProcess($path,"folder already exsists, will trye to clone $url --> "))
{
}
else
{
Invoke-Git "submodule add -f $url $path"
}
}
# Try to add a git submodule using the path and url properties
}
catch {
Write-Error "Could not add git submodule: $_"
}
}
}
}
<#
.SYNOPSIS
Unabsorbe-ValidGitmodules from a git repository.
.DESCRIPTION
Unabsorbe-ValidGitmodules from a git repository by moving the .git directories from the submodules to the parent repository and updating the configuration.
.PARAMETER Paths
The paths of the submodules to extract. If not specified, all submodules are extracted.
.EXAMPLE
Extract-Submodules
.EXAMPLE
Extract-Submodules "foo" "bar"
[alias]
extract-submodules = "!gitextractsubmodules() { set -e && { if [ 0 -lt \"$#\" ]; then printf \"%s\\n\" \"$@\"; else git ls-files --stage | sed -n \"s/^160000 [a-fA-F0-9]\\+ [0-9]\\+\\s*//p\"; fi; } | { local path && while read -r path; do if [ -f \"${path}/.git\" ]; then local git_dir && git_dir=\"$(git -C \"${path}\" rev-parse --absolute-git-dir)\" && if [ -d \"${git_dir}\" ]; then printf \"%s\t%s\n\" \"${git_dir}\" \"${path}/.git\" && mv --no-target-directory --backup=simple -- \"${git_dir}\" \"${path}/.git\" && git --work-tree=\"${path}\" --git-dir=\"${path}/.git\" config --local --path --unset core.worktree && rm -f -- \"${path}/.git~\" && if 1>&- command -v attrib.exe; then MSYS2_ARG_CONV_EXCL=\"*\" attrib.exe \"+H\" \"/D\" \"${path}/.git\"; fi; fi; fi; done; }; } && gitextractsubmodules"
git extract-submodules [<path>...]
#>
function Unabsorbe-ValidGitmodules {
param (
[string[]]$Paths
)
# get the paths of all submodules if not specified
if (-not $Paths) {
$Paths = Get-SubmoduleDeep
}
# loop through each submodule path
foreach ($Path in $Paths) {
$gg = "$Path/.git"
# check if the submodule has a .git file
if (Test-Path -Path "$gg" -PathType Leaf) {
# get the absolute path of the .git directory
$GitDir = Get-GitDir -Path $Path
# check if the .git directory exists
if (Test-Path -Path $GitDir -PathType Container) {
# display the .git directory and the .git file
Write-Host "$GitDir`t$gg"
# move the .git directory to the submodule path
Move-Item -Path $GitDir -Destination "$gg" -Force -Backup
# unset the core.worktree config for the submodule
Remove-Worktree -ConfigPath "$gg/config"
# remove the backup file if any
Remove-Item -Path "$gg~" -Force -ErrorAction SilentlyContinue
# hide the .git directory on Windows
Hide-GitDir -Path $Path
}
}
}
}
# \GitUpdateSubmodulesAutomatically.ps1
<#
.SYNOPSIS
Updates the submodules of a git repository.
.DESCRIPTION
This function updates the submodules of a git repository, using the PsIni module and the git commands. The function removes any broken submodules, adds any new submodules, syncs the submodule URLs with the .gitmodules file, and pushes the changes to the remote repository.
.PARAMETER RepositoryPath
The path of the git repository where the submodules are located.
.PARAMETER SubmoduleNames
An array of submodule names that will be updated. If not specified, all submodules will be updated.
#>
function Update-Git-Submodules {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]
$RepositoryPath,
[Parameter(Mandatory = $false)]
[string[]]
$SubmoduleNames
)
# Set the error action preference to stop on any error
$ErrorActionPreference = "Stop"
# Change the current location to the repository path
Set-Location -Path $RepositoryPath
#update .gitmodules
config-to-gitmodules
$submodules = Get-SubmoduleDeep $RepositoryPath
# If submodule names are specified, filter out only those submodules from the array
if ($SubmoduleNames) {
$submodules = $submodules | Where-Object { $_.submodule.Name -in $SubmoduleNames }
}
# Loop through each submodule in the array and update it
foreach ($submodule in $submodules) {
# Get all submodules from the .gitmodules file as an array of objects
$submodulePath = $submodule.path
# Check if submodule directory exists
if (Test-Path -Path $submodulePath) {
# Change current location to submodule directory
Push-Location -Path $submodulePath
# Get submodule URL from git config
$submoduleUrl = Get-GitRemoteUrl
# Check if submodule URL is empty or local path
if ([string]::IsNullOrEmpty($submoduleUrl) -or (Test-Path -Path $submoduleUrl)) {
# Set submodule URL to remote origin URL
$submoduleUrl = (byPath-RepoUrl -Path $submodulePath)
if(!($submoduleUrl))
{
$submoduleUrl = $submodule.url
}
Set-GitRemoteUrl -Url $submoduleUrl
}
# Return to previous location
Pop-Location
# Update submodule recursively
Invoke-Git "submodule update --init --recursive $submodulePath"
}
else {
# Add submodule from remote URL
Invoke-Git "submodule add $(byPath-RepoUrl -Path $submodulePath) $submodulePath"
}
}
# Sync the submodule URLs with the .gitmodules file
Invoke-Git "submodule sync"
# Push the changes to the remote repository
Invoke-Git "push origin master"
}
function Healthy-GetSubmodules {
# Synopsis: A script to get the submodules recursively for a given repo path or a list of repo paths
# Parameter: RepoPaths - The path or paths to the repos
param (
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string[]]$RepoPaths # The path or paths to the repos
)
# A function to validate a path argument
# Call the main function for each repo path in the pipeline
foreach ($RepoPath in $RepoPaths) {
Get-SubmoduleDeep -RepoPath $RepoPath
}
}
# \git_add_submodule.ps1
#Get-Content .\.gitmodules | ? { $_ -match 'url' } | % { ($_ -split "=")[1].trim() }
function git_add_submodule () {
Write-Host "[Add Git Submodule from .gitmodules]" -ForegroundColor Green
Write-Host "... Dump git_add_submodule.temp ..." -ForegroundColor DarkGray
git config -f .gitmodules --get-regexp '^submodule\..*\.path$' > git_add_submodule.temp
Get-content git_add_submodule.temp | ForEach-Object {
try {
$path_key, $path = $_.split(" ")
$url_key = "$path_key" -replace "\.path",".url"
$url= git config -f .gitmodules --get "$url_key"
Write-Host "$url --> $path" -ForegroundColor DarkCyan
Invoke-Git "submodule add $url $path"
} catch {
Write-Host $_.Exception.Message -ForegroundColor Red
continue
}
}
Write-Host "... Remove git_add_submodule.temp ..." -ForegroundColor DarkGray
Remove-Item git_add_submodule.temp
}
#Git-InitializeSubmodules -repoPath 'G:\ToGit\projectFolderBare\scoopbucket-presist'
# \removeAtPathReadToIndex.ps1
function removeAtPathReadToIndex {
param (
[Parameter(Mandatory=$true, HelpMessage=".git, The path of the git folder to convert")]
[ValidateScript({Test-Path $_ -PathType Container ; Resolve-Path -Path $_ -ErrorAction Stop})]
[Alias("GitFolder")][string]$errorus,[Parameter(Mandatory=$true,HelpMessage="subModuleRepoDir, The path of the submodule folder to replace the git folder")]
#can be done with everything and menu
[Parameter(Mandatory=$true,HelpMessage="subModuleDirInsideGit")]
[ValidateScript({Test-Path $_ -PathType Container ; Resolve-Path -Path $_ -ErrorAction Stop})]
[Alias("SubmoduleFolder")][string]$toReplaceWith
)
# Get the config file path from the git folder
$configFile = Join-Path $GitFolder 'config'
# Push the current location and change to the target folder
# Get the target folder, name and parent path from the git folder
Write-Verbos "#---- asFile"
$asFile = ([System.IO.Fileinfo]$errorus.trim('\'))
Write-Verbos $asFile
$targetFolder = $asFile.Directory
$name = $targetFolder.Name
$path = $targetFolder.Parent.FullName
about-Repo #does nothing without -verbos
Push-Location
Set-Location $targetFolder
index-Remove $name $path
# Change to the parent path and get the root of the git repository
# Add and absorb the submodule using the relative path and remote url
$relative = get-Relative $path $targetFolder
Add-AbsorbSubmodule -Ref ( get-Origin) -Relative $relative
# Pop back to the previous location
Pop-Location
# Restore the previous error action preference
$ErrorActionPreference = $previousErrorAction
}
<#
.SYNOPSIS
Removes all files except those of a given name.
.DESCRIPTION
Removes all files except those of a given name from the Git history using filter-branch.
.PARAMETER FileName
The name of the file to keep.
.EXAMPLE
Remove-AllFilesExcept -FileName "readme.md"
#>
function Remove-AllFilesExcept {
param (
[Parameter(Mandatory)]
[string]$FileName
)
git filter-branch --prune-empty -f --index-filter "git ls-tree -r --name-only --full-tree $GIT_COMMIT | grep -v '$FileName' | xargs git rm -r"
}
<#
.SYNOPSIS
Moves a file to a new directory.
.DESCRIPTION
Moves a file from the current directory to a new directory using filter-branch.
.PARAMETER FileName
The name of the file to move.
.PARAMETER NewDir
The name of the new directory.
.EXAMPLE
Move-File -FileName "my-file" -NewDir "new-dir"
#>
function Move-File {
param (
[Parameter(Mandatory)]
[string]$FileName,
[Parameter(Mandatory)]
[string]$NewDir
)
git filter-branch --tree-filter "
if [ -f current-dir/$FileName ]; then
mv current-dir/$FileName $NewDir/
fi" --force HEAD
}
<#
.SYNOPSIS
Moves a directory to a new location.
.DESCRIPTION
Moves a directory to a new location using filter-branch and subdirectory-filter.
.PARAMETER DirName
The name of the directory to move.
.EXAMPLE
Move-Directory -DirName "foo"
#>
function Move-Directory {
param (
[Parameter(Mandatory)]
[string]$DirName
)
set -eux
mkdir -p __github-migrate__
mvplz="if [ -d $DirName ]; then mv $DirName __github-migrate__/; fi;"
git filter-branch -f --tree-filter "$mvplz" HEAD
git filter-branch -f --subdirectory-filter __github-migrate__
}
<#
.SYNOPSIS
Renames all occurrences of a word in file names.
.DESCRIPTION
Renames all occurrences of a word in file names using filter-branch and string replacement.
.PARAMETER OldWord
The word to replace.
.PARAMETER NewWord
The word to use instead.
.EXAMPLE
Rename-WordInFileNames -OldWord "Result" -NewWord "Stat"
#>
function Rename-WordInFileNames {
param (
[Parameter(Mandatory)]
[string]$OldWord,
[Parameter(Mandatory)]
[string]$NewWord
)
git filter-branch --tree-filter '
for file in $(find . ! -path "*.git*" ! -path "*.idea*")
do
if [ "$file" != "${file/$OldWord/$NewWord}" ]
then
mv "$file" "${file/$OldWord/$NewWord}"
fi
done
' --force HEAD
}
<#
.SYNOPSIS
Replaces all occurrences of a word in file contents.
.DESCRIPTION
Replaces all occurrences of a word in file contents using filter-branch and sed.
.PARAMETER OldWord
The word to replace.
.PARAMETER NewWord
The word to use instead.
.EXAMPLE
Replace-WordInFileContents -OldWord "Result" -NewWord "Stat"
#>
function Replace-WordInFileContents {
param (
[Parameter(Mandatory)]
[string]$OldWord,
[Parameter(Mandatory)]
[string]$NewWord
)
git filter-branch --tree-filter '
for file in $(find . -type f ! -path "*.git*" ! -path "*.idea*")
do
sed -i "" -e s/$OldWord/$NewWord/g $file;
done
' --force HEAD
}
<#
.SYNOPSIS
Gets the names of modified files between two commits.
.DESCRIPTION
Gets the names of modified files between two commits using git diff and regex.
.PARAMETER ReferenceCommitId
The commit id of the reference commit.
.EXAMPLE
Get-ModifiedFileNames 65c0ce6a8e041b78c032f5efbdd0fd3ec9bc96f5
#>
function Get-ModifiedFileNames {
param (
[Parameter(Mandatory)]
[string]$ReferenceCommitId
)
$regex = 'diff --git a.|\sb[/]'
git diff --diff-filter=MRC HEAD $ReferenceCommitId | ?{ $_ -match '^diff.*' } | % { $_ -split($regex) }
}
Import-Module PsIni
function gitRemoveWorktree ($configPath)
{
$iniContent = Get-IniContent -FilePath $configPath
$iniContent.core.Remove("worktree") ;
$iniContent | Out-IniFile -FilePath $configPath -Force
}