Git API
概述
Trae Git API 提供了完整的 Git 版本控制功能,支持仓库管理、分支操作、提交历史、远程仓库同步等操作。开发者可以通过 API 实现 Git 工作流的自动化和集成。
核心概念
Git 仓库
typescript
import { git, Uri } from '@trae/api';
interface Repository {
readonly rootUri: Uri;
readonly state: RepositoryState;
readonly inputBox: SourceControlInputBox;
readonly resourceGroups: SourceControlResourceGroup[];
}
interface RepositoryState {
readonly HEAD: Branch | undefined;
readonly refs: Ref[];
readonly remotes: Remote[];
readonly submodules: Submodule[];
readonly rebaseCommit: Commit | undefined;
readonly mergeChanges: Change[];
readonly indexChanges: Change[];
readonly workingTreeChanges: Change[];
}分支和引用
typescript
interface Branch {
readonly name: string;
readonly commit?: string;
readonly type: RefType;
readonly remote?: string;
readonly upstream?: string;
readonly ahead?: number;
readonly behind?: number;
}
interface Ref {
readonly type: RefType;
readonly name?: string;
readonly commit?: string;
readonly remote?: string;
}
enum RefType {
Head,
RemoteHead,
Tag
}API 参考
仓库管理
获取仓库
typescript
// 获取当前工作区的 Git 仓库
const getCurrentRepository = (): Repository | undefined => {
const repositories = git.repositories;
return repositories.length > 0 ? repositories[0] : undefined;
};
// 获取指定路径的仓库
const getRepositoryByPath = (path: string): Repository | undefined => {
const uri = Uri.file(path);
return git.getRepository(uri);
};
// 监听仓库变化
git.onDidOpenRepository(repository => {
console.log('Repository opened:', repository.rootUri.fsPath);
setupRepositoryListeners(repository);
});
git.onDidCloseRepository(repository => {
console.log('Repository closed:', repository.rootUri.fsPath);
});初始化仓库
typescript
// 初始化新的 Git 仓库
const initRepository = async (path: string): Promise<Repository> => {
try {
const uri = Uri.file(path);
const repository = await git.init(uri);
console.log('Repository initialized:', repository.rootUri.fsPath);
return repository;
} catch (error) {
console.error('Failed to initialize repository:', error);
throw error;
}
};
// 克隆远程仓库
const cloneRepository = async (
url: string,
parentPath: string,
options?: CloneOptions
): Promise<Repository> => {
try {
const parentUri = Uri.file(parentPath);
const repository = await git.clone(url, parentUri, options);
console.log('Repository cloned:', repository.rootUri.fsPath);
return repository;
} catch (error) {
console.error('Failed to clone repository:', error);
throw error;
}
};
interface CloneOptions {
branch?: string;
depth?: number;
recursive?: boolean;
progress?: (progress: Progress) => void;
}分支操作
分支管理
typescript
// 获取所有分支
const getAllBranches = async (repository: Repository): Promise<Branch[]> => {
try {
return await repository.getBranches();
} catch (error) {
console.error('Failed to get branches:', error);
return [];
}
};
// 获取当前分支
const getCurrentBranch = (repository: Repository): Branch | undefined => {
return repository.state.HEAD;
};
// 创建新分支
const createBranch = async (
repository: Repository,
name: string,
checkout = true
): Promise<void> => {
try {
await repository.createBranch(name, checkout);
console.log(`Branch '${name}' created${checkout ? ' and checked out' : ''}`);
} catch (error) {
console.error('Failed to create branch:', error);
throw error;
}
};
// 切换分支
const checkoutBranch = async (
repository: Repository,
name: string
): Promise<void> => {
try {
await repository.checkout(name);
console.log(`Switched to branch '${name}'`);
} catch (error) {
console.error('Failed to checkout branch:', error);
throw error;
}
};
// 删除分支
const deleteBranch = async (
repository: Repository,
name: string,
force = false
): Promise<void> => {
try {
await repository.deleteBranch(name, force);
console.log(`Branch '${name}' deleted`);
} catch (error) {
console.error('Failed to delete branch:', error);
throw error;
}
};分支合并
typescript
// 合并分支
const mergeBranch = async (
repository: Repository,
branch: string
): Promise<void> => {
try {
await repository.merge(branch);
console.log(`Merged branch '${branch}'`);
} catch (error) {
console.error('Failed to merge branch:', error);
throw error;
}
};
// 变基操作
const rebaseBranch = async (
repository: Repository,
branch: string
): Promise<void> => {
try {
await repository.rebase(branch);
console.log(`Rebased onto '${branch}'`);
} catch (error) {
console.error('Failed to rebase:', error);
throw error;
}
};
// 解决合并冲突
const resolveMergeConflicts = async (
repository: Repository,
files: Uri[]
): Promise<void> => {
try {
// 标记冲突文件为已解决
for (const file of files) {
await repository.add([file]);
}
console.log('Merge conflicts resolved');
} catch (error) {
console.error('Failed to resolve conflicts:', error);
throw error;
}
};提交操作
暂存和提交
typescript
// 暂存文件
const stageFiles = async (
repository: Repository,
files: Uri[]
): Promise<void> => {
try {
await repository.add(files);
console.log(`Staged ${files.length} files`);
} catch (error) {
console.error('Failed to stage files:', error);
throw error;
}
};
// 取消暂存文件
const unstageFiles = async (
repository: Repository,
files: Uri[]
): Promise<void> => {
try {
await repository.revert(files);
console.log(`Unstaged ${files.length} files`);
} catch (error) {
console.error('Failed to unstage files:', error);
throw error;
}
};
// 提交更改
const commitChanges = async (
repository: Repository,
message: string,
options?: CommitOptions
): Promise<void> => {
try {
await repository.commit(message, options);
console.log('Changes committed:', message);
} catch (error) {
console.error('Failed to commit changes:', error);
throw error;
}
};
interface CommitOptions {
all?: boolean;
amend?: boolean;
signoff?: boolean;
signCommit?: boolean;
empty?: boolean;
noVerify?: boolean;
}
// 修改最后一次提交
const amendLastCommit = async (
repository: Repository,
message?: string
): Promise<void> => {
try {
await repository.commit(message || '', { amend: true });
console.log('Last commit amended');
} catch (error) {
console.error('Failed to amend commit:', error);
throw error;
}
};提交历史
typescript
// 获取提交历史
const getCommitHistory = async (
repository: Repository,
options?: LogOptions
): Promise<Commit[]> => {
try {
return await repository.log(options);
} catch (error) {
console.error('Failed to get commit history:', error);
return [];
}
};
interface LogOptions {
maxEntries?: number;
skip?: number;
path?: string;
branch?: string;
author?: string;
since?: Date;
until?: Date;
}
interface Commit {
readonly hash: string;
readonly message: string;
readonly parents: string[];
readonly authorDate?: Date;
readonly authorName?: string;
readonly authorEmail?: string;
readonly commitDate?: Date;
}
// 获取特定提交的详细信息
const getCommitDetails = async (
repository: Repository,
hash: string
): Promise<CommitDetails | undefined> => {
try {
return await repository.getCommit(hash);
} catch (error) {
console.error('Failed to get commit details:', error);
return undefined;
}
};
interface CommitDetails extends Commit {
readonly changes: Change[];
readonly diff: string;
}远程仓库操作
远程仓库管理
typescript
// 获取远程仓库列表
const getRemotes = async (repository: Repository): Promise<Remote[]> => {
try {
return await repository.getRemotes();
} catch (error) {
console.error('Failed to get remotes:', error);
return [];
}
};
interface Remote {
readonly name: string;
readonly fetchUrl?: string;
readonly pushUrl?: string;
readonly isReadOnly: boolean;
}
// 添加远程仓库
const addRemote = async (
repository: Repository,
name: string,
url: string
): Promise<void> => {
try {
await repository.addRemote(name, url);
console.log(`Remote '${name}' added: ${url}`);
} catch (error) {
console.error('Failed to add remote:', error);
throw error;
}
};
// 删除远程仓库
const removeRemote = async (
repository: Repository,
name: string
): Promise<void> => {
try {
await repository.removeRemote(name);
console.log(`Remote '${name}' removed`);
} catch (error) {
console.error('Failed to remove remote:', error);
throw error;
}
};同步操作
typescript
// 拉取更新
const pullChanges = async (
repository: Repository,
remote?: string,
branch?: string
): Promise<void> => {
try {
await repository.pull(remote, branch);
console.log('Changes pulled successfully');
} catch (error) {
console.error('Failed to pull changes:', error);
throw error;
}
};
// 推送更改
const pushChanges = async (
repository: Repository,
remote?: string,
branch?: string,
force = false
): Promise<void> => {
try {
await repository.push(remote, branch, force);
console.log('Changes pushed successfully');
} catch (error) {
console.error('Failed to push changes:', error);
throw error;
}
};
// 获取远程更新
const fetchChanges = async (
repository: Repository,
remote?: string
): Promise<void> => {
try {
await repository.fetch(remote);
console.log('Remote changes fetched');
} catch (error) {
console.error('Failed to fetch changes:', error);
throw error;
}
};状态和差异
仓库状态
typescript
// 获取工作区状态
const getWorkingTreeChanges = (repository: Repository): Change[] => {
return repository.state.workingTreeChanges;
};
// 获取暂存区状态
const getIndexChanges = (repository: Repository): Change[] => {
return repository.state.indexChanges;
};
// 获取合并状态
const getMergeChanges = (repository: Repository): Change[] => {
return repository.state.mergeChanges;
};
interface Change {
readonly uri: Uri;
readonly originalUri: Uri;
readonly renameUri: Uri | undefined;
readonly status: Status;
}
enum Status {
INDEX_MODIFIED,
INDEX_ADDED,
INDEX_DELETED,
INDEX_RENAMED,
INDEX_COPIED,
MODIFIED,
DELETED,
UNTRACKED,
IGNORED,
INTENT_TO_ADD,
BOTH_DELETED,
ADDED_BY_US,
DELETED_BY_THEM,
ADDED_BY_THEM,
DELETED_BY_US,
BOTH_ADDED,
BOTH_MODIFIED
}文件差异
typescript
// 获取文件差异
const getFileDiff = async (
repository: Repository,
uri: Uri,
staged = false
): Promise<string> => {
try {
return await repository.diff(uri, staged);
} catch (error) {
console.error('Failed to get file diff:', error);
return '';
}
};
// 获取提交间差异
const getCommitDiff = async (
repository: Repository,
commit1: string,
commit2: string
): Promise<string> => {
try {
return await repository.diffBetween(commit1, commit2);
} catch (error) {
console.error('Failed to get commit diff:', error);
return '';
}
};
// 获取文件在特定提交的内容
const getFileAtCommit = async (
repository: Repository,
uri: Uri,
commit: string
): Promise<string> => {
try {
return await repository.show(commit, uri.fsPath);
} catch (error) {
console.error('Failed to get file at commit:', error);
return '';
}
};标签操作
typescript
// 创建标签
const createTag = async (
repository: Repository,
name: string,
message?: string
): Promise<void> => {
try {
await repository.tag(name, message);
console.log(`Tag '${name}' created`);
} catch (error) {
console.error('Failed to create tag:', error);
throw error;
}
};
// 获取所有标签
const getTags = async (repository: Repository): Promise<Ref[]> => {
try {
const refs = await repository.getRefs();
return refs.filter(ref => ref.type === RefType.Tag);
} catch (error) {
console.error('Failed to get tags:', error);
return [];
}
};
// 删除标签
const deleteTag = async (
repository: Repository,
name: string
): Promise<void> => {
try {
await repository.deleteTag(name);
console.log(`Tag '${name}' deleted`);
} catch (error) {
console.error('Failed to delete tag:', error);
throw error;
}
};储藏操作
typescript
// 储藏更改
const stashChanges = async (
repository: Repository,
message?: string,
includeUntracked = false
): Promise<void> => {
try {
await repository.createStash(message, includeUntracked);
console.log('Changes stashed');
} catch (error) {
console.error('Failed to stash changes:', error);
throw error;
}
};
// 应用储藏
const applyStash = async (
repository: Repository,
index = 0
): Promise<void> => {
try {
await repository.popStash(index);
console.log('Stash applied');
} catch (error) {
console.error('Failed to apply stash:', error);
throw error;
}
};
// 获取储藏列表
const getStashes = async (repository: Repository): Promise<Stash[]> => {
try {
return await repository.getStashes();
} catch (error) {
console.error('Failed to get stashes:', error);
return [];
}
};
interface Stash {
readonly index: number;
readonly description: string;
}高级功能
Git 钩子
typescript
class GitHookManager {
private repository: Repository;
constructor(repository: Repository) {
this.repository = repository;
}
// 安装预提交钩子
async installPreCommitHook(script: string): Promise<void> {
const hookPath = Uri.joinPath(
this.repository.rootUri,
'.git',
'hooks',
'pre-commit'
);
await workspace.fs.writeFile(hookPath, Buffer.from(script, 'utf8'));
// 设置执行权限(在支持的平台上)
if (process.platform !== 'win32') {
await this.setExecutablePermission(hookPath);
}
}
// 安装提交消息钩子
async installCommitMsgHook(script: string): Promise<void> {
const hookPath = Uri.joinPath(
this.repository.rootUri,
'.git',
'hooks',
'commit-msg'
);
await workspace.fs.writeFile(hookPath, Buffer.from(script, 'utf8'));
if (process.platform !== 'win32') {
await this.setExecutablePermission(hookPath);
}
}
private async setExecutablePermission(uri: Uri): Promise<void> {
// 实现设置文件执行权限的逻辑
// 这通常需要调用系统命令或使用 Node.js fs 模块
}
}子模块管理
typescript
class SubmoduleManager {
private repository: Repository;
constructor(repository: Repository) {
this.repository = repository;
}
// 添加子模块
async addSubmodule(url: string, path: string): Promise<void> {
try {
await this.repository.addSubmodule(url, path);
console.log(`Submodule added: ${path}`);
} catch (error) {
console.error('Failed to add submodule:', error);
throw error;
}
}
// 初始化子模块
async initSubmodules(): Promise<void> {
try {
await this.repository.syncSubmodules();
console.log('Submodules initialized');
} catch (error) {
console.error('Failed to initialize submodules:', error);
throw error;
}
}
// 更新子模块
async updateSubmodules(): Promise<void> {
try {
const submodules = this.repository.state.submodules;
for (const submodule of submodules) {
await this.repository.updateSubmodule(submodule.path);
}
console.log('Submodules updated');
} catch (error) {
console.error('Failed to update submodules:', error);
throw error;
}
}
}Git 工作流自动化
typescript
class GitWorkflow {
private repository: Repository;
constructor(repository: Repository) {
this.repository = repository;
}
// 功能分支工作流
async createFeatureBranch(featureName: string): Promise<void> {
// 确保在主分支上
await this.repository.checkout('main');
// 拉取最新更改
await this.repository.pull();
// 创建并切换到功能分支
const branchName = `feature/${featureName}`;
await this.repository.createBranch(branchName, true);
console.log(`Feature branch '${branchName}' created`);
}
// 完成功能分支
async finishFeatureBranch(featureName: string): Promise<void> {
const branchName = `feature/${featureName}`;
// 切换到主分支
await this.repository.checkout('main');
// 拉取最新更改
await this.repository.pull();
// 合并功能分支
await this.repository.merge(branchName);
// 推送更改
await this.repository.push();
// 删除功能分支
await this.repository.deleteBranch(branchName);
console.log(`Feature branch '${branchName}' finished`);
}
// 发布工作流
async createRelease(version: string): Promise<void> {
// 创建发布分支
const releaseBranch = `release/${version}`;
await this.repository.createBranch(releaseBranch, true);
// 创建版本标签
await this.repository.tag(`v${version}`, `Release version ${version}`);
// 推送分支和标签
await this.repository.push('origin', releaseBranch);
await this.repository.pushTags();
console.log(`Release ${version} created`);
}
}事件监听
仓库事件
typescript
class GitEventListener {
private repository: Repository;
private disposables: Disposable[] = [];
constructor(repository: Repository) {
this.repository = repository;
this.setupListeners();
}
private setupListeners(): void {
// 监听仓库状态变化
this.disposables.push(
this.repository.state.onDidChange(() => {
this.handleStateChange();
})
);
// 监听分支变化
this.disposables.push(
this.repository.onDidChangeState(() => {
this.handleBranchChange();
})
);
}
private handleStateChange(): void {
const state = this.repository.state;
console.log('Repository state changed:');
console.log('- Working tree changes:', state.workingTreeChanges.length);
console.log('- Index changes:', state.indexChanges.length);
console.log('- Current branch:', state.HEAD?.name || 'detached');
}
private handleBranchChange(): void {
const currentBranch = this.repository.state.HEAD;
console.log('Branch changed to:', currentBranch?.name || 'detached');
}
dispose(): void {
this.disposables.forEach(d => d.dispose());
this.disposables = [];
}
}错误处理
Git 操作错误处理
typescript
class GitError extends Error {
constructor(
message: string,
public readonly operation: string,
public readonly repository: Repository,
public readonly originalError?: Error
) {
super(message);
this.name = 'GitError';
}
}
const safeGitOperation = async <T>(
operation: () => Promise<T>,
operationName: string,
repository: Repository
): Promise<T> => {
try {
return await operation();
} catch (error) {
throw new GitError(
`Git ${operationName} failed: ${error.message}`,
operationName,
repository,
error
);
}
};
// 使用示例
const safeCommit = async (
repository: Repository,
message: string
): Promise<void> => {
return safeGitOperation(
() => repository.commit(message),
'commit',
repository
);
};性能优化
Git 操作缓存
typescript
class GitCache {
private branchCache = new Map<string, Branch[]>();
private commitCache = new Map<string, Commit[]>();
private cacheTimeout = 30000; // 30 seconds
async getBranches(repository: Repository): Promise<Branch[]> {
const key = repository.rootUri.toString();
const cached = this.branchCache.get(key);
if (cached) {
return cached;
}
const branches = await repository.getBranches();
this.branchCache.set(key, branches);
// 设置缓存过期
setTimeout(() => {
this.branchCache.delete(key);
}, this.cacheTimeout);
return branches;
}
async getCommits(
repository: Repository,
options?: LogOptions
): Promise<Commit[]> {
const key = `${repository.rootUri.toString()}-${JSON.stringify(options)}`;
const cached = this.commitCache.get(key);
if (cached) {
return cached;
}
const commits = await repository.log(options);
this.commitCache.set(key, commits);
setTimeout(() => {
this.commitCache.delete(key);
}, this.cacheTimeout);
return commits;
}
invalidate(repository: Repository): void {
const prefix = repository.rootUri.toString();
for (const key of this.branchCache.keys()) {
if (key.startsWith(prefix)) {
this.branchCache.delete(key);
}
}
for (const key of this.commitCache.keys()) {
if (key.startsWith(prefix)) {
this.commitCache.delete(key);
}
}
}
}相关 API
示例项目
查看 Git API 示例 了解完整的实现示例。