|
1 | 1 | import * as vscode from "vscode" |
| 2 | +import { execFile } from "child_process" |
| 3 | +import { promisify } from "util" |
| 4 | +import * as path from "path" |
2 | 5 | import { outputChannel } from "./extension" |
3 | | -import { GitExtension, Remote, Repository } from "./vscode-git" |
4 | 6 |
|
5 | | -async function getGitAPI() { |
6 | | - const gitExtension = |
7 | | - vscode.extensions.getExtension<GitExtension>("vscode.git") |
8 | | - if (!gitExtension) return null |
9 | | - const exports = await gitExtension.activate() |
10 | | - return exports.getAPI(1) |
| 7 | +const execFileAsync = promisify(execFile) |
| 8 | + |
| 9 | +export interface Repo { |
| 10 | + rootUri: vscode.Uri |
| 11 | +} |
| 12 | + |
| 13 | +async function git(cwd: string, ...args: string[]): Promise<string> { |
| 14 | + const { stdout } = await execFileAsync("git", args, { cwd }) |
| 15 | + return stdout.trim() |
11 | 16 | } |
12 | 17 |
|
13 | | -export async function getRepo(fileUri: vscode.Uri) { |
14 | | - const api = await getGitAPI() |
15 | | - if (!api) { |
16 | | - vscode.window.showErrorMessage("Git API not available") |
| 18 | +export async function getRepo(fileUri: vscode.Uri): Promise<Repo | null> { |
| 19 | + try { |
| 20 | + const stat = await vscode.workspace.fs.stat(fileUri) |
| 21 | + const cwd = |
| 22 | + stat.type & vscode.FileType.Directory |
| 23 | + ? fileUri.fsPath |
| 24 | + : path.dirname(fileUri.fsPath) |
| 25 | + const root = await git(cwd, "rev-parse", "--show-toplevel") |
| 26 | + return { rootUri: vscode.Uri.file(root) } |
| 27 | + } catch { |
| 28 | + outputChannel.appendLine( |
| 29 | + "Could not find git repository for file: " + fileUri.fsPath, |
| 30 | + ) |
17 | 31 | return null |
18 | 32 | } |
19 | | - const existing = api.getRepository(fileUri) |
20 | | - if (existing) return existing |
21 | | - outputChannel.appendLine("No repository found, triggering git refresh...") |
22 | | - await vscode.commands.executeCommand("git.refresh") |
23 | | - const afterRefresh = api.getRepository(fileUri) |
24 | | - if (afterRefresh) return afterRefresh |
25 | | - // Repos are discovered asynchronously after activation; wait for one to open |
26 | | - return new Promise<ReturnType<typeof api.getRepository>>((resolve) => { |
27 | | - const timeout = setTimeout(() => { |
28 | | - disposable.dispose() |
29 | | - resolve(null) |
30 | | - outputChannel.appendLine("Hit 5 second timeout for repository to load.") |
31 | | - }, 5000) |
32 | | - const disposable = api.onDidOpenRepository(() => { |
33 | | - const repo = api.getRepository(fileUri) |
34 | | - if (repo) { |
35 | | - outputChannel.appendLine("Found repository via onDidOpenRepository.") |
36 | | - clearTimeout(timeout) |
37 | | - disposable.dispose() |
38 | | - resolve(repo) |
39 | | - } |
40 | | - }) |
41 | | - }) |
42 | 33 | } |
43 | 34 |
|
44 | 35 | export async function origin( |
45 | | - repository: Repository, |
| 36 | + repo: Repo, |
46 | 37 | remote: string, |
47 | 38 | ): Promise<string | null> { |
48 | | - const found = repository.state.remotes.find((r: Remote) => r.name === remote) |
49 | | - return found?.fetchUrl ?? found?.pushUrl ?? null |
| 39 | + try { |
| 40 | + return await git(repo.rootUri.fsPath, "remote", "get-url", remote) |
| 41 | + } catch { |
| 42 | + return null |
| 43 | + } |
50 | 44 | } |
51 | 45 |
|
52 | 46 | export async function getSHAForBranch( |
53 | | - repository: Repository, |
| 47 | + repo: Repo, |
54 | 48 | branchName: string, |
55 | 49 | ): Promise<string | null> { |
56 | 50 | try { |
57 | | - const branch = await repository.getBranch(branchName) |
58 | | - return branch.commit ?? null |
| 51 | + return await git(repo.rootUri.fsPath, "rev-parse", branchName) |
59 | 52 | } catch { |
60 | 53 | return null |
61 | 54 | } |
62 | 55 | } |
63 | 56 |
|
64 | 57 | export async function head( |
65 | | - repository: Repository, |
| 58 | + repo: Repo, |
66 | 59 | ): Promise<[string, string | null] | null> { |
67 | | - const repoHead = repository.state.HEAD |
68 | | - if (!repoHead?.commit) return null |
69 | | - return [repoHead.commit, repoHead.name ?? null] |
| 60 | + try { |
| 61 | + const sha = await git(repo.rootUri.fsPath, "rev-parse", "HEAD") |
| 62 | + let branchName: string | null = null |
| 63 | + try { |
| 64 | + branchName = await git( |
| 65 | + repo.rootUri.fsPath, |
| 66 | + "symbolic-ref", |
| 67 | + "--short", |
| 68 | + "HEAD", |
| 69 | + ) |
| 70 | + } catch { |
| 71 | + // detached HEAD, branchName stays null |
| 72 | + } |
| 73 | + return [sha, branchName] |
| 74 | + } catch { |
| 75 | + return null |
| 76 | + } |
70 | 77 | } |
71 | 78 |
|
72 | 79 | export async function dir( |
73 | | - repository: Repository, |
| 80 | + repo: Repo, |
74 | 81 | ): Promise<{ git: string; repository: string } | null> { |
75 | | - const root = repository.rootUri.fsPath |
| 82 | + const root = repo.rootUri.fsPath |
76 | 83 | return { git: root, repository: root } |
77 | 84 | } |
0 commit comments