mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-27 03:54:24 +01:00
Git - show main worktree under the Worktrees node (#287564)
This commit is contained in:
1
extensions/git/src/api/git.d.ts
vendored
1
extensions/git/src/api/git.d.ts
vendored
@@ -80,6 +80,7 @@ export interface Worktree {
|
||||
readonly name: string;
|
||||
readonly path: string;
|
||||
readonly ref: string;
|
||||
readonly main: boolean;
|
||||
readonly detached: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { LogOutputChannel, SourceControlArtifactProvider, SourceControlArtifactGroup, SourceControlArtifact, Event, EventEmitter, ThemeIcon, l10n, workspace, Uri, Disposable, Command } from 'vscode';
|
||||
import { coalesce, dispose, filterEvent, IDisposable, isCopilotWorktree } from './util';
|
||||
import { Repository } from './repository';
|
||||
import { Commit, Ref, RefType } from './api/git';
|
||||
import { Ref, RefType, Worktree } from './api/git';
|
||||
import { OperationKind } from './operation';
|
||||
|
||||
/**
|
||||
@@ -55,11 +55,14 @@ function sortRefByName(refA: Ref, refB: Ref): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
function sortByCommitDateDesc(a: { commitDetails?: Commit }, b: { commitDetails?: Commit }): number {
|
||||
const aCommitDate = a.commitDetails?.commitDate?.getTime() ?? 0;
|
||||
const bCommitDate = b.commitDetails?.commitDate?.getTime() ?? 0;
|
||||
|
||||
return bCommitDate - aCommitDate;
|
||||
function sortByWorktreeTypeAndNameAsc(a: Worktree, b: Worktree): number {
|
||||
if (a.main && !b.main) {
|
||||
return -1;
|
||||
} else if (!a.main && b.main) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
}
|
||||
|
||||
export class GitArtifactProvider implements SourceControlArtifactProvider, IDisposable {
|
||||
@@ -164,7 +167,7 @@ export class GitArtifactProvider implements SourceControlArtifactProvider, IDisp
|
||||
} else if (group === 'worktrees') {
|
||||
const worktrees = await this.repository.getWorktreeDetails();
|
||||
|
||||
return worktrees.sort(sortByCommitDateDesc).map(w => ({
|
||||
return worktrees.sort(sortByWorktreeTypeAndNameAsc).map(w => ({
|
||||
id: w.path,
|
||||
name: w.name,
|
||||
description: coalesce([
|
||||
@@ -172,10 +175,11 @@ export class GitArtifactProvider implements SourceControlArtifactProvider, IDisp
|
||||
w.commitDetails?.hash.substring(0, shortCommitLength),
|
||||
w.commitDetails?.message.split('\n')[0]
|
||||
]).join(' \u2022 '),
|
||||
icon: isCopilotWorktree(w.path)
|
||||
? new ThemeIcon('chat-sparkle')
|
||||
: new ThemeIcon('worktree'),
|
||||
timestamp: w.commitDetails?.commitDate?.getTime(),
|
||||
icon: w.main
|
||||
? new ThemeIcon('repo')
|
||||
: isCopilotWorktree(w.path)
|
||||
? new ThemeIcon('chat-sparkle')
|
||||
: new ThemeIcon('worktree')
|
||||
}));
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface IDotGit {
|
||||
readonly path: string;
|
||||
readonly commonPath?: string;
|
||||
readonly superProjectPath?: string;
|
||||
readonly isBare: boolean;
|
||||
}
|
||||
|
||||
export interface IFileStatus {
|
||||
@@ -575,7 +576,12 @@ export class Git {
|
||||
commonDotGitPath = path.normalize(commonDotGitPath);
|
||||
}
|
||||
|
||||
const raw = await fs.readFile(path.join(commonDotGitPath ?? dotGitPath, 'config'), 'utf8');
|
||||
const coreSections = GitConfigParser.parse(raw).find(s => s.name === 'core');
|
||||
const isBare = coreSections?.properties['bare'] === 'true';
|
||||
|
||||
return {
|
||||
isBare,
|
||||
path: dotGitPath,
|
||||
commonPath: commonDotGitPath !== dotGitPath ? commonDotGitPath : undefined,
|
||||
superProjectPath: superProjectPath ? path.normalize(superProjectPath) : undefined
|
||||
@@ -2954,10 +2960,27 @@ export class Repository {
|
||||
private async getWorktreesFS(): Promise<Worktree[]> {
|
||||
try {
|
||||
// List all worktree folder names
|
||||
const worktreesPath = path.join(this.dotGit.commonPath ?? this.dotGit.path, 'worktrees');
|
||||
const mainRepositoryPath = this.dotGit.commonPath ?? this.dotGit.path;
|
||||
const worktreesPath = path.join(mainRepositoryPath, 'worktrees');
|
||||
const dirents = await fs.readdir(worktreesPath, { withFileTypes: true });
|
||||
const result: Worktree[] = [];
|
||||
|
||||
if (!this.dotGit.isBare) {
|
||||
// Add main worktree for a non-bare repository
|
||||
const headPath = path.join(mainRepositoryPath, 'HEAD');
|
||||
const headContent = (await fs.readFile(headPath, 'utf8')).trim();
|
||||
|
||||
const mainRepositoryWorktreeName = path.basename(path.dirname(mainRepositoryPath));
|
||||
|
||||
result.push({
|
||||
name: mainRepositoryWorktreeName,
|
||||
path: path.dirname(mainRepositoryPath),
|
||||
ref: headContent.replace(/^ref: /, ''),
|
||||
detached: !headContent.startsWith('ref: '),
|
||||
main: true
|
||||
} satisfies Worktree);
|
||||
}
|
||||
|
||||
for (const dirent of dirents) {
|
||||
if (!dirent.isDirectory()) {
|
||||
continue;
|
||||
@@ -2977,7 +3000,8 @@ export class Repository {
|
||||
// Remove 'ref: ' prefix
|
||||
ref: headContent.replace(/^ref: /, ''),
|
||||
// Detached if HEAD does not start with 'ref: '
|
||||
detached: !headContent.startsWith('ref: ')
|
||||
detached: !headContent.startsWith('ref: '),
|
||||
main: false
|
||||
});
|
||||
} catch (err) {
|
||||
if (/ENOENT/.test(err.message)) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { Branch, BranchQuery, Change, CommitOptions, DiffChange, FetchOptions, F
|
||||
import { AutoFetcher } from './autofetch';
|
||||
import { GitBranchProtectionProvider, IBranchProtectionProviderRegistry } from './branchProtection';
|
||||
import { debounce, memoize, sequentialize, throttle } from './decorators';
|
||||
import { Repository as BaseRepository, BlameInformation, Commit, CommitShortStat, GitError, LogFileOptions, LsTreeElement, PullOptions, RefQuery, Stash, Submodule, Worktree } from './git';
|
||||
import { Repository as BaseRepository, BlameInformation, Commit, CommitShortStat, GitError, IDotGit, LogFileOptions, LsTreeElement, PullOptions, RefQuery, Stash, Submodule, Worktree } from './git';
|
||||
import { GitHistoryProvider } from './historyProvider';
|
||||
import { Operation, OperationKind, OperationManager, OperationResult } from './operation';
|
||||
import { CommitCommandsCenter, IPostCommitCommandsProviderRegistry } from './postCommitCommands';
|
||||
@@ -866,7 +866,7 @@ export class Repository implements Disposable {
|
||||
return this.repository.rootRealPath;
|
||||
}
|
||||
|
||||
get dotGit(): { path: string; commonPath?: string } {
|
||||
get dotGit(): IDotGit {
|
||||
return this.repository.dotGit;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user