阚金翔
达则兼济天下,穷则独善其身

部分克隆 + 稀疏检出

一、需求背景

在git使用过程中,为了解决只对目标文件或者目标文件夹以及灵活组合的配置下去维护对应的文件需求,避免工作目录+.git仓库过大的问题

二、脚本实例

# ====== 配置区 =====================================
# 仓库地址
$RepoUrl = "git@github.com:fastapi/fastapi.git"
# 分支名(或主分支)
$Branch = "woshiyigemingzi"
# 想要拉取的文件夹(相对仓库根目录路径)
$TargetDirs = @(
    "我是和git同级别\helloword"
)
# 想要拉取的"单个文件"(相对仓库根目录路径)
# 这个数组必须存在,即使为空
$TargetFiles = @()

# 本地工作目录
$WorkDir = "D:\workspace\code\helloword"
# 浅克隆深度(只取最近5次提交)
$Depth = 5
# 私钥路径
$PrivateKey = "D:\xiatianxiatian\qiaoqiaoguoqu\liuxiaxiaomimi"
# ====== 执行区 =========================================

# 简单的暂停函数
function Pause-Script {
    Write-Host "Press any key to continue..."
    $null = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}

# ====== 优化删除确认部分 ======
# 清理工作区(优化后的确认提示)
if (Test-Path $WorkDir) {
    $items = Get-ChildItem $WorkDir -Recurse -ErrorAction SilentlyContinue
    if ($items.Count -gt 0) {
        Write-Host "=" * 60
        Write-Host "WORKING DIRECTORY WILL BE DELETED" -ForegroundColor Red
        Write-Host "=" * 60
        Write-Host "Directory: $WorkDir" -ForegroundColor Yellow
        Write-Host "Contains: $($items.Count) items total" -ForegroundColor Yellow
        
        # 统计文件和目录数量
        $files = $items | Where-Object { -not $_.PSIsContainer }
        $dirs = $items | Where-Object { $_.PSIsContainer }
        Write-Host "  - Files: $($files.Count)" -ForegroundColor Yellow
        Write-Host "  - Directories: $($dirs.Count)" -ForegroundColor Yellow
        
        # 显示前5个项目
        if ($items.Count -gt 0) {
            Write-Host "Top items:" -ForegroundColor Yellow
            $items | Select-Object -First 5 | ForEach-Object {
                $type = if ($_.PSIsContainer) { "DIR" } else { "FILE" }
                $size = if (-not $_.PSIsContainer) { " ($([math]::Round($_.Length/1KB, 2)) KB)" } else { "" }
                Write-Host "  [$type] $($_.Name)$size" -ForegroundColor Yellow
            }
            if ($items.Count -gt 5) {
                Write-Host "  ... and $($items.Count - 5) more items" -ForegroundColor Yellow
            }
        }
        
        Write-Host "=" * 60
        Write-Host "This operation will PERMANENTLY delete the entire directory!" -ForegroundColor Red
        Write-Host "=" * 60
        
        $confirm = Read-Host "Type 'DELETE' to confirm deletion, or press Enter to cancel"
        if ($confirm -ne "DELETE") {
            Write-Host "Operation cancelled by user."
            exit 1
        }
    }
    
    # 执行删除
    try {
        Remove-Item $WorkDir -Recurse -Force -ErrorAction Stop
        Write-Host "Directory removed: $WorkDir"
    }
    catch {
        Write-Host "Failed to remove directory: $_" -ForegroundColor Red
        Write-Host "Possible reasons:" -ForegroundColor Red
        Write-Host "1. Files are open in another program" -ForegroundColor Red
        Write-Host "2. Insufficient permissions" -ForegroundColor Red
        Write-Host "3. Path too long" -ForegroundColor Red
        Pause-Script
        exit 1
    }
}

# 创建新目录
New-Item -ItemType Directory -Path $WorkDir -Force | Out-Null
Set-Location $WorkDir
Write-Host "Created working directory: $WorkDir"

# 配置SSH认证
$env:GIT_SSH_COMMAND = "ssh -i `"$PrivateKey`" -o IdentitiesOnly=yes -o StrictHostKeyChecking=no"
Write-Host "Using SSH key: $PrivateKey"

# 执行部分克隆
Write-Host "Cloning repository (partial clone)..."
$cloneResult = git clone --filter=blob:none --no-checkout --depth $Depth -b $Branch $RepoUrl . 2>&1
if ($LASTEXITCODE -ne 0) {
    Write-Host "Clone failed. Ensure:" -ForegroundColor Red
    Write-Host "1. Git version >= 2.19" -ForegroundColor Red
    Write-Host "2. Server supports partial clone" -ForegroundColor Red
    Write-Host "3. SSH key has repository access" -ForegroundColor Red
    Write-Host "Error: $cloneResult" -ForegroundColor Red
    Pause-Script
    exit 1
}

# 配置稀疏检出
Write-Host "Configuring sparse checkout..."
git sparse-checkout init --no-cone 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
    Write-Host "Sparse checkout init failed"
    Pause-Script
    exit 1
}

# 构造稀疏检出的匹配路径
$includePaths = @()

foreach ($dir in $TargetDirs) {
    if ([string]::IsNullOrWhiteSpace($dir)) { continue }
    $normalizedDir = $dir.Replace('\', '/').Trim('/')
    $includePaths += "$normalizedDir"
    $includePaths += "$normalizedDir/*"
    Write-Host "Include directory: $normalizedDir"
}

foreach ($file in $TargetFiles) {
    if ([string]::IsNullOrWhiteSpace($file)) { continue }
    $normalizedFile = $file.Replace('\', '/').TrimStart('/')
    $includePaths += $normalizedFile
    Write-Host "Include file: $normalizedFile"
}

if ($includePaths.Count -eq 0) {
    Write-Host "No directories or files specified."
    Pause-Script
    exit 1
}

# 设置稀疏检出规则
Write-Host "Setting sparse checkout paths..."
git sparse-checkout set @includePaths 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
    Write-Host "Failed to set sparse checkout paths"
    Pause-Script
    exit 1
}

# 检出文件
Write-Host "Checking out files..."
$checkoutResult = git checkout $Branch 2>&1
if ($LASTEXITCODE -ne 0) {
    Write-Host "Checkout failed: $checkoutResult"
    Pause-Script
    exit 1
}

# 验证结果
$success = $true

Write-Host "`nVerifying directories..." -ForegroundColor Cyan
foreach ($dir in $TargetDirs) {
    if ([string]::IsNullOrWhiteSpace($dir)) { continue }
    $targetPath = Join-Path $WorkDir $dir
    if (Test-Path $targetPath -PathType Container) {
        $fileCount = (Get-ChildItem $targetPath -Recurse -File -ErrorAction SilentlyContinue).Count
        Write-Host "[Success]$dir ($fileCount files)" -ForegroundColor Green
    }
    else {
        Write-Host "[FAIL]$dir (not found)" -ForegroundColor Red
        $success = $false
    }
}

Write-Host "`nVerifying files..." -ForegroundColor Cyan
foreach ($file in $TargetFiles) {
    if ([string]::IsNullOrWhiteSpace($file)) { continue }
    $filePath = Join-Path $WorkDir $file
    if (Test-Path $filePath -PathType Leaf) {
        Write-Host "[Success]$file" -ForegroundColor Green
    }
    else {
        Write-Host "[FAIL]$file (not found)" -ForegroundColor Red
        $success = $false
    }
}

if (-not $success) {
    Write-Host "`nSome items were not found. Check paths are correct in repository." -ForegroundColor Yellow
    Write-Host "To view repository structure: git ls-tree -r HEAD --name-only" -ForegroundColor Yellow
}

# 清理敏感信息
$env:GIT_SSH_COMMAND = $null

Write-Host "`nDone." -ForegroundColor Green
exit 0

三、说明

1.对ps1脚本文件调用的时候可以外部使用批处理文件进行,可以跨版本兼容和路径可靠性等

2.配置对应的文件夹和文件的时候,是针对仓库根目录的

3.拉取后尽量不要非常规修改git规则,以免对于后续提交会有影响

4.关于密钥私钥和对应的,windows下属性权限修改

参考资料

posted on 2025-12-06 12:51  阚金翔  阅读(0)  评论(0)    收藏  举报