Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/src/lib/app-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,9 @@ export interface IAppState {
/** Whether or not the worktrees dropdown should be shown in the toolbar */
readonly showWorktrees: boolean

/** Whether linked worktrees should be shown under their repository in the sidebar */
readonly showWorktreesInSidebar: boolean

/** Whether or not the Compare tab should be shown in the repository view */
readonly showCompareTab: boolean

Expand Down
60 changes: 52 additions & 8 deletions app/src/lib/git/worktree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import type { WorktreeEntry, WorktreeType } from '../../models/worktree'
import { git } from './core'
import { normalizePath } from '../helpers/path'

function getDotGitPath(repositoryPath: string): string {
return Path.join(repositoryPath, '.git')
}

export interface IWorktreePathInfo {
readonly isLinkedWorktree: boolean
readonly mainWorktreePath: string | null
}

export function parseWorktreePorcelainOutput(
stdout: string
): ReadonlyArray<WorktreeEntry> {
Expand Down Expand Up @@ -104,6 +113,10 @@ export async function removeWorktree(
await git(args, repository.path, 'removeWorktree')
}

export async function pruneWorktrees(repository: Repository): Promise<void> {
await git(['worktree', 'prune'], repository.path, 'pruneWorktrees')
}

export async function moveWorktree(
repository: Repository,
oldPath: string,
Expand Down Expand Up @@ -135,17 +148,48 @@ export async function getMainWorktreePath(
return main?.path ?? null
}

/**
* Synchronously checks if a repository path is a linked worktree by examining
* whether `.git` is a file (linked worktree) or directory (main worktree).
*/
export function isLinkedWorktreeSync(repositoryPath: string): boolean {
export function getWorktreePathInfoSync(
repositoryPath: string
): IWorktreePathInfo | null {
try {
const dotGit = Path.join(repositoryPath, '.git')
const dotGit = getDotGitPath(repositoryPath)
// eslint-disable-next-line no-sync
const stats = Fs.statSync(dotGit)
return stats.isFile()

if (stats.isDirectory()) {
return { isLinkedWorktree: false, mainWorktreePath: repositoryPath }
}

if (!stats.isFile()) {
return null
}

// eslint-disable-next-line no-sync
const contents = Fs.readFileSync(dotGit, 'utf8').trim()
if (!contents.startsWith('gitdir: ')) {
return null
}

const gitDirPath = Path.resolve(
repositoryPath,
contents.substring('gitdir: '.length)
)

// eslint-disable-next-line no-sync
const commondir = Fs.readFileSync(
Path.join(gitDirPath, 'commondir'),
'utf8'
).trim()
if (commondir.length === 0) {
return null
}

const commonGitDir = Path.resolve(gitDirPath, commondir)
return {
isLinkedWorktree: true,
mainWorktreePath: Path.dirname(commonGitDir),
}
} catch {
return false
return null
}
}
Loading
Loading