src/Util/GitParse.ps1
|
# GitParse — Función standalone que ejecuta git status + parse y devuelve un # hashtable con los datos del repo. NO depende de [Repo] ni de [GitService] — # eso permite que viva adentro de un runspace paralelo (ForEach-Object -Parallel) # donde las clases del script padre no son visibles (limitación PS 7). # # El caller (RepoDiscoveryService.DiscoverParallel) recibe los hashtables y # los mapea a [Repo] en el parent runspace, donde la clase sí está cargada. function Get-RepoStateData { param( [Parameter(Mandatory)] [string] $Path ) $result = @{ Path = $Path Name = Split-Path $Path -Leaf Status = 'nogit' Branch = '—' IsOnMainBranch = $false Ahead = 0 Behind = 0 Modified = 0 Added = 0 Deleted = 0 Untracked = 0 StashCount = 0 LastCommit = $null # @{ Hash, Message, Author, Date } o $null } $result.Id = $result.Name.ToLower() -replace '[^a-z0-9]', '-' # ¿Es repo git? $gitMarker = Join-Path $Path '.git' if (-not (Test-Path -LiteralPath $gitMarker)) { return $result # nogit, returna lo que ya está } # Cambiamos al directorio para no pasar -C (más portable). El runspace # tiene su propia working dir, no contamina al parent. $prev = Get-Location try { Set-Location -LiteralPath $Path -ErrorAction Stop } catch { return $result } try { # 1) Status porcelain v2 con --branch $output = & git status --porcelain=v2 --branch 2>$null if ($LASTEXITCODE -ne 0 -or $null -eq $output) { return $result } $hasConflict = $false foreach ($line in $output) { if ($line.StartsWith('# branch.head ')) { $result.Branch = $line.Substring(14).Trim() $result.IsOnMainBranch = ($result.Branch -in @('main', 'master', 'develop')) } elseif ($line.StartsWith('# branch.ab ')) { $parts = $line.Substring(12).Trim() -split '\s+' if ($parts.Count -eq 2) { $result.Ahead = [int]($parts[0].TrimStart('+')) $result.Behind = [int]($parts[1].TrimStart('-')) } } elseif ($line.StartsWith('1 ') -or $line.StartsWith('2 ')) { $xy = $line.Substring(2, 2) $x = $xy[0]; $y = $xy[1] if ($x -ne '.') { switch ($x) { 'M' { $result.Modified++ } 'A' { $result.Added++ } 'D' { $result.Deleted++ } 'R' { $result.Modified++ } 'C' { $result.Added++ } default { $result.Modified++ } } } if ($y -ne '.') { switch ($y) { 'M' { $result.Modified++ } 'A' { $result.Added++ } 'D' { $result.Deleted++ } 'R' { $result.Modified++ } default { $result.Modified++ } } } } elseif ($line.StartsWith('? ')) { $result.Untracked++ } elseif ($line.StartsWith('u ')) { $hasConflict = $true } } # 2) Stashes $stashOutput = & git stash list 2>$null if ($LASTEXITCODE -eq 0 -and $stashOutput) { $result.StashCount = @($stashOutput).Count } # 3) Last commit $lastLine = & git log -1 --format='%h%x09%an%x09%cr%x09%s' 2>$null if ($LASTEXITCODE -eq 0 -and $lastLine) { # `$lastLine` puede ser string o array; normalizamos a string. $first = if ($lastLine -is [array]) { $lastLine[0] } else { $lastLine } $parts = ($first -split "`t", 4) if ($parts.Count -eq 4) { $result.LastCommit = @{ Hash = $parts[0] Author = $parts[1] Date = $parts[2] Message = $parts[3] } } } # 4) Status derivado (mismo orden que GitService.GetRepoState) $dirty = $result.Modified + $result.Added + $result.Deleted + $result.Untracked if ($hasConflict) { $result.Status = 'conflict' } elseif ($dirty -gt 0) { $result.Status = 'dirty' } elseif ($result.Ahead -gt 0) { $result.Status = 'unpushed' } elseif ($result.Behind -gt 0) { $result.Status = 'behind' } else { $result.Status = 'clean' } } finally { Set-Location -LiteralPath $prev -ErrorAction SilentlyContinue } return $result } |