845 lines
31 KiB
PowerShell
845 lines
31 KiB
PowerShell
<#
|
||
.SYNOPSIS
|
||
Git 管理工具 — 查看状态、备份节点、管理分支
|
||
.DESCRIPTION
|
||
通用 Git 仓库管理脚本,适用于任何项目目录。
|
||
自动以脚本所在目录作为仓库根目录。
|
||
#>
|
||
param(
|
||
[Parameter(Position=0)]
|
||
[string]$Action = ""
|
||
)
|
||
|
||
$ErrorActionPreference = "Stop"
|
||
|
||
# 统一设置 UTF-8 编码,防止中文乱码
|
||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
|
||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||
$env:LANG = "zh_CN.UTF-8"
|
||
|
||
Set-Location $PSScriptRoot
|
||
|
||
# Verify this is a git repo
|
||
$isRepo = Test-Path (Join-Path $PSScriptRoot ".git")
|
||
if (-not $isRepo) {
|
||
Write-Host ""
|
||
Write-Host " ╔══════════════════════════════════════╗" -ForegroundColor Cyan
|
||
Write-Host " ║ Git 管理工具 ║" -ForegroundColor Cyan
|
||
Write-Host " ╚══════════════════════════════════════╝" -ForegroundColor Cyan
|
||
Write-Host ""
|
||
Write-Host " ● 当前目录尚未初始化 Git 仓库" -ForegroundColor Yellow
|
||
Write-Host " ──────────────────────────────"
|
||
Write-Host " 路径: $PSScriptRoot" -ForegroundColor White
|
||
Write-Host ""
|
||
$confirm = Read-Host " 是否立即初始化并创建初始备份? (y/n)"
|
||
if ($confirm -ne 'y') {
|
||
Write-Host ""
|
||
Write-Host " 已取消。请先运行: git init" -ForegroundColor DarkGray
|
||
exit 0
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host " 正在初始化仓库..." -ForegroundColor Yellow
|
||
git init
|
||
|
||
Write-Host " 正在配置本地用户身份..." -ForegroundColor Yellow
|
||
git config --local user.name "local"
|
||
git config --local user.email "local@localhost"
|
||
|
||
Write-Host " 正在创建初始备份提交..." -ForegroundColor Yellow
|
||
git add -A
|
||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm"
|
||
$commitMsg = "[初始备份] $timestamp"
|
||
$commitResult = git commit -m $commitMsg 2>&1
|
||
if ($LASTEXITCODE -ne 0) {
|
||
Write-Host ""
|
||
Write-Host " ✗ 初始提交失败!" -ForegroundColor Red
|
||
Write-Host $commitResult -ForegroundColor Red
|
||
exit 1
|
||
}
|
||
|
||
$newHash = try { git rev-parse --short HEAD 2>$null } catch { "unknown" }
|
||
Write-Host ""
|
||
Write-Host " ✓ Git 仓库已初始化并完成初始备份!" -ForegroundColor Green
|
||
Write-Host " 提交: $newHash" -ForegroundColor White
|
||
Write-Host " 信息: $commitMsg" -ForegroundColor White
|
||
Write-Host ""
|
||
Read-Host " 按 Enter 继续进入菜单"
|
||
}
|
||
|
||
function Write-Banner {
|
||
Write-Host ""
|
||
Write-Host " ╔══════════════════════════════════════╗" -ForegroundColor Cyan
|
||
Write-Host " ║ Git 管理工具 ║" -ForegroundColor Cyan
|
||
Write-Host " ╚══════════════════════════════════════╝" -ForegroundColor Cyan
|
||
Write-Host ""
|
||
}
|
||
|
||
function Show-FullStatus {
|
||
Write-Host " ● 完整状态" -ForegroundColor Yellow
|
||
Write-Host " ────────────"
|
||
|
||
# Repo path
|
||
$repoPath = (Get-Location).Path
|
||
$repoName = Split-Path $repoPath -Leaf
|
||
Write-Host " 项目: " -NoNewline; Write-Host $repoName -ForegroundColor White
|
||
Write-Host " 仓库: " -NoNewline; Write-Host $repoPath -ForegroundColor White
|
||
|
||
# Branches
|
||
$branches = git branch 2>$null
|
||
if ($branches) {
|
||
Write-Host " 分支:"
|
||
foreach ($b in $branches) {
|
||
$b = $b.Trim()
|
||
if ($b.StartsWith("*")) { Write-Host " $b" -ForegroundColor Green }
|
||
else { Write-Host " $b" }
|
||
}
|
||
} else { Write-Host " 分支: 无" }
|
||
|
||
# Remote
|
||
$remotes = git remote 2>$null
|
||
if ($remotes) {
|
||
foreach ($r in $remotes) { Write-Host " 远程: $r" }
|
||
} else { Write-Host " 远程: 无(纯本地仓库)" -ForegroundColor DarkGray }
|
||
|
||
# Last commit
|
||
$lastCommit = try { git log -1 --oneline 2>$null } catch { $null }
|
||
if ($lastCommit) { Write-Host " 最新: " -NoNewline; Write-Host $lastCommit -ForegroundColor White }
|
||
else { Write-Host " 提交: 无(空仓库)" -ForegroundColor DarkGray }
|
||
|
||
# Commit count
|
||
$commitCount = try { git rev-list --count HEAD 2>$null } catch { $null }
|
||
if ($commitCount) { Write-Host (" 提交数: {0}" -f $commitCount) }
|
||
|
||
Write-Host ""
|
||
|
||
# Files
|
||
$tracked = (git ls-files 2>$null).Count
|
||
Write-Host " 跟踪: $tracked 个文件"
|
||
|
||
# Untracked (excluding .git, bin, obj, Logs)
|
||
$allFiles = @(Get-ChildItem -Recurse -File -ErrorAction SilentlyContinue | Where-Object {
|
||
$_.FullName -notmatch '\\(\.git|bin|obj|Logs)\\?' -and
|
||
$_.FullName -notmatch '\\node_modules\\?' -and
|
||
$_.FullName -notmatch '\\__pycache__\\?'
|
||
}).Count
|
||
$untracked = $allFiles - $tracked
|
||
Write-Host " 未跟踪: $untracked 个文件"
|
||
|
||
# Repo size
|
||
$gitSize = (Get-ChildItem -Path .git -Recurse -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
|
||
Write-Host (" 仓库: {0:N0} KB (.git)" -f ($gitSize / 1KB))
|
||
|
||
# Git objects
|
||
$objInfo = git count-objects -v 2>$null
|
||
$objCount = ($objInfo | Select-String "count:" | Out-String).Trim() -replace '\D',''
|
||
$objSize = ($objInfo | Select-String "size:" | Out-String).Trim() -replace '.*:\s*',''
|
||
if ($objCount) {
|
||
Write-Host (" 对象: {0} 个, {1}" -f $objCount, $objSize)
|
||
}
|
||
|
||
# Uncommitted changes
|
||
$changes = git status --short 2>$null
|
||
if ($changes) {
|
||
Write-Host ""
|
||
Write-Host " ● 未提交变更" -ForegroundColor Yellow
|
||
Write-Host " ────────────"
|
||
foreach ($c in $changes) { Write-Host " $c" }
|
||
}
|
||
|
||
# Stashes
|
||
$stashes = git stash list 2>$null
|
||
if ($stashes) {
|
||
Write-Host ""
|
||
Write-Host " ● 暂存 (Stash)" -ForegroundColor Yellow
|
||
Write-Host " ──────────────"
|
||
foreach ($s in $stashes) { Write-Host " $s" }
|
||
}
|
||
}
|
||
|
||
function Show-Branches {
|
||
Write-Host ""
|
||
Write-Host " ● 分支列表" -ForegroundColor Yellow
|
||
Write-Host " ──────────"
|
||
|
||
$branches = git branch 2>$null
|
||
if ($branches) {
|
||
Write-Host ""
|
||
Write-Host " 本地分支:" -ForegroundColor Cyan
|
||
foreach ($b in $branches) {
|
||
$b = $b.Trim()
|
||
if ($b.StartsWith("*")) {
|
||
Write-Host " ★ " -NoNewline -ForegroundColor Green
|
||
Write-Host ($b.Substring(1).Trim()) -ForegroundColor Green
|
||
} else {
|
||
Write-Host " $b"
|
||
}
|
||
}
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host " 分支提交:" -ForegroundColor Cyan
|
||
$allBranches = git branch 2>$null
|
||
foreach ($b in $allBranches) {
|
||
$b = $b.Trim().TrimStart('*').Trim()
|
||
if ($b) {
|
||
$count = try { git rev-list --count $b 2>$null } catch { "0" }
|
||
$last = try { git log $b -1 --oneline 2>$null } catch { "" }
|
||
Write-Host " $b" -NoNewline
|
||
Write-Host (" ({0} commits) " -f $count) -NoNewline -ForegroundColor DarkGray
|
||
Write-Host $last -ForegroundColor DarkGray
|
||
}
|
||
}
|
||
}
|
||
|
||
function Backup-Current {
|
||
param([string]$Message)
|
||
|
||
if (-not $Message) {
|
||
$Message = Read-Host " 输入备份信息(描述当前状态)"
|
||
if (-not $Message) { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host " ● 备份当前状态" -ForegroundColor Yellow
|
||
Write-Host " ──────────────"
|
||
|
||
$hasChanges = git status --porcelain 2>$null
|
||
if (-not $hasChanges) {
|
||
Write-Host " 工作区干净,无需备份。" -ForegroundColor DarkGray
|
||
return
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host " 将要备份的变更:" -ForegroundColor Cyan
|
||
git status --short
|
||
|
||
Write-Host ""
|
||
$confirm = Read-Host " 确认备份? (y/n)"
|
||
if ($confirm -ne 'y') { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
|
||
# 确保本地仓库有用户身份(纯本地备份,自动设置默认值)
|
||
$gitUserName = try { git config user.name 2>$null } catch { $null }
|
||
$gitUserEmail = try { git config user.email 2>$null } catch { $null }
|
||
if (-not $gitUserName) { git config --local user.name "local" }
|
||
if (-not $gitUserEmail) { git config --local user.email "local@localhost" }
|
||
|
||
git add -A
|
||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm"
|
||
$commitMsg = "[备份] $timestamp - $Message"
|
||
$commitResult = git commit -m $commitMsg 2>&1
|
||
if ($LASTEXITCODE -ne 0) {
|
||
Write-Host ""
|
||
Write-Host " ✗ 备份失败!" -ForegroundColor Red
|
||
Write-Host $commitResult -ForegroundColor Red
|
||
return
|
||
}
|
||
|
||
$newHash = try { git rev-parse --short HEAD 2>$null } catch { "unknown" }
|
||
$fileCount = try { @(git diff-tree --no-commit-id --name-only -r HEAD 2>$null).Count } catch { 0 }
|
||
Write-Host ""
|
||
Write-Host " ✓ 备份完成!" -ForegroundColor Green
|
||
Write-Host " 提交: $newHash" -ForegroundColor White
|
||
Write-Host " 信息: $commitMsg" -ForegroundColor White
|
||
Write-Host " 文件: $fileCount 个文件已保存"
|
||
}
|
||
|
||
function New-Branch {
|
||
param([string]$Name)
|
||
|
||
if (-not $Name) { $Name = Read-Host " 输入新分支名称" }
|
||
if (-not $Name) { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
|
||
$hasChanges = git status --porcelain 2>$null
|
||
if ($hasChanges) {
|
||
Write-Host " 当前有未提交变更,建议先备份。" -ForegroundColor Yellow
|
||
$confirm = Read-Host " 自动备份后创建分支? (y/n)"
|
||
if ($confirm -eq 'y') { Backup-Current -Message "创建分支 $Name 前的自动备份" }
|
||
else { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
}
|
||
|
||
git checkout -b $Name
|
||
Write-Host " ✓ 已创建并切换到分支: $Name" -ForegroundColor Green
|
||
}
|
||
|
||
function Switch-Branch {
|
||
param([string]$Name)
|
||
|
||
if (-not $Name) {
|
||
Write-Host " 可用分支:" -ForegroundColor Cyan
|
||
git branch
|
||
$Name = Read-Host " 输入要切换的分支名"
|
||
}
|
||
if (-not $Name) { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
|
||
$hasChanges = git status --porcelain 2>$null
|
||
if ($hasChanges) {
|
||
Write-Host " 当前有未提交变更!" -ForegroundColor Yellow
|
||
$confirm = Read-Host " 自动备份后切换? (y/n)"
|
||
if ($confirm -eq 'y') { Backup-Current -Message "切换到 $Name 前的自动备份" }
|
||
else { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
}
|
||
|
||
git checkout $Name
|
||
Write-Host " ✓ 已切换到分支: $Name" -ForegroundColor Green
|
||
}
|
||
|
||
function Merge-Branch {
|
||
param([string]$Source)
|
||
|
||
if (-not $Source) {
|
||
Write-Host " 可用分支:" -ForegroundColor Cyan
|
||
git branch
|
||
$Source = Read-Host " 输入要合并的源分支名"
|
||
}
|
||
if (-not $Source) { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
|
||
$currentBranch = git rev-parse --abbrev-ref HEAD
|
||
Write-Host " 将合并 " -NoNewline
|
||
Write-Host $Source -NoNewline -ForegroundColor Yellow
|
||
Write-Host " → " -NoNewline
|
||
Write-Host $currentBranch -ForegroundColor Yellow
|
||
|
||
$hasChanges = git status --porcelain 2>$null
|
||
if ($hasChanges) {
|
||
Write-Host " 当前有未提交变更!" -ForegroundColor Yellow
|
||
$confirm = Read-Host " 自动备份后继续? (y/n)"
|
||
if ($confirm -eq 'y') { Backup-Current -Message "合并 $Source 前的自动备份" }
|
||
else { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
}
|
||
|
||
$result = git merge $Source 2>&1
|
||
if ($LASTEXITCODE -ne 0) {
|
||
Write-Host " ✗ 合并冲突! 请手动解决。" -ForegroundColor Red
|
||
Write-Host ""
|
||
Write-Host " 冲突文件:" -ForegroundColor Yellow
|
||
git diff --name-only --diff-filter=U
|
||
Write-Host ""
|
||
Write-Host " 解决后运行: git add <文件> ; git commit" -ForegroundColor Cyan
|
||
} else {
|
||
Write-Host " ✓ 合并成功!" -ForegroundColor Green
|
||
}
|
||
}
|
||
|
||
function Delete-Branch {
|
||
param([string]$Name)
|
||
|
||
if (-not $Name) {
|
||
Write-Host " 可用分支:" -ForegroundColor Cyan
|
||
git branch
|
||
$Name = Read-Host " 输入要删除的分支名"
|
||
}
|
||
if (-not $Name) { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
|
||
$currentBranch = git rev-parse --abbrev-ref HEAD
|
||
if ($Name -eq $currentBranch) {
|
||
Write-Host " 不能删除当前所在分支!" -ForegroundColor Red
|
||
return
|
||
}
|
||
|
||
$unmerged = try { git log $currentBranch..$Name --oneline 2>$null } catch { $null }
|
||
if ($unmerged) {
|
||
Write-Host " ⚠ 分支 $Name 有未合并的提交:" -ForegroundColor Yellow
|
||
Write-Host $unmerged
|
||
$confirm = Read-Host " 强制删除? (y/n)"
|
||
if ($confirm -eq 'y') { git branch -D $Name }
|
||
else { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
} else {
|
||
git branch -d $Name
|
||
}
|
||
|
||
Write-Host " ✓ 已删除分支: $Name" -ForegroundColor Green
|
||
}
|
||
|
||
function Reset-Git {
|
||
Clear-Host
|
||
Write-Banner
|
||
Write-Host " ● 重置 Git 仓库" -ForegroundColor Red
|
||
Write-Host " ────────────────"
|
||
Write-Host ""
|
||
Write-Host " ⚠ 警告:此操作将删除所有 Git 历史记录!" -ForegroundColor Yellow
|
||
Write-Host " ⚠ 当前所有分支、提交、备份节点将永久丢失!" -ForegroundColor Yellow
|
||
Write-Host ""
|
||
Write-Host " 操作完成后将:" -ForegroundColor Cyan
|
||
Write-Host " 1. 删除 .git 目录(清除全部历史)"
|
||
Write-Host " 2. 重新执行 git init"
|
||
Write-Host " 3. 自动创建初始备份提交"
|
||
Write-Host ""
|
||
Write-Host " 如果确认,请输入 " -NoNewline
|
||
Write-Host "yes" -NoNewline -ForegroundColor Red
|
||
Write-Host "(其他任何输入均取消)"
|
||
Write-Host ""
|
||
$confirm = Read-Host " 请输入确认"
|
||
if ($confirm -ne 'yes') {
|
||
Write-Host ""
|
||
Write-Host " 已取消,Git 历史未改变。" -ForegroundColor Green
|
||
return
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host " 正在删除 .git 目录..." -ForegroundColor Yellow
|
||
Remove-Item -Recurse -Force (Join-Path $PSScriptRoot ".git")
|
||
|
||
Write-Host " 正在初始化新仓库..." -ForegroundColor Yellow
|
||
git init
|
||
|
||
Write-Host " 正在配置本地用户身份..." -ForegroundColor Yellow
|
||
git config --local user.name "local"
|
||
git config --local user.email "local@localhost"
|
||
|
||
Write-Host " 正在创建初始备份提交..." -ForegroundColor Yellow
|
||
git add -A
|
||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm"
|
||
$commitMsg = "[初始备份] $timestamp"
|
||
$commitResult = git commit -m $commitMsg 2>&1
|
||
if ($LASTEXITCODE -ne 0) {
|
||
Write-Host ""
|
||
Write-Host " ✗ 初始提交失败!" -ForegroundColor Red
|
||
Write-Host $commitResult -ForegroundColor Red
|
||
return
|
||
}
|
||
|
||
$newHash = try { git rev-parse --short HEAD 2>$null } catch { "unknown" }
|
||
Write-Host ""
|
||
Write-Host " ✓ Git 仓库已重置并完成初始备份!" -ForegroundColor Green
|
||
Write-Host " 提交: $newHash" -ForegroundColor White
|
||
Write-Host " 信息: $commitMsg" -ForegroundColor White
|
||
}
|
||
|
||
function Show-Log {
|
||
Write-Host ""
|
||
Write-Host " ● 提交历史(最近 20 条)" -ForegroundColor Yellow
|
||
Write-Host " ────────────────────────"
|
||
Write-Host ""
|
||
git log --oneline --graph --decorate -20 --all
|
||
}
|
||
|
||
# ==================== 恢复相关函数 ====================
|
||
|
||
function Show-RecentCommits {
|
||
param([int]$Count = 15)
|
||
Write-Host " 最近 $Count 条提交:" -ForegroundColor Cyan
|
||
Write-Host " ────────────────"
|
||
$i = 0
|
||
$commits = git log --pretty=format:"%h|%ad|%s" --date=format:"%Y-%m-%d %H:%M" -$Count 2>$null
|
||
$list = @()
|
||
foreach ($line in $commits) {
|
||
$parts = $line -split '\|', 3
|
||
$i++
|
||
$list += [PSCustomObject]@{
|
||
Index = $i
|
||
Hash = $parts[0]
|
||
Date = $parts[1]
|
||
Subject = $parts[2]
|
||
}
|
||
$idxStr = "{0,2}" -f $i
|
||
Write-Host " [$idxStr] " -NoNewline -ForegroundColor Yellow
|
||
Write-Host $parts[0] -NoNewline -ForegroundColor Green
|
||
Write-Host " $($parts[1]) " -NoNewline -ForegroundColor DarkGray
|
||
Write-Host $parts[2] -ForegroundColor White
|
||
}
|
||
return ,$list
|
||
}
|
||
|
||
function Restore-Last {
|
||
Write-Host ""
|
||
Write-Host " ● 恢复到上一次备份(丢弃所有未提交修改)" -ForegroundColor Yellow
|
||
Write-Host " ─────────────────────────────────────────"
|
||
|
||
$hasChanges = git status --porcelain 2>$null
|
||
if (-not $hasChanges) {
|
||
Write-Host " 工作区干净,无需恢复。" -ForegroundColor DarkGray
|
||
return
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host " 当前未提交变更:" -ForegroundColor Cyan
|
||
git status --short
|
||
Write-Host ""
|
||
Write-Host " ⚠ 执行后所有未提交修改将丢失!" -ForegroundColor Red
|
||
$confirm = Read-Host " 确认恢复到 HEAD? (y/n)"
|
||
if ($confirm -ne 'y') { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
|
||
git reset --hard HEAD
|
||
git clean -fd
|
||
Write-Host ""
|
||
Write-Host " ✓ 已恢复到 HEAD!" -ForegroundColor Green
|
||
$head = try { git log -1 --oneline 2>$null } catch { "" }
|
||
Write-Host " 当前: $head" -ForegroundColor White
|
||
}
|
||
|
||
function Restore-Commit {
|
||
param([string]$Target)
|
||
|
||
Write-Host ""
|
||
Write-Host " ● 恢复到指定提交" -ForegroundColor Yellow
|
||
Write-Host " ────────────────"
|
||
Write-Host ""
|
||
$list = Show-RecentCommits -Count 20
|
||
|
||
if (-not $Target) {
|
||
Write-Host ""
|
||
$sel = Read-Host " 输入编号或 commit hash(留空取消)"
|
||
if (-not $sel) { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
if ($sel -match '^\d+$') {
|
||
$idx = [int]$sel
|
||
$item = $list | Where-Object { $_.Index -eq $idx } | Select-Object -First 1
|
||
if (-not $item) { Write-Host " 无效编号" -ForegroundColor Red; return }
|
||
$Target = $item.Hash
|
||
} else {
|
||
$Target = $sel
|
||
}
|
||
}
|
||
|
||
# 验证提交存在
|
||
$exists = git rev-parse --verify "$Target^{commit}" 2>$null
|
||
if ($LASTEXITCODE -ne 0) {
|
||
Write-Host " ✗ 提交不存在: $Target" -ForegroundColor Red
|
||
return
|
||
}
|
||
$shortHash = git rev-parse --short $Target
|
||
$subject = git log -1 --pretty=format:"%s" $Target
|
||
|
||
Write-Host ""
|
||
Write-Host " 目标提交: " -NoNewline
|
||
Write-Host $shortHash -NoNewline -ForegroundColor Green
|
||
Write-Host " $subject" -ForegroundColor White
|
||
Write-Host ""
|
||
Write-Host " 恢复模式:" -ForegroundColor Cyan
|
||
Write-Host " [1] 软恢复(仅移动 HEAD,保留所有修改在暂存区)"
|
||
Write-Host " [2] 混合恢复(移动 HEAD,保留修改在工作区,默认)"
|
||
Write-Host " [3] 硬恢复(彻底回退,丢弃之后所有修改) " -NoNewline
|
||
Write-Host "[危险]" -ForegroundColor Red
|
||
Write-Host " [4] 检出文件(只把目标提交的文件覆盖到工作区,不动历史)"
|
||
Write-Host " [0] 取消"
|
||
$mode = Read-Host " 请选择恢复模式"
|
||
|
||
switch ($mode) {
|
||
"1" {
|
||
git reset --soft $Target
|
||
Write-Host " ✓ 已软恢复到 $shortHash" -ForegroundColor Green
|
||
}
|
||
"2" {
|
||
git reset --mixed $Target
|
||
Write-Host " ✓ 已混合恢复到 $shortHash" -ForegroundColor Green
|
||
}
|
||
"3" {
|
||
Write-Host " ⚠ 此操作不可恢复!" -ForegroundColor Red
|
||
$sure = Read-Host " 请输入 yes 确认硬恢复"
|
||
if ($sure -ne 'yes') { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
$hasChanges = git status --porcelain 2>$null
|
||
if ($hasChanges) {
|
||
$bak = Read-Host " 执行前是否先备份当前状态? (y/n)"
|
||
if ($bak -eq 'y') { Backup-Current -Message "硬恢复到 $shortHash 前的自动备份" }
|
||
}
|
||
git reset --hard $Target
|
||
git clean -fd
|
||
Write-Host " ✓ 已硬恢复到 $shortHash" -ForegroundColor Green
|
||
}
|
||
"4" {
|
||
git checkout $Target -- .
|
||
Write-Host " ✓ 已检出 $shortHash 的全部文件到工作区" -ForegroundColor Green
|
||
Write-Host " 提示: 修改尚未提交,可用 [备份] 创建新提交" -ForegroundColor DarkGray
|
||
}
|
||
default { Write-Host " 已取消" -ForegroundColor Red }
|
||
}
|
||
}
|
||
|
||
function Restore-File {
|
||
Write-Host ""
|
||
Write-Host " ● 恢复指定文件" -ForegroundColor Yellow
|
||
Write-Host " ──────────────"
|
||
|
||
$file = Read-Host " 输入要恢复的文件路径(相对仓库根)"
|
||
if (-not $file) { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
|
||
Write-Host ""
|
||
Write-Host " [1] 从 HEAD 恢复(撤销该文件未提交修改)"
|
||
Write-Host " [2] 从指定提交恢复"
|
||
$m = Read-Host " 选择来源"
|
||
if ($m -eq "1") {
|
||
git checkout HEAD -- $file
|
||
} elseif ($m -eq "2") {
|
||
$list = Show-RecentCommits -Count 15
|
||
$sel = Read-Host " 输入编号或 commit hash"
|
||
if (-not $sel) { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
$target = $sel
|
||
if ($sel -match '^\d+$') {
|
||
$item = $list | Where-Object { $_.Index -eq [int]$sel } | Select-Object -First 1
|
||
if (-not $item) { Write-Host " 无效编号" -ForegroundColor Red; return }
|
||
$target = $item.Hash
|
||
}
|
||
git checkout $target -- $file
|
||
} else { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
|
||
if ($LASTEXITCODE -eq 0) {
|
||
Write-Host " ✓ 文件已恢复: $file" -ForegroundColor Green
|
||
} else {
|
||
Write-Host " ✗ 恢复失败" -ForegroundColor Red
|
||
}
|
||
}
|
||
|
||
function Undo-LastCommit {
|
||
Write-Host ""
|
||
Write-Host " ● 撤销最后一次提交(保留修改在工作区)" -ForegroundColor Yellow
|
||
Write-Host " ──────────────────────────────────────"
|
||
|
||
$count = try { [int](git rev-list --count HEAD 2>$null) } catch { 0 }
|
||
if ($count -lt 1) { Write-Host " 无提交可撤销" -ForegroundColor Red; return }
|
||
|
||
$last = git log -1 --oneline
|
||
Write-Host " 最后一次提交: " -NoNewline
|
||
Write-Host $last -ForegroundColor White
|
||
Write-Host ""
|
||
Write-Host " [1] 撤销提交但保留修改(git reset --soft HEAD~1)"
|
||
Write-Host " [2] 撤销提交并丢弃修改(git reset --hard HEAD~1) " -NoNewline
|
||
Write-Host "[危险]" -ForegroundColor Red
|
||
Write-Host " [0] 取消"
|
||
$m = Read-Host " 选择"
|
||
switch ($m) {
|
||
"1" {
|
||
git reset --soft HEAD~1
|
||
Write-Host " ✓ 已撤销提交,修改保留在暂存区。" -ForegroundColor Green
|
||
}
|
||
"2" {
|
||
$sure = Read-Host " 请输入 yes 确认丢弃"
|
||
if ($sure -ne 'yes') { Write-Host " 已取消" -ForegroundColor Red; return }
|
||
git reset --hard HEAD~1
|
||
Write-Host " ✓ 已撤销并丢弃修改。" -ForegroundColor Green
|
||
}
|
||
default { Write-Host " 已取消" -ForegroundColor Red }
|
||
}
|
||
}
|
||
|
||
# ==================== 子菜单 ====================
|
||
|
||
function Show-BranchMenu {
|
||
do {
|
||
Clear-Host
|
||
Write-Banner
|
||
Show-Branches
|
||
Write-Host ""
|
||
Write-Host " ● 分支操作菜单" -ForegroundColor Yellow
|
||
Write-Host " ──────────────"
|
||
Write-Host " [1] 创建新分支" -ForegroundColor White
|
||
Write-Host " [2] 切换分支" -ForegroundColor White
|
||
Write-Host " [3] 合并分支" -ForegroundColor White
|
||
Write-Host " [4] 删除分支" -ForegroundColor White
|
||
Write-Host " [5] 刷新列表" -ForegroundColor DarkGray
|
||
Write-Host " [0] 返回主菜单" -ForegroundColor DarkGray
|
||
Write-Host ""
|
||
$c = Read-Host " 请选择 [0-5]"
|
||
switch ($c) {
|
||
"1" { New-Branch; Read-Host "`n 按 Enter 继续" }
|
||
"2" { Switch-Branch; Read-Host "`n 按 Enter 继续" }
|
||
"3" { Merge-Branch; Read-Host "`n 按 Enter 继续" }
|
||
"4" { Delete-Branch; Read-Host "`n 按 Enter 继续" }
|
||
"5" { continue }
|
||
"0" { return }
|
||
default { Write-Host " 无效选择" -ForegroundColor Red; Start-Sleep -Seconds 1 }
|
||
}
|
||
} while ($true)
|
||
}
|
||
|
||
function Show-RestoreMenu {
|
||
do {
|
||
Clear-Host
|
||
Write-Banner
|
||
Write-Host " 当前分支: " -NoNewline
|
||
Write-Host (git rev-parse --abbrev-ref HEAD) -ForegroundColor Yellow
|
||
$head = try { git log -1 --oneline 2>$null } catch { "" }
|
||
Write-Host " 当前 HEAD: " -NoNewline; Write-Host $head -ForegroundColor White
|
||
$changes = git status --short 2>$null
|
||
if ($changes) {
|
||
Write-Host ""
|
||
Write-Host " 未提交变更:" -ForegroundColor Cyan
|
||
foreach ($l in $changes) { Write-Host " $l" }
|
||
}
|
||
Write-Host ""
|
||
Write-Host " ● 恢复操作菜单" -ForegroundColor Yellow
|
||
Write-Host " ──────────────"
|
||
Write-Host " [1] 恢复到上一次备份(丢弃未提交修改)" -ForegroundColor White
|
||
Write-Host " [2] 恢复到指定提交(软/混合/硬/检出文件)" -ForegroundColor White
|
||
Write-Host " [3] 恢复指定文件" -ForegroundColor White
|
||
Write-Host " [4] 撤销最后一次提交" -ForegroundColor White
|
||
Write-Host " [5] 查看提交历史" -ForegroundColor DarkGray
|
||
Write-Host " [0] 返回主菜单" -ForegroundColor DarkGray
|
||
Write-Host ""
|
||
$c = Read-Host " 请选择 [0-5]"
|
||
switch ($c) {
|
||
"1" { Restore-Last; Read-Host "`n 按 Enter 继续" }
|
||
"2" { Restore-Commit; Read-Host "`n 按 Enter 继续" }
|
||
"3" { Restore-File; Read-Host "`n 按 Enter 继续" }
|
||
"4" { Undo-LastCommit; Read-Host "`n 按 Enter 继续" }
|
||
"5" { Show-Log; Read-Host "`n 按 Enter 继续" }
|
||
"0" { return }
|
||
default { Write-Host " 无效选择" -ForegroundColor Red; Start-Sleep -Seconds 1 }
|
||
}
|
||
} while ($true)
|
||
}
|
||
|
||
# ==================== 交互菜单 ====================
|
||
|
||
function Show-Menu {
|
||
Show-FullStatus
|
||
Write-Host ""
|
||
Write-Host " ● 操作菜单" -ForegroundColor Yellow
|
||
Write-Host " ──────────"
|
||
Write-Host " [1] 备份当前状态(手动输入描述信息)" -ForegroundColor White
|
||
Write-Host " [2] 查看分支详情" -ForegroundColor White
|
||
Write-Host " [3] 分支操作(创建/切换/合并/删除) →" -ForegroundColor White
|
||
Write-Host " [4] 恢复操作(上一次/指定提交/文件/撤销)→" -ForegroundColor White
|
||
Write-Host " [5] 查看提交历史" -ForegroundColor White
|
||
Write-Host " [9] 重置 Git 仓库(清除全部历史)" -ForegroundColor DarkRed
|
||
Write-Host " [0] 退出" -ForegroundColor DarkGray
|
||
Write-Host ""
|
||
}
|
||
|
||
function Show-Help {
|
||
Write-Host "Git 管理工具 — 通用仓库管理脚本"
|
||
Write-Host ""
|
||
Write-Host "用法: .\git-manager.ps1 [操作] [参数]"
|
||
Write-Host ""
|
||
Write-Host "命令行操作:"
|
||
Write-Host " (无参数) 交互模式"
|
||
Write-Host " status 查看完整仓库状态"
|
||
Write-Host " branches 查看分支详情"
|
||
Write-Host " backup <信息> 备份当前状态"
|
||
Write-Host " new <分支名> 创建新分支"
|
||
Write-Host " switch <分支名> 切换分支"
|
||
Write-Host " merge <分支名> 合并分支"
|
||
Write-Host " delete <分支名> 删除分支"
|
||
Write-Host " restore 打开恢复子菜单"
|
||
Write-Host " restore-last 恢复到上一次备份(丢弃未提交修改)"
|
||
Write-Host " restore-commit <hash> 恢复到指定提交"
|
||
Write-Host " undo 撤销最后一次提交"
|
||
Write-Host " log 查看提交历史"
|
||
Write-Host " help 显示帮助"
|
||
Write-Host ""
|
||
Write-Host "示例:"
|
||
Write-Host " .\git-manager.ps1 status"
|
||
Write-Host " .\git-manager.ps1 backup '修复播放器bug,美化界面'"
|
||
Write-Host " .\git-manager.ps1 new feature-audio"
|
||
Write-Host " .\git-manager.ps1 restore-last"
|
||
Write-Host " .\git-manager.ps1 restore-commit a1b2c3d"
|
||
}
|
||
|
||
# ==================== 入口 ====================
|
||
|
||
# Normalize action
|
||
$action = $Action.ToLower().Trim()
|
||
|
||
if ($action -eq "" -or $action -eq "menu") {
|
||
# Interactive mode
|
||
do {
|
||
Clear-Host
|
||
Write-Banner
|
||
Show-Menu
|
||
$choice = Read-Host " 请选择操作 [0-9]"
|
||
|
||
switch ($choice) {
|
||
"1" {
|
||
Clear-Host
|
||
Write-Banner
|
||
Show-FullStatus
|
||
Backup-Current
|
||
Read-Host "`n 按 Enter 返回菜单"
|
||
}
|
||
"2" {
|
||
Clear-Host
|
||
Write-Banner
|
||
Show-Branches
|
||
Show-Log
|
||
Read-Host "`n 按 Enter 返回菜单"
|
||
}
|
||
"3" {
|
||
Show-BranchMenu
|
||
}
|
||
"4" {
|
||
Show-RestoreMenu
|
||
}
|
||
"5" {
|
||
Clear-Host
|
||
Write-Banner
|
||
Show-Log
|
||
Read-Host "`n 按 Enter 返回菜单"
|
||
}
|
||
"9" {
|
||
Reset-Git
|
||
Read-Host "`n 按 Enter 返回菜单"
|
||
}
|
||
"0" {
|
||
Write-Host "`n 再见!" -ForegroundColor Cyan
|
||
break
|
||
}
|
||
default {
|
||
Write-Host "`n 无效选择,请重试。" -ForegroundColor Red
|
||
Start-Sleep -Seconds 1
|
||
}
|
||
}
|
||
} while ($choice -ne "0")
|
||
}
|
||
else {
|
||
# Command-line mode
|
||
switch ($action) {
|
||
"status" {
|
||
Write-Banner
|
||
Show-FullStatus
|
||
Show-Log
|
||
}
|
||
"branches" {
|
||
Write-Banner
|
||
Show-Branches
|
||
}
|
||
"backup" {
|
||
Write-Banner
|
||
$msg = $args -join " "
|
||
Backup-Current -Message $msg
|
||
}
|
||
"new" {
|
||
Write-Banner
|
||
New-Branch -Name $args[0]
|
||
}
|
||
"switch" {
|
||
Write-Banner
|
||
Switch-Branch -Name $args[0]
|
||
}
|
||
"merge" {
|
||
Write-Banner
|
||
Merge-Branch -Source $args[0]
|
||
}
|
||
"delete" {
|
||
Write-Banner
|
||
Delete-Branch -Name $args[0]
|
||
}
|
||
"restore" {
|
||
Show-RestoreMenu
|
||
}
|
||
"restore-last" {
|
||
Write-Banner
|
||
Restore-Last
|
||
}
|
||
"restore-commit" {
|
||
Write-Banner
|
||
Restore-Commit -Target $args[0]
|
||
}
|
||
"undo" {
|
||
Write-Banner
|
||
Undo-LastCommit
|
||
}
|
||
"log" {
|
||
Write-Banner
|
||
Show-Log
|
||
}
|
||
"help" {
|
||
Show-Help
|
||
}
|
||
default {
|
||
Write-Host "未知操作: $Action" -ForegroundColor Red
|
||
Write-Host ""
|
||
Show-Help
|
||
}
|
||
}
|
||
}
|