functions/Find-GitRepo.ps1

function Find-GitRepo
{
    <#
    .SYNOPSIS
    Finds all git repositories that exist under the specifed root directory.
 
    .DESCRIPTION
    Finds all git repositories that exist under the specifed root directory.
    Searches the specifed directory and its subdirectories and returns a set of directory objects, each of which is a git repository.
 
    .PARAMETER RootDirectory
    An array of directory paths to be searched. Paths that do not exist will be ignored.
    If the parameter is omitted, or null, or an empty string, all fixed drives will be searched.
 
    .PARAMETER SetGitRepoPath
    Populates the $GitRepoPath module variable with a list of the paths for all found repositories.
 
    .PARAMETER AppendGitRepoPath
    Appends the list of paths for all found repositories to the $GitRepoPath module variable.
    Paths that are already in the $GitRepoPath module variable will not be duplicated.
 
    .EXAMPLE
    ## Find git repositories under a root directory ##
 
    PS C:\> Find-GitRepo -RootDirectory 'C:\PowdrgitExamples' | Select-Object -ExpandProperty FullName
    C:\PowdrgitExamples\MyToolbox
    C:\PowdrgitExamples\Project1
 
    .EXAMPLE
    ## Populate the $GitRepoPath module variable with SetGitRepoPath parameter ##
 
    PS C:\> $GitRepoPath = $null
    PS C:\> Find-GitRepo -RootDirectory 'C:\PowdrgitExamples' -SetGitRepoPath | Out-Null
    PS C:\> $GitRepoPath
    C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1
 
    .EXAMPLE
    ## Populate the $GitRepoPath module variable with function output ##
 
    PS C:\> $GitRepoPath = $null
    PS C:\> $GitRepoPath = (Find-GitRepo -RootDirectory 'C:\PowdrgitExamples' | Select-Object -ExpandProperty FullName) -join ';'
    PS C:\> $GitRepoPath
    C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1
 
    # This example uses the output of Find-GitRepo to populate the $GitRepoPath module variable.
    # It is equivalent to the previous example, however, this method may be preferred when filtering is required e.g.:
    # $GitRepoPath = (Find-GitRepo -RootDirectory 'C:\PowdrgitExamples' | Where-Object Name -ne 'MyToolbox' | Select-Object -ExpandProperty FullName) -join ';'
 
    .EXAMPLE
    ## Use AppendGitRepoPath to add new repositories to the $GitRepoPath module variable ##
 
    PS C:\> $GitRepoPath = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the existing repository paths are defined
    PS C:\> git init "C:\PowdrgitExamples2\Project2" 2>&1 | Out-Null # create a new git repository
    PS C:\> Find-GitRepo -RootDirectory 'C:\PowdrgitExamples2' -AppendGitRepoPath | Out-Null
    PS C:\> $GitRepoPath
    C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1;C:\PowdrgitExamples2\Project2
 
    # Clean up if required: Remove-Item -Path 'C:\PowdrgitExamples2' -Recurse -Force
 
    .EXAMPLE
    ## Find git repositories by piping objects ##
 
    PS C:\> 'C:\PowdrgitExamples' | Find-GitRepo | Select-Object -ExpandProperty FullName
    C:\PowdrgitExamples\MyToolbox
    C:\PowdrgitExamples\Project1
 
    # Strings can be piped directly into the function.
 
    PS C:\> Get-Item -Path 'C:\PowdrgitExamples' | Find-GitRepo | Select-Object -ExpandProperty FullName
    C:\PowdrgitExamples\MyToolbox
    C:\PowdrgitExamples\Project1
 
    # Path and FullName are aliases for the RootDirectory parameter, allowing directory objects to be piped to Find-GitRepo.
 
    .INPUTS
    [System.IO.DirectoryInfo]
    Accepts directory objects.
 
    .OUTPUTS
    [System.IO.DirectoryInfo]
    Returns directory objects.
 
    .NOTES
    Author : nmbell
 
    .LINK
    about_powdrgit
    .LINK
    Get-GitRepo
    .LINK
    Set-GitRepo
    .LINK
    Test-GitRepoPath
    #>


    # Use cmdlet binding
    [CmdletBinding(
      DefaultParameterSetName = 'Set'
    , HelpURI                 = 'https://github.com/nmbell/powdrgit/blob/main/help/Find-GitRepo.md'
    )]

    # Declare parameters
    Param(

        [Parameter(
          Mandatory                       = $false
        , Position                        = 0
        , ValueFromPipeline               = $true
        , ValueFromPipelineByPropertyName = $true
        )]
        [Alias('FullName','Path')]
        [String[]]
        $RootDirectory

    ,    [Parameter(
          ParameterSetName = 'Set'
        , Mandatory        = $false
        )]
        [Switch]
        $SetGitRepoPath

    ,    [Parameter(
          ParameterSetName = 'Append'
        , Mandatory        = $false
        )]
        [Switch]
        $AppendGitRepoPath

    )

    BEGIN
    {
        $wvBlock          = 'B'

        # Common BEGIN:
        Set-StrictMode -Version 2.0
        $thisFunctionName = $MyInvocation.InvocationName
        $start            = Get-Date
        $wvIndent         = '| '*($PowdrgitCallDepth++)
        Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Start: $($start.ToString('yyyy-MM-dd HH:mm:ss.fff'))"

        # Function BEGIN:
        Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Finding current location"
        Push-Location -StackName FindGitRepo

        If (!$RootDirectory)
        {
            $RootDirectory = Get-Volume `
                                | Where-Object { $_.DriveType -eq 'Fixed' -and $_.DriveLetter } `
                                | Select-Object @{ l = 'DrivePath'; e = {$_.DriveLetter+':\'} } `
                                | Sort-Object -Property DrivePath `
                                | Select-Object -ExpandProperty DrivePath
            Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Setting root directory list to $RootDirectory"
        }
    }

    PROCESS
    {
        $wvBlock = 'P'

        [System.IO.DirectoryInfo[]]$gitReposAll = @()
        ForEach ($drivePath in $RootDirectory)
        {
            [System.IO.DirectoryInfo[]]$gitRepos = @()
            If (!(Test-Path -Path $drivePath))
            {
                Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Skipping root directory $drivePath (does not exist)"
            }
            Else
            {
                Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Looking for git repositories under $drivePath"
                Set-Location -Path $drivePath
                Get-ChildItem -Directory -Hidden -Recurse -Filter '.git' -ErrorAction SilentlyContinue `
                    | Where-Object { $_.FullName -notlike '*\$RECYCLE.BIN\*' } `
                    | Select-Object -ExpandProperty Parent `
                    | Tee-Object -Variable gitRepos
                Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Found $($gitRepos.Count) repositories under $drivePath"
            }
            $gitReposAll += $gitRepos
        }
        Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Found $($gitReposAll.Count) repositories total"

        If ($SetGitRepoPath)
        {
            Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Setting `$GitRepoPath module variable"
            $script:GitRepoPath = ($gitReposAll | Select-Object -ExpandProperty FullName) -join ';'
        }

        If ($AppendGitRepoPath)
        {
            Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Appending to `$GitRepoPath module variable"
            $script:GitRepoPath += (';'*[Bool]$script:GitRepoPath)+(($gitReposAll | Select-Object -ExpandProperty FullName) -join ';')
        }

        Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Deduping and ordering items in `$GitRepoPath module variable"
        $script:GitRepoPath = (($script:GitRepoPath -split ';') | Select-Object -Unique | Sort-Object) -join ';'
    }

    END
    {
        $wvBlock = 'E'

        # Function END:
        Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Setting location to original directory"
        Pop-Location -StackName FindGitRepo

        # Common END:
        $end      = Get-Date
        $duration = New-TimeSpan -Start $start -End $end
        Write-Verbose "$(wvTimestamp)$wvIndent[$thisFunctionName][$wvBlock]Finish: $($end.ToString('yyyy-MM-dd HH:mm:ss.fff')) ($('{0}d {1:00}:{2:00}:{3:00}.{4:000}' -f $duration.Days,$duration.Hours,$duration.Minutes,$duration.Seconds,$duration.Milliseconds))"
        $PowdrgitCallDepth--
    }
}