|
using namespace System.IO
|
|
using namespace System.Collections.Generic
|
|
|
|
# Write verbose messages on import
|
|
if ((Get-PSCallStack)[1].Arguments -imatch 'Verbose=True') { $PSDefaultParameterValues['*:Verbose'] = $true }
|
|
|
|
|
|
if ((Test-ApplicationExistsInPath -ApplicationName 'git') -eq $false) {
|
|
Write-Verbose 'git does not exist in the Path. Skipping the import of git ProfileUtility commands.'
|
|
# Do not export any module commands.
|
|
Export-ModuleMember
|
|
return
|
|
}
|
|
# https://github.com/PowerShell/PowerShell/issues/17730#issuecomment-1190678484
|
|
$ExportedMembers = [List[string]]::new()
|
|
|
|
|
|
# Private function pulled from TM-ProfileUtility
|
|
function Get-CurrentPath {
|
|
<#
|
|
.SYNOPSIS
|
|
Returns the current location's provider path.
|
|
|
|
.OUTPUTS
|
|
Returns the ProviderPath except when the current location matches the start to a UNC path.
|
|
In those cases the $executionContext.SessionState.Path.CurrentLocation.Path is returned instead.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([string])]
|
|
param()
|
|
|
|
$ProviderPath = (Get-Location).ProviderPath
|
|
$result = if ($ProviderPath -match '\\\\') {
|
|
$executionContext.SessionState.Path.CurrentLocation.Path
|
|
} else {
|
|
$ProviderPath
|
|
}
|
|
|
|
return $result
|
|
}
|
|
|
|
|
|
class GitStatusInfo {
|
|
<#
|
|
.SYNOPSIS
|
|
This class is used to encapsulate the current status information of a Git repository.
|
|
|
|
.DESCRIPTION
|
|
GitStatusInfo contains lists of files that have been updated, unchanged, stage, untracked, or ignored.
|
|
It provides a static method, GetCurrentStatusInfo, which checks the status of the repository using the
|
|
'git status' command and fills these lists accordingly.
|
|
|
|
This class helps in managing and visualizing the status information of a Git repository.
|
|
#>
|
|
$Updated = [List[FileInfo]]::new()
|
|
$Deleted = [List[FileInfo]]::new()
|
|
$AddedStaged = [List[FileInfo]]::new()
|
|
$ModifiedStaged = [List[FileInfo]]::new()
|
|
$UnTracked = [List[FileInfo]]::new()
|
|
$Ignored = [List[FileInfo]]::new()
|
|
$Unchanged = [List[FileInfo]]::new()
|
|
|
|
[GitStatusInfo] static GetCurrentStatusInfo([string]$gitPath = [string]::Empty) {
|
|
function Get-FilePaths {
|
|
[OutputType([void])]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$Path,
|
|
|
|
[Parameter(Mandatory)]
|
|
[AllowEmptyCollection()]
|
|
[List[FileInfo]]$Collection,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$Type
|
|
)
|
|
|
|
if ([Directory]::Exists($Path)) {
|
|
foreach ($File in (Get-ChildItem -Path $Path -Recurse -File)) {
|
|
$Collection.Add($File)
|
|
}
|
|
} elseif ([File]::Exists($Path)) {
|
|
$Collection.Add([FileInfo]::New($Path))
|
|
} else {
|
|
Write-Warning "$Type Path '$Path' does not exist."
|
|
}
|
|
}
|
|
|
|
$CurrentStatus = [GitStatusInfo]::New()
|
|
|
|
if ([string]::IsNullOrWhiteSpace($GitPath)) { $GitPath = Get-GitPath }
|
|
|
|
if ([string]::IsNullOrWhiteSpace($GitPath)) {
|
|
Write-Warning 'The path heirarchy does not contain a git directory.'
|
|
return $CurrentStatus
|
|
}
|
|
|
|
$GitPath = (Resolve-Path -Path $GitPath -ErrorAction Stop).Path
|
|
Push-Location $GitPath
|
|
|
|
try {
|
|
foreach ($line in (git status --show-stash --ignored --porcelain)) {
|
|
$StartSymbol = $Line.Substring(0, 2)
|
|
$GitPorcelainPath = $Line.Substring(2).Trim().TrimStart('"').TrimEnd('"')
|
|
$ItemPath = Join-Path -Path $GitPath -ChildPath $GitPorcelainPath
|
|
$Item = Get-Item -Path $ItemPath -Force
|
|
switch ($StartSymbol) {
|
|
' M' { $CurrentStatus.Updated.Add($Item) }
|
|
' D' { $CurrentStatus.Deleted.Add([FileInfo]::New($ItemPath)) }
|
|
'A ' { $CurrentStatus.AddedStaged.Add($Item) }
|
|
'M ' { $CurrentStatus.ModifiedStaged.Add($Item) }
|
|
'??' { Get-FilePaths -Path $Item.FullName -Collection $CurrentStatus.UnTracked -Type 'UnTracked' }
|
|
'!!' { Get-FilePaths -Path $Item.FullName -Collection $CurrentStatus.Ignored -Type 'Ignored' }
|
|
default { Write-Warning "GitStatusInfo Unknown Element: '$StartSymbol'`nLine: $Line" }
|
|
}
|
|
}
|
|
|
|
[string[]]$ChangeElements = @(
|
|
$CurrentStatus.Updated.FullName;
|
|
$CurrentStatus.Deleted.FullName;
|
|
$CurrentStatus.AddedStaged.FullName;
|
|
$CurrentStatus.ModifiedStaged.FullName;
|
|
$CurrentStatus.UnTracked.FullName;
|
|
$CurrentStatus.Ignored.FullName
|
|
)
|
|
|
|
foreach ($line in (git ls-files)) {
|
|
$ItemPath = Join-Path -Path $GitPath -ChildPath $line
|
|
if ($ChangeElements -notcontains $ItemPath) {
|
|
$CurrentStatus.Unchanged.Add((Get-Item -Path $ItemPath -Force))
|
|
}
|
|
}
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
|
|
return $CurrentStatus
|
|
}
|
|
}
|
|
|
|
|
|
function Get-GitBranch {
|
|
<#
|
|
.SYNOPSIS
|
|
Returns the current Git branch with a relevant symbol based on the branch name.
|
|
|
|
.DESCRIPTION
|
|
The function recursively searches for the .git directory in the current and parent directories and retrieves
|
|
the current Git branch. Symbols are also appended to the string if the branch matches the following patterns
|
|
🚀 for '/master' or '/main'
|
|
🚧 for '/dev'
|
|
|
|
.PARAMETER Path
|
|
An optional parameter allowing you to specify the location of the git folder to retrieve a branch information for.
|
|
|
|
.PARAMETER NoSymbol
|
|
An optional parameter that removes all extra formatting (including the brackets and optional branch symbol) and
|
|
only returns the branch name as defined by: git rev-parse --abbrev-ref --symbolic-full-name '@{u}'
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([string])]
|
|
param(
|
|
[Parameter(Mandatory = $false)]
|
|
[Validation.ValidatePathExists('Folder')]
|
|
[string]$Path = (Get-CurrentPath),
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$NoSymbol
|
|
)
|
|
|
|
$Result = [string]::Empty
|
|
|
|
if ((Get-GitPath -Path $Path) -ne [string]::Empty) {
|
|
Push-Location -Path $Path
|
|
try {
|
|
# need to do this so the stderr doesn't show up in $error
|
|
$ErrorActionPreferenceOld = $ErrorActionPreference
|
|
$ErrorActionPreference = 'Ignore'
|
|
$branch = git rev-parse --abbrev-ref --symbolic-full-name '@{u}'
|
|
$ErrorActionPreference = $ErrorActionPreferenceOld
|
|
# handle case where branch is local
|
|
if ($lastexitcode -ne 0 -or $null -eq $branch) {
|
|
$branch = git rev-parse --abbrev-ref HEAD
|
|
}
|
|
if ($NoSymbol) {
|
|
$Result = "$branch"
|
|
} else {
|
|
$branchSymbol = if (($branch -imatch '/master') -or ($branch -imatch '/main')) {
|
|
'🚀'
|
|
} elseif ($branch -imatch '/dev') {
|
|
'🚧'
|
|
}
|
|
$Result = "[$branch$branchSymbol] "
|
|
}
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
return $Result
|
|
}
|
|
$ExportedMembers.Add('Get-GitBranch')
|
|
|
|
|
|
function Get-GitPath {
|
|
<#
|
|
.SYNOPSIS
|
|
Retrieves the path of the current Git repository if it exists in the current path hierarchy.
|
|
|
|
.DESCRIPTION
|
|
This function looks for the .git directory starting from the specified path and continuing up the directory
|
|
tree.
|
|
|
|
.PARAMETER Path
|
|
The path to begin the search for the git repository in. By default this will look in the current working directory.
|
|
|
|
.OUTPUTS
|
|
Returns the path of the repository where the .git directory is located as a string.
|
|
If no .git directory is found, it returns an empty string.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([string])]
|
|
param(
|
|
[Parameter(Mandatory = $false)]
|
|
[Validation.ValidatePathExists('Folder')]
|
|
[string]$Path = (Get-CurrentPath)
|
|
)
|
|
|
|
$IteratePath = "$Path"
|
|
|
|
while ([string]::IsNullOrWhiteSpace($IteratePath) -eq $false) {
|
|
if ([Directory]::Exists((Join-Path -Path $IteratePath -ChildPath '.git'))) {
|
|
return $IteratePath
|
|
}
|
|
$IteratePath = [Path]::GetDirectoryName($IteratePath)
|
|
}
|
|
return [string]::Empty
|
|
}
|
|
$ExportedMembers.Add('Get-GitPath')
|
|
|
|
|
|
function Get-GitStatusInfo {
|
|
<#
|
|
.SYNOPSIS
|
|
Retrieves the git status info for the specified git directory.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([GitStatusInfo])]
|
|
param(
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$Path = [string]::Empty
|
|
)
|
|
|
|
[GitStatusInfo]::GetCurrentStatusInfo($Path)
|
|
}
|
|
$ExportedMembers.Add('Get-GitStatusInfo')
|
|
|
|
|
|
function Redo-GitCommitAsSigned {
|
|
<#
|
|
.SYNOPSIS
|
|
Modifies the last git commit to be signed.
|
|
|
|
.DESCRIPTION
|
|
This function allows you to sign the last commit by amending it with the -S flag.
|
|
|
|
.EXAMPLE
|
|
Redo-GitCommitAsSigned
|
|
Signs the last commit in the current repository.
|
|
#>
|
|
[Alias('Sign-LastGitCommit')]
|
|
[OutputType([Void])]
|
|
param()
|
|
|
|
git commit --amend --no-edit -n -S
|
|
}
|
|
$ExportedMembers.Add('Redo-GitCommitAsSigned')
|
|
|
|
|
|
function Remove-GitTag {
|
|
<#
|
|
.SYNOPSIS
|
|
Removes a specified git tag both locally and remotely.
|
|
|
|
.PARAMETER Tag
|
|
The name of the git tag to remove.
|
|
|
|
.PARAMETER Remote
|
|
Removes the tag from the origin location in addition to the removing the tag from the local repository.
|
|
|
|
.EXAMPLE
|
|
# Remove the 'v1.0.0' tag from the local and remote repositories.
|
|
Remove-GitTag -Tag 'v1.0.0' -Remote
|
|
#>
|
|
[CmdletBinding()]
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string]$Tag,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$IncludeRemote
|
|
)
|
|
|
|
git tag -d $Tag
|
|
if ($IncludeRemote) {
|
|
git push --delete origin $Tag
|
|
}
|
|
}
|
|
$ExportedMembers.Add('Remove-GitTag')
|
|
|
|
|
|
function Set-GitFileAssumeUnchanged {
|
|
<#
|
|
.SYNOPSIS
|
|
Tells git to assume a file in the Git repository is unchanged.
|
|
|
|
.DESCRIPTION
|
|
This function makes Git "assume" the file has not been changed, meaning that Git will ignore any changes to
|
|
that file. The file will not appear in 'git status' and it will not be checked for changes.
|
|
This is useful for files with local changes that are needed, but should not be committed.
|
|
|
|
.PARAMETER FilePath
|
|
The relative or absolute path to the file to mark as "assume unchanged".
|
|
|
|
.PARAMETER GitPath
|
|
The relative or absolute path to the file to mark as "assume unchanged".
|
|
#>
|
|
[Alias('Ignore-GitFileChanges')]
|
|
[OutputType([Void])]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[Validation.ValidatePathExists('File')]
|
|
[string]$FilePath
|
|
)
|
|
|
|
$Item = Get-Item -Path $FilePath -Force -ErrorAction Stop
|
|
Push-Location (Get-GitPath -Path $Item.Parent)
|
|
try {
|
|
$StatusInfo = [GitStatusInfo]::GetCurrentStatusInfo()
|
|
if ($StatusInfo.Staged.FullName -contains $Item.FullName) {
|
|
# Reset the item so that it is no longer staged
|
|
git reset HEAD -- $Item.FullName
|
|
}
|
|
git update-index --assume-unchanged $FilePath
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
$ExportedMembers.Add('Set-GitFileAssumeUnchanged')
|
|
|
|
|
|
function Get-GitBinaryFiles {
|
|
<#
|
|
.SYNOPSIS
|
|
Retrieves the FileInfo object for each file git considers as a "Binary" file.
|
|
|
|
.DESCRIPTION
|
|
Retrieves all files that git considers as having a binary file encoding.
|
|
|
|
.PARAMETER Path
|
|
The path to begin the search for the binary files in. By default this will look in the current working directory.
|
|
|
|
.OUTPUTS
|
|
Returns the FileInfo objects for the files that git considers as having a binary file encoding.
|
|
If no binary files are found then nothing is returned to the user.
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([System.IO.FileInfo])]
|
|
param(
|
|
[Parameter(Mandatory = $false)]
|
|
[Validation.ValidatePathExists('Folder')]
|
|
[string]$Path = (Get-CurrentPath)
|
|
)
|
|
|
|
begin { Push-Location $Path }
|
|
|
|
process {
|
|
try {
|
|
$NonBinaryFiles = git grep -Il .
|
|
$BinaryFiles = git ls-files | Where-Object { $NonBinaryFiles -notcontains $_ }
|
|
foreach ($File in $BinaryFiles) {
|
|
Get-Item -Path $File
|
|
}
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
}
|
|
$ExportedMembers.Add('Get-GitBinaryFiles')
|
|
|
|
|
|
if ($IsLinux -or $IsMacOS) {
|
|
function Set-GitSettings {
|
|
<#
|
|
.SYNOPSIS
|
|
Configures default git settings for a Linux PowerShell environment.
|
|
|
|
.DESCRIPTION
|
|
This function sets up the default SSH and GPG settings for git within a Linux PowerShell environment.
|
|
It is not designed for use on non-Linux platforms.
|
|
|
|
.PARAMETER GithubSSHKeyPath
|
|
The file path to the SSH key you want to use for GitHub.
|
|
If no key is provided then the function will use the default id_ed25519 key if it exists or prompts the user
|
|
to select an available key.
|
|
|
|
.EXAMPLE
|
|
# Configures git settings using the default or user-selected SSH key.
|
|
Set-GitSettings
|
|
|
|
.EXAMPLE
|
|
# Configures git settings using the specified SSH key at '~/.ssh/my_key'.
|
|
Set-GitSettings -GithubSSHKeyPath '~/.ssh/my_key'
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([Void])]
|
|
param (
|
|
[Parameter(Mandatory = $false)]
|
|
[IO.FileInfo]$GithubSSHKeyPath
|
|
)
|
|
|
|
$SSHAgent = 'ssh-agent'
|
|
$SSHAdd = 'ssh-add'
|
|
if (
|
|
(
|
|
(Test-ApplicationExistsInPath -ApplicationName $SSHAgent) -and
|
|
(Test-ApplicationExistsInPath -ApplicationName $SSHAdd)
|
|
) -ne $true
|
|
) {
|
|
throw 'Missing required applications. Task requires both ssh-agent and ssh-add.'
|
|
}
|
|
try { & $SSHAgent -k *>&1 | Out-Null } catch { }
|
|
$Output = & $SSHAgent -s
|
|
|
|
$Env:SSH_AUTH_SOCK = $Output[0].Split('=')[1].split(';')[0]
|
|
$Env:SSH_AGENT_PID = $Output[1].Split('=')[1].split(';')[0]
|
|
$Env:GPG_TTY = tty
|
|
|
|
# Find the right SSH key to use for github.
|
|
$SSH = if (Test-Path $GithubSSHKeyPath -PathType Leaf -ErrorAction Ignore) {
|
|
# If the user profides a path, and it exists, use that.
|
|
$GithubSSHKeyPath
|
|
} elseif (Test-Path '~/.ssh/id_ed25519' -PathType Leaf -ErrorAction Ignore) {
|
|
# If no user provided key exists then try github default recommended.
|
|
(Get-Item '~/.ssh/id_ed25519').FullName
|
|
} else {
|
|
# last resort - ask the user.
|
|
$Keys = Get-ChildItem -Path '~/.ssh/' -File | Where-Object {
|
|
($_.Extension -ine '.pub') -and
|
|
($_.name -ine 'known_hosts')
|
|
}
|
|
if ($Keys) {
|
|
$Index = 0
|
|
$KeyList = Foreach ($Key in $Keys) {
|
|
[PSCustomObject]@{
|
|
Number = $Index
|
|
Path = $Key.FullName
|
|
}
|
|
++$Index
|
|
}
|
|
$PromptAnswer = Read-Host -Prompt (
|
|
"Enter the number for the ssh key you want to use.`n" +
|
|
($KeyList | Format-Table | Out-String)
|
|
)
|
|
$Keys[$PromptAnswer].FullName
|
|
} else {
|
|
Write-Error 'No SSH Keys found.'
|
|
}
|
|
}
|
|
& $SSHAdd $SSH
|
|
}
|
|
$ExportedMembers.Add('Set-GitSettings')
|
|
}
|
|
|
|
|
|
Export-ModuleMember -Function $ExportedMembers.ToArray()
|