// 将指定目录下的所有 git 项目,备份至一个 git 仓库中
// gitea 有 api 可以批量建库
using System.Collections.Concurrent;
using CliWrap;
using CliWrap.Buffered;
// 列出所有待操作的目录
var root = "D:\\xCodes\\";
var folders = Directory.EnumerateDirectories(root, "*", SearchOption.AllDirectories);
foreach (var folder in folders)
{
// 处理有 .git 的目录
var gitfolder = Path.Combine(folder, ".git");
if (!Directory.Exists(gitfolder))
{
continue;
}
// 有 xignore 文件或目录的直接跳过
var xignoreFile = Path.Combine(folder, "xignore");
if (File.Exists(xignoreFile) || Directory.Exists(xignoreFile))
{
Console.WriteLine($"{folder} has xignore file or directory");
continue;
}
// 待处理目录
Console.WriteLine(folder);
var folderName = Path.GetFileName(folder);
// 检测有没有未签入的数据, 如果有的话,人工核对处理
var result = await Cli.Wrap("git")
.WithArguments(["status", "--porcelain"])
.WithWorkingDirectory(folder)
.ExecuteBufferedAsync();
if (result.ExitCode != 0)
{
throw new Exception($"git remote failed: {result.ExitCode}");
}
if (!string.IsNullOrWhiteSpace(result.StandardOutput))
{
throw new Exception($"git status not clean: {folder}, {result.StandardOutput}");
}
// 列出所有远程
result = await Cli.Wrap("git")
.WithArguments(["remote"])
.WithWorkingDirectory(folder)
.ExecuteBufferedAsync();
if (result.ExitCode != 0)
{
throw new Exception($"git remote failed: {result.ExitCode}");
}
var remotes = result.StandardOutput.Split('\r', '\n').Where(x => !string.IsNullOrWhiteSpace(x)).ToArray();
// 为了方便,统一将备份远程命名为 x_backup
if (!remotes.Contains("x_backup"))
{
// 如果没有,新建远程
var remote = remotes.First();
// 取一个现有的仓库名名作为现在使用的仓库名,如果出错,人工再修改
result = await Cli.Wrap("git")
.WithArguments(["remote", "get-url", remote])
.WithWorkingDirectory(folder)
.ExecuteBufferedAsync();
if (result.ExitCode != 0)
{
throw new Exception($"git remote failed: {result.ExitCode}");
}
var uri = new Uri(result.StandardOutput);
var fileName = Path.GetFileNameWithoutExtension(uri.LocalPath);
Console.WriteLine($"{remote}: {fileName}");
// 新建远程
var newUrl = $"https://git.xxx.xxx/x/{fileName}.git";
Console.WriteLine(newUrl);
result = await Cli.Wrap("git")
.WithArguments(["remote", "add", "x_backup", newUrl])
.WithWorkingDirectory(folder)
.ExecuteBufferedAsync();
if (result.ExitCode != 0)
{
throw new Exception($"git remote failed: {result.ExitCode}");
}
}
// 将所有分支推送至远程
result = await Cli.Wrap("git")
.WithArguments(["push", "--all", "x_backup"])
.WithWorkingDirectory(folder)
.ExecuteBufferedAsync();
if (result.ExitCode != 0)
{
throw new Exception($"git remote failed: {result.ExitCode}");
}
}
# gitea 操作
# 判断库是否存在
# /api/v1/repos/$giteaUsername/$targetRepoName
$response = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Get
# 从三方库导入
# /api/v1/repos/migrate
# 构建请求体
$body = @{
clone_addr = $remoteGitRepoUrl # 待导入库 地址
uid = (Invoke-RestMethod -Uri "$env:GITEA_SERVER_URL/api/v1/users/$giteaUsername" -Headers @{Authorization = "token $env:GITEA_API_TOKEN" }).id # 应该是当前用户 id
repo_name = $targetRepoName # 库名
mirror = $false # 要不要自动从源 git 库中定时同步
private = $true # 是否设置为私有
auth_username = $remoteGitUsername # 待导入库的账号
auth_password = $remoteGitPassword # 待导入库的密码
} | ConvertTo-Json
# 发送 POST 请求
$response = Invoke-RestMethod -Uri $apiUrl -Method Post -Headers @{Authorization = "token $env:GITEA_API_TOKEN" } -Body $body -ContentType "application/json"