整体思路用 GitLab API 个人访问令牌分页拉取你有权访问的全部项目列表包含路径和克隆地址。使用git clone --mirror将每个项目下载为裸仓库该仓库包含所有分支、标签和提交历史。可选如果项目使用了 Git LFS再额外拉取大文件。最终得到一个完全离线的本地目录之后无需服务器即可切换任意分支。准备工作安装 Git for Windows下载地址Redirecting…安装时选择默认组件务必包含 Git Bash虽然我们使用 PowerShell但需要 git 命令。创建 GitLab Access Token登录 GitLab → Settings → Access Tokens勾选read_api和read_repository生成后复制 Token。确定 GitLab 地址如https://gitlab.example.com。第一步获取所有项目列表并导出为文件在 PowerShell 中运行以下脚本将项目路径和 HTTPS 克隆地址保存到一个 CSV 文件中。脚本会自动处理分页避免遗漏。q如何运行脚本1. 将脚本保存为.ps1文件打开记事本或任何纯文本编辑器。把脚本代码完整粘贴进去。修改开头的配置变量如$GitLabUrl、$Token、路径等。保存文件时文件名例如backup-gitlab.ps1编码选择UTF-8不要选 UTF-8 with BOM 也没关系但确保是 UTF-8可以用nopad打开点击code选择convert to UTF-8-BOM保存否则执行脚本会报错保存位置建议放在一个简短路径下比如C:\backup\backup-gitlab.ps12. 打开 PowerShell按Win R输入powershell回车。或者直接在开始菜单搜索“PowerShell”点击打开。如果你需要修改执行策略并保存后续都会生效可能需要以管理员身份运行。右键点击 PowerShell 图标 → “以管理员身份运行”。但仅临时运行脚本则不需要管理员。3. 处理执行策略关键PowerShell 的执行策略ExecutionPolicy用于防止运行未签名的脚本。你可以选择临时绕过或永久更改。方法 A临时绕过推荐无需管理员权限在 PowerShell 窗口里直接执行powershell -ExecutionPolicy Bypass -File C:\backup\backup-gitlab.ps1这条命令会在当前打开的 PowerShell 中启动一个新的子进程以Bypass策略运行你的脚本不影响系统默认设置用完即走。# 配置区域请修改为你的实际信息 $GitLabUrl https://gitlab.example.com # 你的 GitLab 地址 $Token your_personal_access_token # 你的 Access Token $OutputCsv C:\backup\projects.csv # 保存项目列表的文件路径 # # 如果输出目录不存在则创建 $OutputDir Split-Path $OutputCsv -Parent if (!(Test-Path $OutputDir)) { New-Item -ItemType Directory -Path $OutputDir -Force } # 准备 HTTP 请求头 $headers { PRIVATE-TOKEN $Token } # 初始化结果列表 $allProjects () $page 1 Write-Host 正在获取项目列表... -ForegroundColor Cyan do { $url $GitLabUrl/api/v4/projects?per_page100page$pagemembershiptrue try { $response Invoke-RestMethod -Uri $url -Headers $headers -Method Get } catch { Write-Error 请求失败页 $page$_ break } # 如果返回是空数组说明已到最后一页 if ($response.Count -eq 0) { break } $allProjects $response Write-Host 已获取第 $page 页累计 $($allProjects.Count) 个项目 $page } while ($true) # 提取需要的字段路径群组/子群组/项目名和 HTTPS 克隆地址 $projects $allProjects | Select-Object path_with_namespace, http_url_to_repo # 导出为 CSV 文件无类型信息 $projects | Export-Csv -Path $OutputCsv -NoTypeInformation -Encoding UTF8 Write-Host 项目列表已保存到: $OutputCsv -ForegroundColor Green Write-Host 共获取到 $($projects.Count) 个项目 -ForegroundColor Green参数说明membershiptrue只拉取你有成员权限的项目避免备份无关的公共仓库。如果需要备份所有可见项目移除此参数即可。输出 CSV 包含两列path_with_namespace如group/subgroup/project和http_url_to_repo如https://gitlab.example.com/group/subgroup/project.git。第二步批量镜像克隆所有项目同样在 PowerShell 中执行以下脚本它会读取第一步生成的 CSV 文件并按原始群组层级创建目录对每个项目执行git clone --mirror。# 配置区域 $Token your_personal_access_token # 你的 Access Token $CsvPath C:\backup\projects.csv # 上一步生成的项目列表 $BackupRoot C:\backup\gitlab-full-backup # 备份的根目录建议用短路径 # # 导入项目列表 $projects Import-Csv -Path $CsvPath # 为了在 HTTPS URL 中自动嵌入令牌定义一个函数来转换地址 function Add-TokenToUrl($httpUrl) { # 将 https:// 替换为 https://oauth2:TOKEN return $httpUrl -replace https://, https://oauth2:${Token} } # 创建备份根目录 if (!(Test-Path $BackupRoot)) { New-Item -ItemType Directory -Path $BackupRoot -Force } Set-Location $BackupRoot $total $projects.Count $current 0 Write-Host 开始批量镜像克隆共 $total 个项目... -ForegroundColor Cyan foreach ($proj in $projects) { $current $namespace $proj.path_with_namespace # 如 group/subgroup/project $httpUrl $proj.http_url_to_repo $authUrl Add-TokenToUrl $httpUrl # 目标目录保留 .git 后缀裸仓库命名惯例 $targetDir Join-Path $BackupRoot $namespace.git Write-Host [$current/$total] 克隆: $namespace -ForegroundColor Yellow # 如果目标目录已存在且是 git 仓库则跳过避免重复克隆 if (Test-Path (Join-Path $targetDir HEAD)) { Write-Host 已存在跳过 continue } # 创建父目录 $parentDir Split-Path $targetDir -Parent if (!(Test-Path $parentDir)) { New-Item -ItemType Directory -Path $parentDir -Force } # 执行镜像克隆将输出直接打印到控制台 git clone --mirror $authUrl $targetDir if ($LASTEXITCODE -ne 0) { Write-Warning 克隆失败: $namespace # 可决定是否删除不完整的目录 } } Write-Host 所有项目克隆完成备份根目录: $BackupRoot -ForegroundColor Green注意脚本会在 HTTPS URL 中直接嵌入令牌oauth2:TOKEN这仅适用于个人私有环境。操作完成后你可以删除此脚本或清除命令历史记录。裸仓库以.git结尾这是镜像克隆的默认行为你后续可以直接用它们来恢复工作副本。第三步可选处理 Git LFS 大文件如果项目使用了 Git LFS镜像克隆只会下载指针文件真实的大文件还需单独拉取。在克隆全部完成后可以遍历所有仓库执行 LFS 拉取# 进入备份根目录 Set-Location $BackupRoot # 查找所有 .git 目录裸仓库 $repoDirs Get-ChildItem -Recurse -Filter *.git -Directory foreach ($repo in $repoDirs) { Write-Host 检查 LFS: $($repo.FullName) Set-Location $repo.FullName # 检测该仓库是否使用了 LFS if (git lfs ls-files 2$null) { Write-Host 正在拉取 LFS 文件... git lfs fetch --all } }如果你的项目确定没有使用 LFS可以跳过这一步。普通 Git 对象已在clone --mirror时全部下载。第四步验证备份完整性随机挑选一个项目测试能否正常切换分支# 从裸仓库创建一个临时工作副本$testRepo C:\temp\test-projectgit clone C:\backup\gitlab-full-backup\group\project.git $testRepoSet-Location $testRepogit branch -a # 应列出所有远程分支此处实际是本地裸仓库中的分支引用git checkout 某个分支名 # 应能正常切换或者直接查看裸仓库中的分支git --git-dirC:\backup\gitlab-full-backup\group\project.git branch -a只要所有分支都能列出说明备份完整之后 GitLab 服务器关闭也不会影响你使用这些代码。关于 Windows 路径长度限制Git 在 Windows 下受260 字符路径限制影响。如果群组层级很深可能导致克隆失败。解决方案缩短备份根目录路径例如直接放在C:\b\。启用长路径支持Windows 10 1607按WinR输入gpedit.msc专业版或通过注册表text计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled设为1后重启。或者用 PowerShell 管理员运行powershellNew-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem -Name LongPathsEnabled -Value 1 -PropertyType DWORD -Force重启后生效。若仍有个别极深路径失败可考虑在备份时截断或映射到短目录名但会丢失层级结构不过这种情况很少见。最终步骤打包存档备份完成后你可以把整个gitlab-full-backup目录压缩成一个 ZIP 文件方便传输或存储Compress-Archive -Path C:\backup\gitlab-full-backup -DestinationPath C:\backup\gitlab-backup-$(Get-Date -Format yyyyMMdd).zip接收方解压后即可通过git clone本地路径或直接使用裸仓库查看所有历史。以上为拉取备份裸仓库逻辑q:裸仓库和普通仓库的区别对比项普通仓库non-bare裸仓库bare工作目录有能看到.cpp、.py等源码文件没有工作目录看不到任何项目文件内部结构项目根目录下有一个隐藏的.git文件夹整个仓库就是.git文件夹的内容通常命名为xxx.git能否直接编辑可以修改文件后add/commit不能直接编辑没有工作目录无法修改文件用途日常开发、看代码、改代码服务端共享、备份、中转分支/历史包含通过git log、git branch查看完整包含所有分支、标签、历史和普通仓库完全一样典型获取方式git clone urlgit clone --mirror url或git init --bare你使用的git clone --mirror命令目的就是制作一个完整镜像它默认生成的就是裸仓库。这恰恰是我们备份时最需要的形态——因为裸仓库不带工作文件体积更小且完整保留了所有版本数据非常适合备份和传输。你看到的“没有代码”是正常的代码其实都在.git/objects和.git/refs里只是没有“展开”到文件系统而已。如何从裸仓库中取出“不同分支的代码”裸仓库本身不能直接切换分支查看文件但它可以作为数据源随时生成一个带工作目录的普通仓库并且能自由切换分支。方法极简单方法一从裸仓库克隆一个新仓库最直接比如你备份的裸仓库在C:\backup\gitlab-full-backup\mygroup\myproject.git想在D:\mycode下看到代码并切换到develop分支# 1. 从裸仓库克隆出普通仓库自动获得工作目录git clone C:\backup\gitlab-full-backup\mygroup\myproject.git D:\mycode# 2. 进入新目录cd D:\mycode# 3. 列出所有分支原本裸仓库里的分支现在都能看到git branch -a# 4. 切换到你想看的分支代码文件立即出现git checkout develop这时D:\mycode下就会看到develop分支的完整源码你可以随意修改、编译。想切到main或任何其他分支也只需要git checkout就行完全不需要原来的 GitLab 服务器。方法二直接在裸仓库上附加一个工作目录高级如果想直接在裸仓库旁边生成工作文件可以用git worktree但裸仓库通常不用于此目的。更简单的还是上面“再克隆一次”的方法毕竟它是本地操作秒级完成不会占用双倍空间因为 Git 使用硬链接或对象复用。批量将裸仓库恢复成普通仓库方便随时查看假如你想把整个备份全部展开成普通仓库每个项目都能直接看到文件$backupRoot C:\backup\gitlab-full-backup $targetRoot D:\gitlab-code Get-ChildItem -Path $backupRoot -Recurse -Filter *.git -Directory | ForEach-Object { $bareRepo $_.FullName $relativePath $_.FullName.Substring($backupRoot.Length 1) -replace \.git$, $targetPath Join-Path $targetRoot $relativePath Write-Host 处理项目$relativePath # 1. 检测并拉取 LFS 对象在克隆前完成保证裸仓库数据完整 $lfsCheck git --git-dir$bareRepo lfs ls-files 21 if ($lfsCheck) { Write-Host 检测到 Git LFS 文件正在拉取大文件... git --git-dir$bareRepo lfs fetch --all } else { Write-Host 该项目未使用 Git LFS } # 2. 从裸仓库克隆出带工作目录的普通仓库 Write-Host 正在生成普通仓库... git clone $bareRepo $targetPath # 3. 进入新仓库为所有远程分支创建本地跟踪分支 Push-Location $targetPath try { $remoteBranches git branch -r | Where-Object { $_ -notmatch - } | ForEach-Object { $_.Trim() } foreach ($remoteBranch in $remoteBranches) { $localBranch $remoteBranch -replace ^origin/, if ($localBranch -eq HEAD) { continue } $exists git branch --list $localBranch if (-not $exists) { git branch --track $localBranch $remoteBranch 2$null } } Write-Host --- $relativePath 分支列表 --- git branch -a Write-Host } finally { Pop-Location } } Write-Host 全部处理完成 -ForegroundColor Green