arcade-services icon indicating copy to clipboard operation
arcade-services copied to clipboard

darc vmr diff should not clone the comparison repo

Open mmitche opened this issue 5 months ago • 4 comments

darc vmr diff does not need to clone the repos to work. It can use a combination of the local git-tree object hashes as well as the REST APIs to quickly generate a diff. There are REST API equivalents of the local tree hash.

Here is a local version:

param (
    [Parameter(Mandatory=$true)]
    [string]$Dir1,
    [string]$Dir1Ref = "HEAD",

    [Parameter(Mandatory=$true)]
    [string]$Dir2,
    [string]$Dir2Ref = "HEAD",

    [Parameter(Mandatory=$true)]
    [string]$GlobPattern
)

function Get-GitTreeEntries {
    param (
        [string]$RepoPath,
        [string]$TreePath,
        [string]$Ref
    )

    Push-Location $(Join-Path $RepoPath $TreePath)

    Write-Verbose "Listing tree at $RepoPath -> $Ref"

    $entries = git ls-tree "$Ref" 2>$null | ForEach-Object {
        if ($_ -match '(\d+)\s+(blob|tree)\s+([a-f0-9]+)\s+(.*)') {
            [PSCustomObject]@{
                Mode = $matches[1]
                Type = $matches[2]
                Hash = $matches[3]
                Path = if ($TreePath) { "$TreePath/$($matches[4])" } else { $matches[4] }
            }
        }
    }

    Pop-Location
    return $entries
}

function Compare-GitTrees {
    param (
        [string]$Repo1,
        [string]$Repo2,
        [string]$Ref1,
        [string]$Ref2,
        [string]$TreePath
    )

    Write-Verbose "Comparing tree: $TreePath"

    $entries1 = Get-GitTreeEntries -RepoPath $Repo1 -TreePath $TreePath -Ref $Ref1
    $entries2 = Get-GitTreeEntries -RepoPath $Repo2 -TreePath $TreePath -Ref $Ref2

    $map1 = @{}
    $entries1 | ForEach-Object { $map1[$_.Path] = $_ }

    $map2 = @{}
    $entries2 | ForEach-Object { $map2[$_.Path] = $_ }

    $allPaths = ($map1.Keys + $map2.Keys) | Sort-Object -Unique

    foreach ($path in $allPaths) {
        if ($path -notlike $GlobPattern) {
            Write-Verbose "Skipped $path"
        }
        $e1 = $map1[$path]
        $e2 = $map2[$path]

        if ($e1 -and $e2) {
            if ($e1.Type -eq 'tree' -and $e2.Type -eq 'tree') {
                if ($e1.Hash -ne $e2.Hash) {
                    Write-Verbose "Tree differs: $path (hash1=$($e1.Hash), hash2=$($e2.Hash))"
                    Compare-GitTrees -Repo1 $Repo1 -Ref1 $Ref1 -Repo2 $Repo2 -Ref2 $Ref2 -TreePath $path
                } else {
                    Write-Verbose "Tree matches: $path (hash=$($e1.Hash))"
                }
            } elseif ($e1.Type -eq 'blob' -and $e2.Type -eq 'blob') {
                if ($e1.Hash -ne $e2.Hash) {
                    $file1 = Join-Path $Repo1 $path
                    $file2 = Join-Path $Repo2 $path
                    Write-Host "`nDiff for: $path"
                    Write-Verbose "Blob differs: $path"
                    git diff --no-index --ignore-cr-at-eol $file1 $file2
                } else {
                    Write-Verbose "Blob matches: $path (hash=$($e1.Hash))"
                }
            }
        } elseif ($e1 -and $e1.Type -eq 'blob' -and $path -like $GlobPattern) {
            Write-Host "`nOnly in ${Repo1}: $path"
            Write-Verbose "Blob only in Repo1: $path"
        } elseif ($e2 -and $e2.Type -eq 'blob' -and $path -like $GlobPattern) {
            Write-Host "`nOnly in ${Repo2}: $path"
            Write-Verbose "Blob only in Repo2: $path"
        }
    }

    Write-Verbose "Finished comparing tree: $TreePath"
}

Compare-GitTrees -Repo1 $Dir1 -Ref1 $Dir1Ref -Repo2 $Dir2 -Ref2 $Dir2Ref -TreePath ""

mmitche avatar Jul 29 '25 23:07 mmitche