Functions/Copy-GitRepository.ps1

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Copy-GitRepository {
    <#
    .SYNOPSIS
    Clones a Git repository.

    .DESCRIPTION
    The `Copy-GitRepository` function clones a Git repository from the URL specified by `Uri` to the path specified by `DestinationPath` and checks out the `master` branch. If the repository requires authentication, pass the username/password via the `Credential` parameter.

    To clone a local repository, pass a file system path to the `Uri` parameter.

    .EXAMPLE
    Copy-GitRepository -Uri 'https://github.com/webmd-health-services/PowerGit' -DestinationPath PowerGit
    #>

    param(
        [Parameter(Mandatory, Position = 0)]
        [string]
        # The URI or path to the source repository to clone.
        $Source,

        [string]
        # The directory where the repository should be cloned to. Must not exist or be empty.
        $DestinationPath,

        [pscredential]
        # The credentials to use to connect to the source repository.
        $Credential,

        [Switch]
        # Returns a `System.IO.DirectoryInfo` object for the new copy's `.git` directory.
        $PassThru
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

    $Source = ConvertTo-GitFullPath -Uri $Source
    if (-not $DestinationPath) {
        $DestinationPath = Join-Path $PWD (Split-Path $Source -LeafBase)
    }
    $DestinationPath = ConvertTo-GitFullPath -Path $DestinationPath

    $options = [libgit2sharp.CloneOptions]::new()
    if ( $Credential ) {
        $gitCredential = [LibGit2Sharp.SecureUsernamePasswordCredentials]::new()
        $gitCredential.Username = $Credential.UserName
        $gitCredential.Password = $Credential.Password
        $options.CredentialsProvider = { return $gitCredential }
    }

    $cancelClone = $false
    $options.OnProgress = {
        param(
            $Output
        )

        Write-Verbose -Message $Output
        return -not $cancelClone
    }

    $lastUpdated = Get-Date
    $options.OnTransferProgress = {
        param(
            [LibGit2Sharp.TransferProgress]
            $TransferProgress
        )

        # Only update progress every 1/10th of a second, otherwise updating the progress takes longer than the clone
        if ( ((Get-Date) - $lastUpdated).TotalMilliseconds -lt 100 ) {
            return $true
        }

        if ($ProgressPreference -ne 'SilentlyContinue') {
            $numBytes = $TransferProgress.ReceivedBytes
            if ( $numBytes -lt 1kb ) {
                $unit = 'B'
            } elseif ( $numBytes -lt 1mb ) {
                $unit = 'KB'
                $numBytes = $numBytes / 1kb
            } elseif ( $numBytes -lt 1gb ) {
                $unit = 'MB'
                $numBytes = $numBytes / 1mb
            } elseif ( $numBytes -lt 1tb ) {
                $unit = 'GB'
                $numBytes = $numBytes / 1gb
            } elseif ( $numBytes -lt 1pb ) {
                $unit = 'TB'
                $numBytes = $numBytes / 1tb
            } else {
                $unit = 'PB'
                $numBytes = $numBytes / 1pb
            }

            Write-Progress -Activity ('Cloning {0} -> {1}' -f $Source, $DestinationPath) `
                -Status ('{0}/{1} objects, {2:n0} {3}' -f $TransferProgress.ReceivedObjects, $TransferProgress.TotalObjects, $numBytes, $unit) `
                -PercentComplete (($TransferProgress.ReceivedObjects / $TransferProgress.TotalObjects) * 100)
            Set-Variable -Name 'lastUpdated' -Value (Get-Date) -Scope 1
        }
        return (-not $cancelClone)
    }

    try {
        $cloneCompleted = $false
        $gitPath = [LibGit2Sharp.Repository]::Clone($Source, $DestinationPath, $options)
        if ( $PassThru -and $gitPath ) {
            Get-Item -Path $gitPath -Force
        }
        $cloneCompleted = $true
    } finally {
        if ( -not $cloneCompleted ) {
            $cancelClone = $true
        }
    }
}