0
0
Files
vs-p/out/panels/services/GitService.js

297 lines
10 KiB
JavaScript

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GitService = void 0;
const fs = __importStar(require("fs"));
const isomorphic_git_1 = __importDefault(require("isomorphic-git"));
const node_1 = __importDefault(require("isomorphic-git/http/node"));
const path = __importStar(require("path"));
const child_process_1 = require("child_process");
const util_1 = require("util");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
/**
* Git操作服务
*/
class GitService {
/**
* 获取远程分支列表
*/
static async fetchBranches(url, username, token) {
try {
const options = {
http: node_1.default,
url: url
};
if (username || token) {
options.onAuth = () => ({
username: username || '',
password: token || ''
});
}
const refs = await isomorphic_git_1.default.listServerRefs(options);
const branchRefs = refs.filter((ref) => ref.ref.startsWith('refs/heads/') || ref.ref.startsWith('refs/remotes/origin/'));
const branches = branchRefs.map((ref) => {
let branchName;
if (ref.ref.startsWith('refs/remotes/')) {
branchName = ref.ref.replace('refs/remotes/origin/', '');
}
else {
branchName = ref.ref.replace('refs/heads/', '');
}
return {
name: branchName,
isCurrent: branchName === 'main' || branchName === 'master',
selected: false
};
});
return branches;
}
catch (error) {
console.error('获取分支失败:', error);
throw error;
}
}
/**
* 克隆仓库
*/
static async cloneRepository(url, localPath, branch = 'main', onProgress, username, token) {
const parentDir = path.dirname(localPath);
await fs.promises.mkdir(parentDir, { recursive: true });
// 检查目录是否已存在且非空(不变)
let dirExists = false;
try {
await fs.promises.access(localPath);
dirExists = true;
}
catch {
dirExists = false;
}
if (dirExists) {
const dirContents = await fs.promises.readdir(localPath);
if (dirContents.length > 0 && dirContents.some(item => item !== '.git')) {
throw new Error('目标目录不为空,请清空目录或选择其他路径');
}
}
const options = {
fs,
http: node_1.default,
dir: localPath,
url,
singleBranch: true,
depth: 1,
ref: branch,
onProgress
};
if (username || token) {
options.onAuth = () => ({
username: username || '',
password: token || ''
});
}
await isomorphic_git_1.default.clone(options);
}
/**
* 拉取最新更改
*/
static async pullChanges(localPath) {
await isomorphic_git_1.default.pull({
fs: fs,
http: node_1.default,
dir: localPath,
author: { name: 'DCSP User', email: 'user@dcsp.local' },
fastForward: true
});
}
/**
* 构建分支树结构
*/
static buildBranchTree(branches) {
const root = [];
branches.forEach(branch => {
const parts = branch.name.split('/');
let currentLevel = root;
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const isLeaf = i === parts.length - 1;
const fullName = parts.slice(0, i + 1).join('/');
let node = currentLevel.find((n) => n.name === part);
if (!node) {
node = {
name: part,
fullName: fullName,
isLeaf: isLeaf,
children: [],
level: i,
expanded: true
};
currentLevel.push(node);
}
if (isLeaf) {
node.branch = branch;
}
currentLevel = node.children;
}
});
return root;
}
/**
* 初始化Git仓库
*/
static async initRepository(localPath, branchName) {
await execAsync(`git init && git checkout -b "${branchName}"`, { cwd: localPath });
}
/**
* 添加远程仓库
*/
static async addRemote(localPath, repoUrl, username, token) {
let finalUrl = repoUrl;
if (username && token) {
const u = encodeURIComponent(username);
const t = encodeURIComponent(token);
finalUrl = repoUrl.replace('://', `://${u}:${t}@`);
}
await execAsync(`git remote add origin "${finalUrl}"`, { cwd: localPath });
}
/**
* 提交初始文件
*/
static async commitInitialFiles(localPath) {
try {
await execAsync('git add .', { cwd: localPath });
await execAsync(`git commit -m "Initial commit from DCSP - ${new Date().toLocaleString()}"`, { cwd: localPath });
}
catch (error) {
if (error.stderr?.includes('nothing to commit')) {
console.log('没有需要提交的更改');
}
else {
throw error;
}
}
}
/**
* 推送到远程仓库
*/
static async pushToRemote(localPath, branchName) {
await execAsync(`git push -u origin "${branchName}" --force`, { cwd: localPath });
}
/**
* 使用Git命令提交并推送
*/
static async commitAndPush(localPath) {
await execAsync('git add .', { cwd: localPath });
await execAsync(`git commit -m "Auto commit from DCSP - ${new Date().toLocaleString()}"`, { cwd: localPath });
await execAsync('git push', { cwd: localPath });
}
/**
* 检查是否有未提交的更改
*/
static async hasUncommittedChanges(localPath) {
try {
const { stdout } = await execAsync('git status --porcelain', { cwd: localPath });
return !!stdout.trim();
}
catch {
return false;
}
}
/**
* 推送到指定仓库URL
*/
static async pushToRepoUrl(localPath, repoUrl, branchName, username, token) {
let finalUrl = repoUrl;
if (username && token) {
const u = encodeURIComponent(username);
const t = encodeURIComponent(token);
finalUrl = repoUrl.replace('://', `://${u}:${t}@`);
}
const commands = [
'git fetch --unshallow || true',
`git checkout -B "${branchName}"`,
`git push -u "${finalUrl}" "${branchName}" --force`
];
await execAsync(commands.join(' && '), { cwd: localPath });
}
/**
* 生成模块文件夹名称
*/
static generateModuleFolderName(url, branch) {
const repoName = url.split('/').pop()?.replace('.git', '') || 'unknown-repo';
let folderName = branch.trim();
folderName = folderName.replace(/^\/+/, "").replace(/\/+$/, ""); // 去掉两端 "/"
folderName = folderName.replace(/\//g, "-"); // "/" 转为 "-"
folderName = folderName.replace(/[<>:"\\|?*\r\n\t]/g, "-"); // 替换 Windows 不允许的字符
if (!folderName)
folderName = "default"; // 空的 fallback
return {
displayName: repoName,
folderName
};
}
/**
* 构建文件树
*/
static async buildFileTree(dir, relativePath = '') {
try {
const files = await fs.promises.readdir(dir);
const tree = [];
for (const file of files) {
if (file.startsWith('.') && file !== '.git')
continue;
if (file === '.dcsp-data.json')
continue;
const filePath = path.join(dir, file);
const stats = await fs.promises.stat(filePath);
const currentRelativePath = path.join(relativePath, file);
if (stats.isDirectory()) {
const children = await GitService.buildFileTree(filePath, currentRelativePath);
tree.push({
name: file,
type: 'folder',
path: currentRelativePath,
children: children
});
}
else {
tree.push({
name: file,
type: 'file',
path: currentRelativePath
});
}
}
return tree;
}
catch (error) {
console.error('构建文件树失败:', error);
return [];
}
}
}
exports.GitService = GitService;
//# sourceMappingURL=GitService.js.map