根据日期撤销TFS签出

工作笔记 工作记录
📅 2026-01-27 10:46 🔄 2026-01-27 10:46 👤 admin

param(
    [datetime] $TargetDate = ([datetime]"2025-09-15"),
    [switch] $DoIt = $false,
    [string] $TfExePath = "tf",
    [string] $Root = ".",
    [string[]] $ExcludeExtensions = @(".csproj")  # 可传入多个,例如: -ExcludeExtensions ".csproj",".vbproj"
)

$target = $TargetDate.Date
Write-Host "Root: $Root"
Write-Host "TargetDate: $target"
Write-Host "Dry-run: $(-not $DoIt)"
Write-Host "TfExePath: $TfExePath"
Write-Host "Exclude extensions: $($ExcludeExtensions -join ', ')"
Write-Host ""

try {
    $items = Get-ChildItem -Path $Root -Recurse -File -ErrorAction Stop
} catch {
    Write-Error "Scan failed: $_"
    exit 1
}

# 过滤:按 LastWriteTime 且排除指定扩展(不区分大小写)
$excludeSet = $ExcludeExtensions | ForEach-Object { $_.ToLowerInvariant() }
$matched = $items |
    Where-Object {
        ($_.LastWriteTime.Date -eq $target) -and
        (-not ($excludeSet -contains ($_.Extension.ToLowerInvariant())))
    } |
    Select-Object -ExpandProperty FullName

if (-not $matched -or $matched.Count -eq 0) {
    Write-Host "No files found with LastWriteTime = $target (after excluding extensions)." -ForegroundColor Yellow
    exit 0
}

Write-Host "Found $($matched.Count) files. Sample:" -ForegroundColor Green
$matched | Select-Object -First 20 | ForEach-Object { Write-Host $_ }

if (-not $DoIt) {
    Write-Host ""
    Write-Host "Dry-run mode. To execute undo, re-run with -DoIt." -ForegroundColor Yellow
    Write-Host "Example (dry-run):"
    Write-Host "  powershell -ExecutionPolicy Bypass -File .\scripts\undo-by-lastwritetime.ps1 -TargetDate 2025-09-15 -Root .."
    Write-Host "Example (execute):"
    Write-Host "  powershell -ExecutionPolicy Bypass -File .\scripts\undo-by-lastwritetime.ps1 -TargetDate 2025-09-15 -Root .. -DoIt"
    Write-Host "如果要排除更多扩展: -ExcludeExtensions \".csproj\",\".vbproj\""
    exit 0
}

# 验证 tf.exe 可用
try {
    Get-Command $TfExePath -ErrorAction Stop | Out-Null
} catch {
    Write-Error "tf.exe not found. Run in Developer Command Prompt for VS or provide -TfExePath."
    exit 1
}

$counts = @{Undone = 0; NoPending = 0; Failed = 0}

foreach ($f in $matched) {
    Write-Host "Undoing: $f" -NoNewline
    try {
        $out = & $TfExePath undo "$f" /noprompt 2>&1
        if ($LASTEXITCODE -eq 0 -and ($out -match "Undo" -or $out -match "undo successful" -or $out -match "Undoed")) {
            Write-Host " -> Undone" -ForegroundColor Green
            $counts.Undone++
        } elseif ($out -match "No pending changes" -or $out -match "no pending changes" -or $out -match "No changes found") {
            Write-Host " -> No pending changes" -ForegroundColor Yellow
            $counts.NoPending++
        } else {
            Write-Host " -> Result: $out" -ForegroundColor Cyan
            $counts.Failed++
        }
    } catch {
        Write-Host " -> Failed: $_" -ForegroundColor Red
        $counts.Failed++
    }
}

Write-Host ""
Write-Host ("Summary: Undone: {0}`nNoPending: {1}`nFailed: {2}" -f $counts.Undone, $counts.NoPending, $counts.Failed)